Changes in / [b7fd9daf:f95634e]


Ignore:
Files:
15 added
86 deleted
155 edited

Legend:

Unmodified
Added
Removed
  • benchmark/Cargo.toml.in

    rb7fd9daf rf95634e  
    66
    77[[bin]]
    8 name = "rdq-cycle-tokio"
     8name = "cycle-tokio"
    99path = "@abs_srcdir@/readyQ/cycle.rs"
    1010
    1111[[bin]]
    12 name = "rdq-locality-tokio"
     12name = "locality-tokio"
    1313path = "@abs_srcdir@/readyQ/locality.rs"
    14 
    15 [[bin]]
    16 name = "rdq-transfer-tokio"
    17 path = "@abs_srcdir@/readyQ/transfer.rs"
    18 
    19 [[bin]]
    20 name = "rdq-yield-tokio"
    21 path = "@abs_srcdir@/readyQ/yield.rs"
    2214
    2315[features]
  • benchmark/Makefile.am

    rb7fd9daf rf95634e  
    2121include $(top_srcdir)/tools/build/cfa.make
    2222
    23 AM_CFLAGS = -O3 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror
     23AM_CFLAGS = -O2 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror
    2424AM_CFAFLAGS = -quiet -nodebug
    2525AM_UPPFLAGS = -quiet -nodebug -multi -std=c++14
     
    197197        $(srcdir)/fixcsv.sh $@
    198198
    199 # use --no-print-directory to generate csv appropriately
    200 mutexStmt.csv:
    201         echo "building $@"
    202         echo "1-lock,2-lock,4-lock,8-lock,1-no-stmt-lock,2-no-stmt-lock,4-no-stmt-lock,8-no-stmt-lock,1-monitor,2-monitor,4-monitor" > $@
    203         +make mutexStmt-lock1.runquiet >> $@ && echo -n ',' >> $@
    204         +make mutexStmt-lock2.runquiet >> $@ && echo -n ',' >> $@
    205         +make mutexStmt-lock4.runquiet >> $@ && echo -n ',' >> $@
    206         +make mutexStmt-lock8.runquiet >> $@ && echo -n ',' >> $@
    207         +make mutexStmt-no-stmt-lock1.runquiet >> $@ && echo -n ',' >> $@
    208         +make mutexStmt-no-stmt-lock2.runquiet >> $@ && echo -n ',' >> $@
    209         +make mutexStmt-no-stmt-lock4.runquiet >> $@ && echo -n ',' >> $@
    210         +make mutexStmt-no-stmt-lock8.runquiet >> $@ && echo -n ',' >> $@
    211         +make mutexStmt-monitor1.runquiet >> $@ && echo -n ',' >> $@
    212         +make mutexStmt-monitor2.runquiet >> $@ && echo -n ',' >> $@
    213         +make mutexStmt-monitor4.runquiet >> $@
    214         $(srcdir)/fixcsv.sh $@
    215 
    216199schedint.csv:
    217200        echo "building $@"
     
    374357## =========================================================================================================
    375358
    376 mutexStmt$(EXEEXT) :                \
    377         mutexStmt-cpp1.run                      \
    378         mutexStmt-cpp2.run                      \
    379         mutexStmt-cpp4.run                      \
    380         mutexStmt-cpp8.run                      \
    381         mutexStmt-java.run                      \
    382         mutexStmt-lock1.run                 \
    383         mutexStmt-lock2.run                 \
    384         mutexStmt-lock4.run                 \
    385         mutexStmt-lock8.run                 \
    386         mutexStmt-no-stmt-lock1.run \
    387         mutexStmt-no-stmt-lock2.run \
    388         mutexStmt-no-stmt-lock4.run \
    389         mutexStmt-no-stmt-lock8.run \
    390         mutexStmt-monitor1.run      \
    391         mutexStmt-monitor2.run      \
    392         mutexStmt-monitor4.run
    393 
    394 mutexStmt-lock1$(EXEEXT):
    395         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock1.cfa
    396 
    397 mutexStmt-lock2$(EXEEXT):
    398         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock2.cfa
    399 
    400 mutexStmt-lock4$(EXEEXT):
    401         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock4.cfa
    402 
    403 mutexStmt-lock8$(EXEEXT):
    404         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock8.cfa
    405 
    406 mutexStmt-cpp1$(EXEEXT):
    407         $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp1.cc
    408 
    409 mutexStmt-cpp2$(EXEEXT):
    410         $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp2.cc
    411 
    412 mutexStmt-cpp4$(EXEEXT):
    413         $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp4.cc
    414 
    415 mutexStmt-cpp8$(EXEEXT):
    416         $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp8.cc
    417 
    418 mutexStmt-monitor1$(EXEEXT):
    419         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor1.cfa
    420 
    421 mutexStmt-monitor2$(EXEEXT):
    422         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor2.cfa
    423 
    424 mutexStmt-monitor4$(EXEEXT):
    425         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor4.cfa
    426 
    427 mutexStmt-no-stmt-lock1$(EXEEXT):
    428         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock1.cfa
    429 
    430 mutexStmt-no-stmt-lock2$(EXEEXT):
    431         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock2.cfa
    432 
    433 mutexStmt-no-stmt-lock4$(EXEEXT):
    434         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock4.cfa
    435 
    436 mutexStmt-no-stmt-lock8$(EXEEXT):
    437         $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock8.cfa
    438 
    439 mutexStmt-java$(EXEEXT):
    440         $(BENCH_V_JAVAC)javac -d $(builddir) $(srcdir)/mutexStmt/JavaThread.java
    441         echo "#!/bin/sh" > a.out
    442         echo "java JavaThread \"$$""@\"" >> a.out
    443         chmod a+x a.out
    444 
    445 ## =========================================================================================================
    446 
    447359schedint$(EXEEXT) :             \
    448360        schedint-cfa1.run       \
     
    612524## =========================================================================================================
    613525
    614 RDQBENCHES = \
    615         rdq-cycle-cfa \
    616         rdq-cycle-tokio \
    617         rdq-cycle-go \
    618         rdq-cycle-fibre \
    619         rdq-yield-cfa \
    620         rdq-yield-tokio \
    621         rdq-yield-go \
    622         rdq-yield-fibre \
    623         rdq-locality-cfa \
    624         rdq-locality-tokio \
    625         rdq-locality-go \
    626         rdq-locality-fibre \
    627         rdq-transfer-cfa \
    628         rdq-transfer-tokio \
    629         rdq-transfer-go \
    630         rdq-transfer-fibre
    631 
    632 rdq-benches:
    633         +make $(RDQBENCHES)
    634 
    635 clean-rdq-benches:
    636         rm -rf $(RDQBENCHES) $(builddir)/target go.mod
    637 
    638 rdq-%-tokio$(EXEEXT): $(builddir)/target/release/rdq-%-tokio$(EXEEXT)
    639         $(BENCH_V_RUSTC)cp $(builddir)/target/release/$(basename $@) $@
    640 
    641 $(builddir)/target/release/rdq-%-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs
    642         $(BENCH_V_RUSTC)cd $(builddir) && cargo build --release
    643 
    644 rdq-%-cfa$(EXEEXT): $(srcdir)/readyQ/%.cfa $(srcdir)/readyQ/rq_bench.hfa
    645         $(BENCH_V_CFA)$(CFACOMPILE) $< -o $@
    646 
    647 go.mod:
    648         touch $@
    649         go mod edit -module=rdq.bench
    650         go get golang.org/x/sync/semaphore
    651         go get golang.org/x/text/language
    652         go get golang.org/x/text/message
    653 
    654 rdq-%-go$(EXEEXT): $(srcdir)/readyQ/%.go $(srcdir)/readyQ/bench.go go.mod
    655         $(BENCH_V_GOC)go build -o $@ $< $(srcdir)/readyQ/bench.go
    656 
    657 rdq-%-fibre$(EXEEXT): $(srcdir)/readyQ/%.cpp
    658         $(BENCH_V_CXX)$(CXXCOMPILE) $< -o $@ -lfibre -std=c++17 $(AM_CFLAGS)
    659 
    660 # ## =========================================================================================================
    661 
    662 CLEANFILES = $(RDQBENCHES) go.mod go.sum
    663 
    664 clean-local:
    665         -rm -rf target
     526%-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs
     527        cd $(builddir) && cargo build --release
     528        cp $(builddir)/target/release/$(basename $@) $@
  • benchmark/bench.h

    rb7fd9daf rf95634e  
    2121        return 1000000000LL * ts.tv_sec + ts.tv_nsec;
    2222} // bench_time
    23 
    24 
    25 #if defined(__cforall)
    26 struct test_spinlock {
    27         volatile bool lock;
    28 };
    29 
    30 static inline void lock( test_spinlock & this ) {
    31         for ( ;; ) {
    32                 if ( (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0) ) break;
    33         }
    34 }
    35 
    36 static inline void unlock( test_spinlock & this ) {
    37         __atomic_clear( &this.lock, __ATOMIC_RELEASE );
    38 }
    39 #endif
    4023
    4124#ifndef BENCH_N
  • benchmark/bench.rs

    rb7fd9daf rf95634e  
    11use std::io::{self, Write};
    2 use std::option;
    32use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
    43use std::time::{Instant,Duration};
    5 use std::u128;
    64
    75use clap::{Arg, ArgMatches};
     
    2927
    3028impl BenchData {
    31         pub fn new(options: ArgMatches, nthreads: usize, default_it: option::Option<u64>) -> BenchData {
     29        pub fn new(options: ArgMatches, nthreads: usize) -> BenchData {
    3230                let (clock_mode, stop_count, duration) = if options.is_present("iterations") {
    3331                        (false,
    3432                        options.value_of("iterations").unwrap().parse::<u64>().unwrap(),
    35                         -1.0)
    36                 } else if !default_it.is_none() {
    37                         (false,
    38                         default_it.unwrap(),
    3933                        -1.0)
    4034                } else {
     
    5448        }
    5549
    56         #[allow(dead_code)]
    5750        pub async fn wait(&self, start: &Instant) -> Duration{
    5851                loop {
     
    7669}
    7770
    78 // ==================================================
    79 pub fn _lehmer64( state: &mut u128 ) -> u64 {
    80         *state = state.wrapping_mul(0xda942042e4dd58b5);
    81         return (*state >> 64) as u64;
    82 }
  • benchmark/io/http/filecache.cfa

    rb7fd9daf rf95634e  
    185185        sout | "Filled cache from path \"" | path | "\" with" | fcount | "files";
    186186        if( conflicts > 0 ) {
    187                 sout | "Found" | conflicts | "conflicts (size: " | file_cache.size | ", seed: " | options.file_cache.hash_seed | ")";
     187                sout | "Found" | conflicts | "conflicts (seed: " | options.file_cache.hash_seed | ")";
    188188                #if defined(REJECT_CONFLICTS)
    189189                        abort("Conflicts found in the cache");
  • benchmark/io/http/http_ring.cpp

    rb7fd9daf rf95634e  
    118118// Get a fix reply based on the return code
    119119const char * http_msgs[] = {
    120         "HTTP/1.1 200 OK\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 15\r\nConnection: keep-alive\r\n\r\nHello, World!\r\n",
    121         "HTTP/1.1 400 Bad Request\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
    122         "HTTP/1.1 404 Not Found\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
    123         "HTTP/1.1 405 Method Not \r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
    124         "HTTP/1.1 408 Request Timeout\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
    125         "HTTP/1.1 413 Payload Too Large\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
    126         "HTTP/1.1 414 URI Too Long\r\nServer: HttpForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
     120        "HTTP/1.1 200 OK\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 15\r\nConnection: keep-alive\r\n\r\nHello, World!\r\n",
     121        "HTTP/1.1 400 Bad Request\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
     122        "HTTP/1.1 404 Not Found\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
     123        "HTTP/1.1 405 Method Not \r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
     124        "HTTP/1.1 408 Request Timeout\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
     125        "HTTP/1.1 413 Payload Too Large\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
     126        "HTTP/1.1 414 URI Too Long\r\nServer: HttoForall\r\nContent-Type: text/plain\r\nContent-Length: 0 \r\n\r\n",
    127127};
    128128static_assert( KNOWN_CODES == (sizeof(http_msgs) / sizeof(http_msgs[0])) );
  • benchmark/io/http/main.cfa

    rb7fd9daf rf95634e  
    190190                        init_protocol();
    191191                        {
    192                                 Worker * workers = anew(options.clopts.nworkers);
     192                                Worker workers[options.clopts.nworkers];
    193193                                for(i; options.clopts.nworkers) {
    194194                                        // if( options.file_cache.fixed_fds ) {
     
    212212                                }
    213213                                sout | nl;
    214                                 if(!options.interactive) park();
    215214                                {
    216215                                        char buffer[128];
     
    250249
    251250                                sout | "Stopping connection threads..." | nonl; flush( sout );
    252                                 adelete(workers);
    253251                        }
    254252                        sout | "done";
  • benchmark/io/http/options.cfa

    rb7fd9daf rf95634e  
    2121        false, // log
    2222        false, // stats
    23         true, // interactive
    24         0, // redirect
    25         0, // redirect
    2623
    2724        { // file_cache
     
    5552        // bool sqkpoll = false;
    5653        // bool iokpoll = false;
    57         unsigned nentries = 0;
     54        unsigned nentries = 16;
    5855        bool isolate = false;
    5956
     
    6562                {'\0', "isolate",        "Create one cluster per processor", isolate, parse_settrue},
    6663                {'\0', "log",            "Enable logs", options.log, parse_settrue},
    67                 {'\0', "sout",           "Redirect standard out to file", options.reopen_stdout},
    68                 {'\0', "serr",           "Redirect standard error to file", options.reopen_stderr},
    6964                {'\0', "stats",          "Enable statistics", options.stats, parse_settrue},
    70                 {'\0', "shell",          "Disable interactive mode", options.interactive, parse_setfalse},
    7165                {'\0', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog},
    7266                {'\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},
     
    8579        parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left );
    8680
    87         if( nentries != 0 && !is_pow2(nentries) ) {
     81        if( !is_pow2(nentries) ) {
    8882                unsigned v = nentries;
    8983                v--;
     
    137131
    138132        options.file_cache.path = path;
    139 
    140         if( options.reopen_stdout && options.reopen_stderr && 0 == strcmp(options.reopen_stdout, options.reopen_stderr) ) {
    141                 serr | "Redirect sout and serr to the same file is not supported";
    142                 exit(EXIT_FAILURE);
    143         }
    144 
    145         if( options.reopen_stdout ) {
    146                 sout | "redirecting sout to '" | options.reopen_stdout | "'";
    147                 FILE  * ret = freopen( options.reopen_stdout, "w", stdout);
    148                 if( ret == 0p ) {
    149                         serr | "Failed to redirect sout to '" | options.reopen_stdout | "'";
    150                         exit(EXIT_FAILURE);
    151                 }
    152         }
    153 
    154         if( options.reopen_stderr ) {
    155                 sout | "redirecting serr to '" | options.reopen_stderr | "'";
    156                 FILE  * ret = freopen( options.reopen_stderr, "w", stderr);
    157                 if( ret == 0p ) {
    158                         serr | "Failed to redirect serr to '" | options.reopen_stderr | "'";
    159                         exit(EXIT_FAILURE);
    160                 }
    161         }
    162133}
  • benchmark/io/http/options.hfa

    rb7fd9daf rf95634e  
    1010        bool log;
    1111        bool stats;
    12         bool interactive;
    13         const char * reopen_stdout;
    14         const char * reopen_stderr;
    1512
    1613        struct {
  • benchmark/io/http/protocol.cfa

    rb7fd9daf rf95634e  
    1111#include <fstream.hfa>
    1212#include <iofwd.hfa>
    13 #include <io/types.hfa>
    14 #include <mutex_stmt.hfa>
    1513
    1614#include <assert.h>
     
    2826#define PLAINTEXT_MEMCPY
    2927#define PLAINTEXT_NOCOPY
    30 #define LINKED_IO
    3128
    3229struct https_msg_str {
     
    5653}
    5754
    58 static inline int answer( int fd, const char * it, int len ) {
     55static inline int answer( int fd, const char * it, int len) {
    5956        while(len > 0) {
    6057                // Call write
    6158                int ret = cfa_send(fd, it, len, 0, CFA_IO_LAZY);
    6259                if( ret < 0 ) {
    63                         if( errno == ECONNRESET || errno == EPIPE ) { close(fd); return -ECONNRESET; }
     60                        if( errno == ECONNRESET || errno == EPIPE ) return -ECONNRESET;
    6461                        if( errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN;
    6562
     
    8077}
    8178
    82 static int fill_header(char * it, size_t size) {
     79int answer_header( int fd, size_t size ) {
     80        char buffer[512];
     81        char * it = buffer;
    8382        memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len);
    8483        it += http_msgs[OK200]->len;
    8584        int len = http_msgs[OK200]->len;
    8685        len += snprintf(it, 512 - len, "%d \n\n", size);
    87         return len;
    88 }
    89 
    90 static int answer_header( int fd, size_t size ) {
    91         char buffer[512];
    92         int len = fill_header(buffer, size);
    9386        return answer( fd, buffer, len );
    9487}
     
    142135}
    143136
    144 static int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) {
     137
     138[HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len) {
     139        char * it = buffer;
     140        size_t count = len - 1;
     141        int rlen = 0;
     142        READ:
     143        for() {
     144                int ret = cfa_recv(fd, (void*)it, count, 0, CFA_IO_LAZY);
     145                // int ret = read(fd, (void*)it, count);
     146                if(ret == 0 ) return [OK200, true, 0, 0];
     147                if(ret < 0 ) {
     148                        if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ;
     149                        if( errno == ECONNRESET ) return [E408, true, 0, 0];
     150                        if( errno == EPIPE ) return [E408, true, 0, 0];
     151                        abort( "read error: (%d) %s\n", (int)errno, strerror(errno) );
     152                }
     153                it[ret + 1] = '\0';
     154                rlen += ret;
     155
     156                if( strstr( it, "\r\n\r\n" ) ) break;
     157
     158                it += ret;
     159                count -= ret;
     160
     161                if( count < 1 ) return [E414, false, 0, 0];
     162        }
     163
     164        if( options.log ) {
     165                write(sout, buffer, rlen);
     166                sout | nl;
     167        }
     168
     169        it = buffer;
     170        int ret = memcmp(it, "GET /", 5);
     171        if( ret != 0 ) return [E400, false, 0, 0];
     172        it += 5;
     173
     174        char * end = strstr( it, " " );
     175        return [OK200, false, it, end - it];
     176}
     177
     178int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) {
    145179        unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE;
    146180        off_t offset = 0;
     
    173207}
    174208
    175 static void zero_sqe(struct io_uring_sqe * sqe) {
    176         sqe->flags = 0;
    177         sqe->ioprio = 0;
    178         sqe->fd = 0;
    179         sqe->off = 0;
    180         sqe->addr = 0;
    181         sqe->len = 0;
    182         sqe->fsync_flags = 0;
    183         sqe->__pad2[0] = 0;
    184         sqe->__pad2[1] = 0;
    185         sqe->__pad2[2] = 0;
    186         sqe->fd = 0;
    187         sqe->off = 0;
    188         sqe->addr = 0;
    189         sqe->len = 0;
    190 }
    191 
    192 enum FSM_STATE {
    193         Initial,
    194         Retry,
    195         Error,
    196         Done,
    197 };
    198 
    199 struct FSM_Result {
    200         FSM_STATE state;
    201         int error;
    202 };
    203 
    204 static inline void ?{}(FSM_Result & this) { this.state = Initial; this.error = 0; }
    205 static inline bool is_error(FSM_Result & this) { return Error == this.state; }
    206 static inline bool is_done(FSM_Result & this) { return Done == this.state; }
    207 
    208 static inline int error(FSM_Result & this, int error) {
    209         this.error = error;
    210         this.state = Error;
    211         return error;
    212 }
    213 
    214 static inline int done(FSM_Result & this) {
    215         this.state = Done;
    216         return 0;
    217 }
    218 
    219 static inline int retry(FSM_Result & this) {
    220         this.state = Retry;
    221         return 0;
    222 }
    223 
    224 static inline int need(FSM_Result & this) {
    225         switch(this.state) {
    226                 case Initial:
    227                 case Retry:
    228                         return 1;
    229                 case Error:
    230                         if(this.error == 0) mutex(serr) serr | "State marked error but code is 0";
    231                 case Done:
    232                         return 0;
    233         }
    234 }
    235 
    236 // Generator that handles sending the header
    237 generator header_g {
    238         io_future_t f;
    239         const char * next;
    240         int fd; size_t len;
    241         FSM_Result res;
    242 };
    243 
    244 static inline void ?{}(header_g & this, int fd, const char * it, size_t len ) {
    245         this.next = it;
    246         this.fd = fd;
    247         this.len = len;
    248 }
    249 
    250 static inline void fill(header_g & this, struct io_uring_sqe * sqe) {
    251         zero_sqe(sqe);
    252         sqe->opcode = IORING_OP_SEND;
    253         sqe->user_data = (uintptr_t)&this.f;
    254         sqe->flags = IOSQE_IO_LINK;
    255         sqe->fd = this.fd;
    256         sqe->addr = (uintptr_t)this.next;
    257         sqe->len = this.len;
    258 }
    259 
    260 static inline int error(header_g & this, int error) {
    261         int ret = close(this.fd);
    262         if( ret != 0 ) {
    263                 mutex(serr) serr | "Failed to close fd" | errno;
    264         }
    265         return error(this.res, error);
    266 }
    267 
    268 static inline int wait_and_process(header_g & this) {
    269         wait(this.f);
    270 
    271         // Did something crazy happen?
    272         if(this.f.result > this.len) {
    273                 mutex(serr) serr | "HEADER sent too much!";
    274                 return error(this, -ERANGE);
    275         }
    276 
    277         // Something failed?
    278         if(this.f.result < 0) {
    279                 int error = -this.f.result;
    280                 if( error == ECONNRESET ) return error(this, -ECONNRESET);
    281                 if( error == EPIPE ) return error(this, -EPIPE);
    282                 if( error == ECANCELED ) {
    283                         mutex(serr) serr | "HEADER was cancelled, WTF!";
    284                         return error(this, -ECONNRESET);
    285                 }
    286                 if( error == EAGAIN || error == EWOULDBLOCK) {
    287                         mutex(serr) serr | "HEADER got eagain, WTF!";
    288                         return error(this, -ECONNRESET);
    289                 }
    290         }
    291 
    292         // Done?
    293         if(this.f.result == this.len) {
    294                 return done(this.res);
    295         }
    296 
    297         // It must be a Short read
    298         this.len  -= this.f.result;
    299         this.next += this.f.result;
    300         reset(this.f);
    301         return retry(this.res);
    302 }
    303 
    304 // Generator that handles splicing in a file
    305 struct splice_in_t {
    306         io_future_t f;
    307         int fd; int pipe; size_t len; off_t off;
    308         FSM_Result res;
    309 };
    310 
    311 static inline void ?{}(splice_in_t & this, int fd, int pipe, size_t len) {
    312         this.fd = fd;
    313         this.pipe = pipe;
    314         this.len = len;
    315         this.off = 0;
    316 }
    317 
    318 static inline void fill(splice_in_t & this, struct io_uring_sqe * sqe) {
    319         zero_sqe(sqe);
    320         sqe->opcode = IORING_OP_SPLICE;
    321         sqe->user_data = (uintptr_t)&this.f;
    322         sqe->flags = 0;
    323         sqe->splice_fd_in = this.fd;
    324         sqe->splice_off_in = this.off;
    325         sqe->fd = this.pipe;
    326         sqe->off = (__u64)-1;
    327         sqe->len = this.len;
    328         sqe->splice_flags = SPLICE_F_MOVE;
    329 }
    330 
    331 static inline int wait_and_process(splice_in_t & this) {
    332         wait(this.f);
    333 
    334         // Did something crazy happen?
    335         if(this.f.result > this.len) {
    336                 mutex(serr) serr | "SPLICE IN spliced too much!";
    337                 return error(this.res, -ERANGE);
    338         }
    339 
    340         // Something failed?
    341         if(this.f.result < 0) {
    342                 int error = -this.f.result;
    343                 if( error == ECONNRESET ) return error(this.res, -ECONNRESET);
    344                 if( error == EPIPE ) return error(this.res, -EPIPE);
    345                 if( error == ECANCELED ) {
    346                         mutex(serr) serr | "SPLICE IN was cancelled, WTF!";
    347                         return error(this.res, -ECONNRESET);
    348                 }
    349                 if( error == EAGAIN || error == EWOULDBLOCK) {
    350                         mutex(serr) serr | "SPLICE IN got eagain, WTF!";
    351                         return error(this.res, -ECONNRESET);
    352                 }
    353         }
    354 
    355         // Done?
    356         if(this.f.result == this.len) {
    357                 return done(this.res);
    358         }
    359 
    360         // It must be a Short read
    361         this.len -= this.f.result;
    362         this.off += this.f.result;
    363         reset(this.f);
    364         return retry(this.res);
    365 }
    366 
    367 generator splice_out_g {
    368         io_future_t f;
    369         int pipe; int fd; size_t len;
    370         FSM_Result res;
    371 };
    372 
    373 static inline void ?{}(splice_out_g & this, int pipe, int fd, size_t len) {
    374         this.pipe = pipe;
    375         this.fd = fd;
    376         this.len = len;
    377 }
    378 
    379 static inline void fill(splice_out_g & this, struct io_uring_sqe * sqe) {
    380         zero_sqe(sqe);
    381         sqe->opcode = IORING_OP_SPLICE;
    382         sqe->user_data = (uintptr_t)&this.f;
    383         sqe->flags = 0;
    384         sqe->splice_fd_in = this.pipe;
    385         sqe->splice_off_in = (__u64)-1;
    386         sqe->fd = this.fd;
    387         sqe->off = (__u64)-1;
    388         sqe->len = this.len;
    389         sqe->splice_flags = SPLICE_F_MOVE;
    390 }
    391 
    392 static inline int error(splice_out_g & this, int error) {
    393         int ret = close(this.fd);
    394         if( ret != 0 ) {
    395                 mutex(serr) serr | "Failed to close fd" | errno;
    396         }
    397         return error(this.res, error);
    398 }
    399 
    400 static inline void wait_and_process(splice_out_g & this) {
    401         wait(this.f);
    402 
    403         // Did something crazy happen?
    404         if(this.f.result > this.len) {
    405                 mutex(serr) serr | "SPLICE OUT spliced too much!";
    406                 return error(this.res, -ERANGE);
    407         }
    408 
    409         // Something failed?
    410         if(this.f.result < 0) {
    411                 int error = -this.f.result;
    412                 if( error == ECONNRESET ) return error(this, -ECONNRESET);
    413                 if( error == EPIPE ) return error(this, -EPIPE);
    414                 if( error == ECANCELED ) {
    415                         this.f.result = 0;
    416                         goto SHORT_WRITE;
    417                 }
    418                 if( error == EAGAIN || error == EWOULDBLOCK) {
    419                         mutex(serr) serr | "SPLICE OUT got eagain, WTF!";
    420                         return error(this, -ECONNRESET);
    421                 }
    422         }
    423 
    424         // Done?
    425         if(this.f.result == this.len) {
    426                 return done(this.res);
    427         }
    428 
    429 SHORT_WRITE:
    430         // It must be a Short Write
    431         this.len -= this.f.result;
    432         reset(this.f);
    433         return retry(this.res);
    434 }
    435 
    436 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t fsize ) {
    437         #if defined(LINKED_IO)
    438                 char buffer[512];
    439                 int len = fill_header(buffer, fsize);
    440                 header_g header = { fd, buffer, len };
    441                 splice_in_t splice_in = { ans_fd, pipe[1], fsize };
    442                 splice_out_g splice_out = { pipe[0], fd, fsize };
    443 
    444                 RETRY_LOOP: for() {
    445                         int have = need(header.res) + need(splice_in.res) + 1;
    446                         int idx = 0;
    447                         struct io_uring_sqe * sqes[3];
    448                         __u32 idxs[3];
    449                         struct $io_context * ctx = cfa_io_allocate(sqes, idxs, have);
    450 
    451                         if(need(splice_in.res)) { fill(splice_in, sqes[idx++]); }
    452                         if(need(   header.res)) { fill(header   , sqes[idx++]); }
    453                         fill(splice_out, sqes[idx]);
    454 
    455                         // Submit everything
    456                         asm volatile("": : :"memory");
    457                         cfa_io_submit( ctx, idxs, have, false );
    458 
    459                         // wait for the results
    460                         // Always wait for splice-in to complete as
    461                         // we may need to kill the connection if it fails
    462                         // If it already completed, this is a no-op
    463                         wait_and_process(splice_in);
    464 
    465                         if(is_error(splice_in.res)) {
    466                                 mutex(serr) serr | "SPLICE IN failed with" | splice_in.res.error;
    467                                 close(fd);
    468                         }
    469 
    470                         // Process the other 2
    471                         wait_and_process(header);
    472                         wait_and_process(splice_out);
    473 
    474                         if(is_done(splice_out.res)) {
    475                                 break RETRY_LOOP;
    476                         }
    477 
    478                         // We need to wait for the completion if
    479                         // - both completed
    480                         // - the header failed
    481                         // -
    482 
    483                         if(  is_error(header.res)
    484                           || is_error(splice_in.res)
    485                           || is_error(splice_out.res)) {
    486                                 return -ECONNRESET;
    487                         }
    488                 }
    489 
    490                 return len + fsize;
    491         #else
    492                 int ret = answer_header(fd, fsize);
    493                 if( ret < 0 ) { close(fd); return ret; }
    494                 return sendfile(pipe, fd, ans_fd, fsize);
    495         #endif
    496 }
    497 
    498 [HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len) {
    499         char * it = buffer;
    500         size_t count = len - 1;
    501         int rlen = 0;
    502         READ:
    503         for() {
    504                 int ret = cfa_recv(fd, (void*)it, count, 0, CFA_IO_LAZY);
    505                 // int ret = read(fd, (void*)it, count);
    506                 if(ret == 0 ) return [OK200, true, 0, 0];
    507                 if(ret < 0 ) {
    508                         if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ;
    509                         if( errno == ECONNRESET ) { close(fd); return [E408, true, 0, 0]; }
    510                         if( errno == EPIPE ) { close(fd); return [E408, true, 0, 0]; }
    511                         abort( "read error: (%d) %s\n", (int)errno, strerror(errno) );
    512                 }
    513                 it[ret + 1] = '\0';
    514                 rlen += ret;
    515 
    516                 if( strstr( it, "\r\n\r\n" ) ) break;
    517 
    518                 it += ret;
    519                 count -= ret;
    520 
    521                 if( count < 1 ) return [E414, false, 0, 0];
    522         }
    523 
    524         if( options.log ) {
    525                 write(sout, buffer, rlen);
    526                 sout | nl;
    527         }
    528 
    529         it = buffer;
    530         int ret = memcmp(it, "GET /", 5);
    531         if( ret != 0 ) return [E400, false, 0, 0];
    532         it += 5;
    533 
    534         char * end = strstr( it, " " );
    535         return [OK200, false, it, end - it];
    536 }
    537 
    538209//=============================================================================================
    539210
     
    543214
    544215const char * original_http_msgs[] = {
    545         "HTTP/1.1 200 OK\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: ",
    546         "HTTP/1.1 200 OK\r\nServer: HttpForall\r\nDate\r\nConnection: keep-alive\r\nContent-Length: 15\r\nContent-Type: text/html: %s \r\n\r\nHello, World!\r\n",
    547         "HTTP/1.1 400 Bad Request\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
    548         "HTTP/1.1 404 Not Found\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
    549         "HTTP/1.1 405 Method Not Allowed\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
    550         "HTTP/1.1 408 Request Timeout\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
    551         "HTTP/1.1 413 Payload Too Large\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
    552         "HTTP/1.1 414 URI Too Long\nServer: HttpForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
     216        "HTTP/1.1 200 OK\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: ",
     217        "HTTP/1.1 200 OK\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 15\n\nHello, World!\n\n",
     218        "HTTP/1.1 400 Bad Request\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
     219        "HTTP/1.1 404 Not Found\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
     220        "HTTP/1.1 405 Method Not Allowed\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
     221        "HTTP/1.1 408 Request Timeout\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
     222        "HTTP/1.1 413 Payload Too Large\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
     223        "HTTP/1.1 414 URI Too Long\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
    553224};
    554225
     
    580251                Time now = timeHiRes();
    581252                strftime( buff, 100, "%a, %d %b %Y %H:%M:%S %Z", now );
    582                 // if( options.log ) sout | "Updated date to '" | buff | "'";
     253                sout | "Updated date to '" | buff | "'";
    583254
    584255                for(i; KNOWN_CODES) {
     
    593264                this.idx = (this.idx + 1) % 2;
    594265
    595                 // if( options.log ) sout | "Date thread sleeping";
     266                sout | "Date thread sleeping";
    596267
    597268                sleep(1`s);
  • benchmark/io/http/protocol.hfa

    rb7fd9daf rf95634e  
    1616
    1717int answer_error( int fd, HttpCode code );
     18int answer_header( int fd, size_t size );
    1819int answer_plaintext( int fd );
    1920int answer_empty( int fd );
    20 int answer_sendfile( int pipe[2], int fd, int ans_fd, size_t count );
    2121
    2222[HttpCode code, bool closed, * const char file, size_t len] http_read(int fd, []char buffer, size_t len);
     23
     24int sendfile( int pipe[2], int fd, int ans_fd, size_t count );
  • benchmark/io/http/worker.cfa

    rb7fd9daf rf95634e  
    122122                        }
    123123
     124                        // Send the header
     125                        int ret = answer_header(fd, count);
     126                        if( ret == -ECONNRESET ) break REQUEST;
     127
    124128                        // Send the desired file
    125                         int ret = answer_sendfile( this.pipe, fd, ans_fd, count);
     129                        ret = sendfile( this.pipe, fd, ans_fd, count);
    126130                        if( ret == -ECONNRESET ) break REQUEST;
    127131
     
    130134
    131135                if( options.log ) sout | "=== Connection closed ===";
     136                close(fd);
    132137                continue CONNECTION;
    133138        }
  • benchmark/readyQ/cycle.cpp

    rb7fd9daf rf95634e  
    4141                        Fibre * threads[tthreads];
    4242                        Partner thddata[tthreads];
    43                         for(unsigned i = 0; i < tthreads; i++) {
     43                        for(int i = 0; i < tthreads; i++) {
    4444                                unsigned pi = (i + nthreads) % tthreads;
    4545                                thddata[i].next = &thddata[pi].self;
    4646                        }
    47                         for(unsigned i = 0; i < tthreads; i++) {
     47                        for(int i = 0; i < tthreads; i++) {
    4848                                threads[i] = new Fibre( reinterpret_cast<void (*)(void *)>(partner_main), &thddata[i] );
    4949                        }
     
    5353                        start = timeHiRes();
    5454
    55                         for(unsigned i = 0; i < nthreads; i++) {
     55                        for(int i = 0; i < nthreads; i++) {
    5656                                thddata[i].self.post();
    5757                        }
     
    6262                        printf("\nDone\n");
    6363
    64                         for(unsigned i = 0; i < tthreads; i++) {
     64                        for(int i = 0; i < tthreads; i++) {
    6565                                thddata[i].self.post();
    6666                                fibre_join( threads[i], nullptr );
  • benchmark/readyQ/cycle.go

    rb7fd9daf rf95634e  
    6060        atomic.StoreInt32(&stop, 1)
    6161        end := time.Now()
    62         duration := end.Sub(start)
     62        delta := end.Sub(start)
    6363
    6464        fmt.Printf("\nDone\n")
     
    7474
    7575        p := message.NewPrinter(language.English)
    76         p.Printf("Duration (ms)        : %d\n", duration.Milliseconds())
     76        p.Printf("Duration (ms)        : %f\n", delta.Seconds());
    7777        p.Printf("Number of processors : %d\n", nprocs);
    7878        p.Printf("Number of threads    : %d\n", tthreads);
    7979        p.Printf("Cycle size (# thrds) : %d\n", ring_size);
    8080        p.Printf("Total Operations(ops): %15d\n", global_counter)
    81         p.Printf("Ops per second       : %18.2f\n", float64(global_counter) / duration.Seconds())
    82         p.Printf("ns per ops           : %18.2f\n", float64(duration.Nanoseconds()) / float64(global_counter))
     81        p.Printf("Ops per second       : %18.2f\n", float64(global_counter) / delta.Seconds())
     82        p.Printf("ns per ops           : %18.2f\n", float64(delta.Nanoseconds()) / float64(global_counter))
    8383        p.Printf("Ops per threads      : %15d\n", global_counter / uint64(tthreads))
    8484        p.Printf("Ops per procs        : %15d\n", global_counter / uint64(nprocs))
    85         p.Printf("Ops/sec/procs        : %18.2f\n", (float64(global_counter) / float64(nprocs)) / duration.Seconds())
    86         p.Printf("ns per ops/procs     : %18.2f\n", float64(duration.Nanoseconds()) / (float64(global_counter) / float64(nprocs)))
     85        p.Printf("Ops/sec/procs        : %18.2f\n", (float64(global_counter) / float64(nprocs)) / delta.Seconds())
     86        p.Printf("ns per ops/procs     : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(nprocs)))
    8787
    8888}
  • benchmark/readyQ/cycle.rs

    rb7fd9daf rf95634e  
    4646
    4747        let tthreads = nthreads * ring_size;
    48         let exp = Arc::new(bench::BenchData::new(options, tthreads, None));
     48        let exp = Arc::new(bench::BenchData::new(options, tthreads));
    4949
    5050        let s = (1000000 as u64).to_formatted_string(&Locale::en);
  • benchmark/readyQ/locality.go

    rb7fd9daf rf95634e  
    286286        // Print with nice 's, i.e. 1'000'000 instead of 1000000
    287287        p := message.NewPrinter(language.English)
    288         p.Printf("Duration (ms)          : %f\n", delta.Milliseconds());
     288        p.Printf("Duration (s)           : %f\n", delta.Seconds());
    289289        p.Printf("Number of processors   : %d\n", nprocs);
    290290        p.Printf("Number of threads      : %d\n", nthreads);
  • benchmark/readyQ/locality.rs

    rb7fd9daf rf95634e  
    124124                                                return (r as *mut MyData, true);
    125125                                        }
    126                                         let got = self.ptr.compare_exchange_weak(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst, Ordering::SeqCst);
    127                                         if got == Ok(expected) {
     126                                        let got = self.ptr.compare_and_swap(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst);
     127                                        if got == expected {
    128128                                                break expected;// We got the seat
    129129                                        }
     
    285285        assert_eq!(&s, "1,000,000");
    286286
    287         let exp = Arc::new(bench::BenchData::new(options, nprocs, None));
     287        let exp = Arc::new(bench::BenchData::new(options, nprocs));
    288288        let mut results = Result::new();
    289289
  • benchmark/readyQ/transfer.cfa

    rb7fd9daf rf95634e  
    3939                        Pause();
    4040                        if( (timeHiRes() - start) > 5`s ) {
    41                                 print_stats_now( bench_cluster, CFA_STATS_READY_Q | CFA_STATS_IO );
    4241                                serr | "Programs has been blocked for more than 5 secs";
    4342                                exit(1);
     
    111110        cfa_option opt[] = {
    112111                BENCH_OPT,
    113                 { 'e', "exhaust", "Whether or not threads that have seen the new epoch should park instead of yielding.", exhaust, parse_yesno}
     112                { 'e', "exhaust", "Whether or not threads that have seen the new epoch should yield or park.", exhaust, parse_yesno}
    114113        };
    115114        BENCH_OPT_PARSE("cforall transition benchmark");
     
    167166        }
    168167
    169         sout | "Duration (ms)           : " | ws(3, 3, unit(eng((end - start)`dms)));
     168        sout | "Duration                : " | ws(3, 3, unit(eng((end - start)`ds))) | 's';
    170169        sout | "Number of processors    : " | nprocs;
    171170        sout | "Number of threads       : " | nthreads;
  • benchmark/readyQ/transfer.cpp

    rb7fd9daf rf95634e  
    173173        }
    174174
    175         std::cout << "Duration (ms)           : " << to_miliseconds(end - start) << std::endl;
     175        std::cout << "Duration                : " << to_miliseconds(end - start) << "ms" << std::endl;
    176176        std::cout << "Number of processors    : " << nprocs << std::endl;
    177177        std::cout << "Number of threads       : " << nthreads << std::endl;
  • benchmark/readyQ/yield.cfa

    rb7fd9daf rf95634e  
    8080                }
    8181
    82                 printf("Duration (ms)       : %'ld\n", (end - start)`dms);
    83                 printf("Number of processors: %'d\n", nprocs);
    84                 printf("Number of threads   : %'d\n", nthreads);
    85                 printf("Total yields        : %'15llu\n", global_counter);
     82                printf("Took %'ld ms\n", (end - start)`ms);
    8683                printf("Yields per second   : %'18.2lf\n", ((double)global_counter) / (end - start)`s);
    8784                printf("ns per yields       : %'18.2lf\n", ((double)(end - start)`ns) / global_counter);
     85                printf("Total yields        : %'15llu\n", global_counter);
    8886                printf("Yields per procs    : %'15llu\n", global_counter / nprocs);
    8987                printf("Yields/sec/procs    : %'18.2lf\n", (((double)global_counter) / nprocs) / (end - start)`s);
  • benchmark/readyQ/yield.cpp

    rb7fd9daf rf95634e  
    154154
    155155                auto dur_nano = duration_cast<std::nano>(duration);
    156                 auto dur_dms  = duration_cast<std::milli>(duration);
    157156
    158                 printf("Duration (ms)       : %'.2lf\n", dur_dms );
     157                std::cout << "Took " << duration << " s\n";
    159158                printf("Total yields        : %'15llu\n", global_counter );
    160159                printf("Yields per procs    : %'15llu\n", global_counter / nprocs );
  • benchmark/rmit.py

    rb7fd9daf rf95634e  
    1616import random
    1717import re
    18 import socket
    1918import subprocess
    2019import sys
     
    9695        return nopts
    9796
    98 # returns the first option with key 'opt'
    99 def search_option(action, opt):
    100         i = 0
    101         while i < len(action):
    102                 if action[i] == opt:
    103                         i += 1
    104                         if i != len(action):
    105                                 return action[i]
    106                 i += 1
    107 
    108         return None
    109 
    11097def actions_eta(actions):
    11198        time = 0
    11299        for a in actions:
    113                 o = search_option(a, '-d')
    114                 if o :
    115                         time += int(o)
     100                i = 0
     101                while i < len(a):
     102                        if a[i] == '-d':
     103                                i += 1
     104                                if i != len(a):
     105                                        time += int(a[i])
     106                        i += 1
    116107        return time
    117 
    118 taskset_maps = None
    119 
    120 def init_taskset_maps():
    121         global taskset_maps
    122         known_hosts = {
    123                 "jax": {
    124                         range(  1,  24) : "48-71",
    125                         range( 25,  48) : "48-71,144-167",
    126                         range( 49,  96) : "48-95,144-191",
    127                         range( 97, 144) : "24-95,120-191",
    128                         range(145, 192) : "0-95,96-191",
    129                 },
    130         }
    131 
    132         if (host := socket.gethostname()) in known_hosts:
    133                 taskset_maps = known_hosts[host]
    134                 return True
    135 
    136         print("Warning unknown host '{}', disable taskset usage".format(host), file=sys.stderr)
    137         return False
    138 
    139 
    140 def settaskset_one(action):
    141         o = search_option(action, '-p')
    142         if not o:
    143                 return action
    144         try:
    145                 oi = int(o)
    146         except ValueError:
    147                 return action
    148 
    149         m = "Not found"
    150         for key in taskset_maps:
    151                 if oi in key:
    152                         return ['taskset', '-c', taskset_maps[key], *action]
    153 
    154         print("Warning no mapping for {} cores".format(oi), file=sys.stderr)
    155         return action
    156 
    157 def settaskset(actions):
    158         return [settaskset_one(a) for a in actions]
    159108
    160109if __name__ == "__main__":
     
    166115        parser.add_argument('--file', nargs='?', type=argparse.FileType('w'), default=sys.stdout)
    167116        parser.add_argument('--trials', help='Number of trials to run per combinaison', type=int, default=3)
    168         parser.add_argument('--notaskset', help='If specified, the trial will not use taskset to match the -p option', action='store_true')
    169117        parser.add_argument('command', metavar='command', type=str, nargs=1, help='the command prefix to run')
    170118        parser.add_argument('candidates', metavar='candidates', type=str, nargs='*', help='the candidate suffix to run')
     
    222170
    223171        # ================================================================================
    224         # Fixup the different commands
    225 
    226         # Add tasksets
    227         withtaskset = False
    228         if not options.notaskset and init_taskset_maps():
    229                 withtaskset = True
    230                 actions = settaskset(actions)
    231 
    232         # ================================================================================
    233         # Now that we know what to run, print it.
    234         # find expected time
    235         time = actions_eta(actions)
    236         print("Running {} trials{}".format(len(actions), "" if time == 0 else " (expecting to take {})".format(str(datetime.timedelta(seconds=int(time)))) ))
    237 
    238         # dry run if options ask for it
     172        # Figure out all the combinations to run
    239173        if options.list:
    240174                for a in actions:
     
    246180        # Prepare to run
    247181
     182        # find expected time
     183        time = actions_eta(actions)
     184        print("Running {} trials{}".format(len(actions), "" if time == 0 else " (expecting to take {})".format(str(datetime.timedelta(seconds=int(time)))) ))
     185
    248186        random.shuffle(actions)
    249187
     
    253191        first = True
    254192        for i, a in enumerate(actions):
    255                 sa = " ".join(a[3:] if withtaskset else a)
     193                sa = " ".join(a)
    256194                if first:
    257195                        first = False
     
    270208                                match = re.search("^(.*):(.*)$", s)
    271209                                if match:
    272                                         try:
    273                                                 fields[match.group(1).strip()] = float(match.group(2).strip().replace(',',''))
    274                                         except:
    275                                                 pass
    276 
    277                 options.file.write(json.dumps([a[3 if withtaskset else 0][2:], sa, fields]))
     210                                        fields[match.group(1).strip()] = float(match.group(2).strip().replace(',',''))
     211
     212                options.file.write(json.dumps([a[0][2:], sa, fields]))
    278213                options.file.flush()
    279214
  • doc/theses/andrew_beach_MMath/Makefile

    rb7fd9daf rf95634e  
    3131
    3232# The main rule, it does all the tex/latex processing.
    33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} termhandle.pstex resumhandle.pstex Makefile | ${BUILD}
     33${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} Makefile | ${BUILD}
    3434        ${LATEX} ${BASE}
    3535        ${BIBTEX} ${BUILD}/${BASE}
     
    4040${FIGTEX}: ${BUILD}/%.tex: %.fig | ${BUILD}
    4141        fig2dev -L eepic $< > $@
    42 
    43 %.pstex : %.fig | ${Build}
    44         fig2dev -L pstex $< > ${BUILD}/$@
    45         fig2dev -L pstex_t -p ${BUILD}/$@ $< > ${BUILD}/$@_t
    4642
    4743# Step through dvi & postscript to handle xfig specials.
  • doc/theses/andrew_beach_MMath/code/CondCatch.java

    rb7fd9daf rf95634e  
    66        static boolean should_catch = false;
    77
     8        static void throw_exception() throws EmptyException {
     9                throw new EmptyException();
     10        }
     11
     12        static void cond_catch() throws EmptyException {
     13                try {
     14                        throw_exception();
     15                } catch (EmptyException exc) {
     16                        if (!should_catch) {
     17                                throw exc;
     18                        }
     19                }
     20        }
     21
    822        private static long loop(int times) {
    923                long startTime = System.nanoTime();
    1024                for (int count = 0 ; count < times ; ++count) {
    1125                        try {
    12                                 try {
    13                                         throw new EmptyException();
    14                                 } catch (EmptyException exc) {
    15                                         if (!should_catch) {
    16                                                 throw exc;
    17                                         }
    18                                 }
     26                                cond_catch();
    1927                        } catch (EmptyException exc) {
    2028                                // ...
     
    3846
    3947                long time = loop(times);
    40                 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.);
     48                System.out.println("Run-Time (ns): " + time);
    4149        }
    4250}
  • doc/theses/andrew_beach_MMath/code/ThrowEmpty.java

    rb7fd9daf rf95634e  
    3939
    4040                long time = loop(times, total_frames);
    41                 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.);
     41                System.out.println("Run-Time (ns): " + time);
    4242        }
    4343}
  • doc/theses/andrew_beach_MMath/code/ThrowFinally.java

    rb7fd9daf rf95634e  
    4444
    4545                long time = loop(times, total_frames);
    46                 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.);
     46                System.out.println("Run-Time (ns): " + time);
    4747        }
    4848}
  • doc/theses/andrew_beach_MMath/code/ThrowOther.java

    rb7fd9daf rf95634e  
    5252
    5353                long time = loop(times, total_frames);
    54                 System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.);
     54                System.out.println("Run-Time (ns): " + time);
    5555        }
    5656}
  • doc/theses/andrew_beach_MMath/code/cond-catch.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.h>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
     8
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    910
    1011bool should_catch = false;
     12
     13void throw_exception() {
     14        throw (empty_exception){&empty_vt};
     15}
     16
     17void cond_catch() {
     18        try {
     19                throw_exception();
     20        } catch (empty_exception * exc ; should_catch) {
     21                asm volatile ("# catch block (conditional)");
     22        }
     23}
    1124
    1225int main(int argc, char * argv[]) {
    1326        unsigned int times = 1;
    1427        if (1 < argc) {
    15                 times = strto(argv[1], 0p, 10);
     28                times = strtol(argv[1], 0p, 10);
    1629        }
    1730        if (2 < argc) {
    18                 should_catch = (unsigned int)strto(argv[2], 0p, 2);
     31                should_catch = strtol(argv[2], 0p, 10);
    1932        }
    2033
     
    2235        for (unsigned int count = 0 ; count < times ; ++count) {
    2336                try {
    24                         throw (empty_exception){&empty_vt};
    25                 } catch (empty_exception * exc ; should_catch) {
    26                         asm volatile ("# catch block (conditional)");
     37                        cond_catch();
    2738                } catch (empty_exception * exc) {
    2839                        asm volatile ("# catch block (unconditional)");
     
    3041        }
    3142        Time end_time = timeHiRes();
    32         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     43        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    3344}
  • doc/theses/andrew_beach_MMath/code/cond-catch.cpp

    rb7fd9daf rf95634e  
    44#include <exception>
    55#include <iostream>
    6 #include <iomanip>
    76
    8 using namespace std;
    97using namespace std::chrono;
    108
     
    1210
    1311bool should_catch = false;
     12
     13void throw_exception() {
     14        throw EmptyException();
     15}
     16
     17void cond_catch() {
     18        try {
     19                throw_exception();
     20        } catch (EmptyException & exc) {
     21                if (!should_catch) {
     22                        throw;
     23                }
     24                asm volatile ("# catch block (conditional)");
     25        }
     26}
    1427
    1528int main(int argc, char * argv[]) {
     
    2538    for (unsigned int count = 0 ; count < times ; ++count) {
    2639        try {
    27                         try {
    28                                 throw EmptyException();
    29                         } catch (EmptyException & exc) {
    30                                 if (!should_catch) {
    31                                         throw;
    32                                 }
    33                                 asm volatile ("# catch block (conditional)");
    34                         }
     40                        cond_catch();
    3541                } catch (EmptyException &) {
    3642                        asm volatile ("# catch block (unconditional)");
     
    3945        time_point<steady_clock> end_time = steady_clock::now();
    4046        nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time);
    41         cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl;
     47        std::cout << "Run-Time (ns): " << duration.count() << std::endl;
    4248}
  • doc/theses/andrew_beach_MMath/code/cond-fixup.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
     8
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    910
    1011bool should_catch = false;
     12
     13void throw_exception() {
     14        throwResume (empty_exception){&empty_vt};
     15}
     16
     17void cond_catch() {
     18        try {
     19                throw_exception();
     20        } catchResume (empty_exception * exc ; should_catch) {
     21                asm volatile ("# fixup block (conditional)");
     22        }
     23}
    1124
    1225int main(int argc, char * argv[]) {
    1326        unsigned int times = 1;
    1427        if (1 < argc) {
    15                 times = strto(argv[1], 0p, 10);
     28                times = strtol(argv[1], 0p, 10);
    1629        }
    1730        if (2 < argc) {
    18                 should_catch = (unsigned int)strto(argv[2], 0p, 2);
     31                should_catch = strtol(argv[2], 0p, 10);
    1932        }
    2033
     
    2235        for (unsigned int count = 0 ; count < times ; ++count) {
    2336                try {
    24                         throwResume (empty_exception){&empty_vt};
    25                 } catchResume (empty_exception * exc ; should_catch) {
    26                         asm volatile ("# fixup block (conditional)");
     37                        cond_catch();
    2738                } catchResume (empty_exception * exc) {
    2839                        asm volatile ("# fixup block (unconditional)");
     
    3041        }
    3142        Time end_time = timeHiRes();
    32         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     43        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    3344}
  • doc/theses/andrew_beach_MMath/code/resume-detor.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
     8
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    910
    1011struct WithDestructor {};
     
    1617void unwind_destructor(unsigned int frames) {
    1718        if (frames) {
     19
    1820                WithDestructor object;
    1921                unwind_destructor(frames - 1);
     
    2729        unsigned int total_frames = 1;
    2830        if (1 < argc) {
    29                 times = strto(argv[1], 0p, 10);
     31                times = strtol(argv[1], 0p, 10);
    3032        }
    3133        if (2 < argc) {
    32                 total_frames = strto(argv[2], 0p, 10);
     34                total_frames = strtol(argv[2], 0p, 10);
    3335        }
    3436
     
    4244        }
    4345        Time end_time = timeHiRes();
    44         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     46        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    4547}
  • doc/theses/andrew_beach_MMath/code/resume-empty.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
    98
    10 void nounwind_empty(unsigned int frames) {
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     10
     11void unwind_empty(unsigned int frames) {
    1112        if (frames) {
    12                 nounwind_empty(frames - 1);
    13                 if ( frames == -1 ) printf( "42" );                             // prevent recursion optimizations
     13                unwind_empty(frames - 1);
    1414        } else {
    1515                throwResume (empty_exception){&empty_vt};
     
    2121        unsigned int total_frames = 1;
    2222        if (1 < argc) {
    23                 times = strto(argv[1], 0p, 10);
     23                times = strtol(argv[1], 0p, 10);
    2424        }
    2525        if (2 < argc) {
    26                 total_frames = strto(argv[2], 0p, 10);
     26                total_frames = strtol(argv[2], 0p, 10);
    2727        }
    2828
    2929        Time start_time = timeHiRes();
    30         for (unsigned int count = 0 ; count < times ; ++count) {
     30        for (int count = 0 ; count < times ; ++count) {
    3131                try {
    32                         nounwind_empty(total_frames);
     32                        unwind_empty(total_frames);
    3333                } catchResume (empty_exception *) {
    3434                        asm volatile ("# fixup block");
     
    3636        }
    3737        Time end_time = timeHiRes();
    38         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     38        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    3939}
  • doc/theses/andrew_beach_MMath/code/resume-finally.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
     8
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    910
    1011void unwind_finally(unsigned int frames) {
     
    2425        unsigned int total_frames = 1;
    2526        if (1 < argc) {
    26                 times = strto(argv[1], 0p, 10);
     27                times = strtol(argv[1], 0p, 10);
    2728        }
    2829        if (2 < argc) {
    29                 total_frames = strto(argv[2], 0p, 10);
     30                total_frames = strtol(argv[2], 0p, 10);
    3031        }
    3132
     
    3940        }
    4041        Time end_time = timeHiRes();
    41         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     42        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    4243}
  • doc/theses/andrew_beach_MMath/code/resume-other.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
    9 exception not_raised_exception;
     7EHM_EXCEPTION(empty_exception)();
    108
    11 void nounwind_other(unsigned int frames) {
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     10
     11EHM_EXCEPTION(not_raised_exception)();
     12
     13void unwind_other(unsigned int frames) {
    1214        if (frames) {
    1315                try {
    14                         nounwind_other(frames - 1);
     16                        unwind_other(frames - 1);
    1517                } catchResume (not_raised_exception *) {
    1618                        asm volatile ("# fixup block (stack)");
     
    2527        unsigned int total_frames = 1;
    2628        if (1 < argc) {
    27                 times = strto(argv[1], 0p, 10);
     29                times = strtol(argv[1], 0p, 10);
    2830        }
    2931        if (2 < argc) {
    30                 total_frames = strto(argv[2], 0p, 10);
     32                total_frames = strtol(argv[2], 0p, 10);
    3133        }
    3234
     
    3436        for (int count = 0 ; count < times ; ++count) {
    3537                try {
    36                         nounwind_other(total_frames);
     38                        unwind_other(total_frames);
    3739                } catchResume (empty_exception *) {
    3840                        asm volatile ("# fixup block (base)");
     
    4042        }
    4143        Time end_time = timeHiRes();
    42         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     44        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    4345}
  • doc/theses/andrew_beach_MMath/code/run.sh

    rb7fd9daf rf95634e  
    11#!/usr/bin/env bash
    22
    3 readonly ALL_TESTS=(raise-{empty,detor,finally,other} try-{catch,finally} \
    4                         cond-match-{all,none} fixup-{empty,other})
     3readonly ALL_TESTS=(cond-match-{all,none} cross-{catch,finally} \
     4                raise-{detor,empty,finally,other})
    55
    66gen-file-name() (
     
    1818)
    1919
    20 readonly N=${1:-1}
     20readonly N=${1:-5}
    2121readonly OUT_FILE=$(gen-file-name ${2:-run-%-$N})
    2222
  • doc/theses/andrew_beach_MMath/code/test.sh

    rb7fd9daf rf95634e  
    44# test.sh LANGUAGE TEST
    55#   Run the TEST in LANGUAGE.
    6 # test.sh -a
    7 #   Build all tests.
    86# test.sh -b SOURCE_FILE...
    97#   Build a test from SOURCE_FILE(s).
    10 # test.sh -c
    11 #   Clean all executables.
    128# test.sh -v LANGUAGE TEST FILE
    139#   View the result from TEST in LANGUAGE stored in FILE.
    1410
    15 readonly DIR=$(dirname "$(readlink -f "$0")")
    16 cd $DIR
    17 
    18 readonly MIL=000000
    19 # Various preset values used as arguments.
    20 readonly ITERS_1M=1$MIL
    21 readonly ITERS_10M=10$MIL
    22 readonly ITERS_100M=100$MIL
    23 readonly ITERS_1000M=1000$MIL
     11readonly ITERATIONS=1000000 # 1 000 000, one million
    2412readonly STACK_HEIGHT=100
    2513
     
    3523        case "$1" in
    3624        *.cfa)
    37                 # A symbolic link/local copy can be used as an override.
    38                 cmd=./cfa
    39                 if [ ! -x $cmd ]; then
    40                         cmd=cfa
    41                 fi
    42                 mmake "${1%.cfa}" "$1" $cmd -DNDEBUG -nodebug -O3 "$1" -o "${1%.cfa}"
     25                # Requires a symbolic link.
     26                mmake "${1%.cfa}" "$1" ./cfa -DNDEBUG -nodebug -O3 "$1" -o "${1%.cfa}"
    4327                ;;
    4428        *.cpp)
    45                 mmake "${1%.cpp}-cpp" "$1" g++-10 -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp"
     29                mmake "${1%.cpp}-cpp" "$1" g++ -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp"
    4630                ;;
    4731        *.java)
     
    5539)
    5640
    57 if [ "-a" = "$1" ]; then
    58         for file in *.cfa *.cpp *.java; do
    59                 build $file
    60         done
    61         exit 0
    62 elif [ "-b" = "$1" ]; then
     41if [ "-b" = "$1" ]; then
    6342        for file in "${@:2}"; do
    6443                build $file
    6544        done
    6645        exit 0
    67 elif [ "-c" = "$1" ]; then
    68         rm $(basename -s ".cfa" -a *.cfa)
    69         rm $(basename -s ".cpp" -a *.cpp)
    70         rm *-cpp
    71         rm *.class
    72         exit 0
    7346elif [ "-v" = "$1" -a 4 = "$#" ]; then
    74         TEST_LANG="$2"
    75         TEST_CASE="$3"
    76         VIEW_FILE="$4"
     47    TEST_LANG="$2"
     48    TEST_CASE="$3"
     49    VIEW_FILE="$4"
    7750elif [ 2 -eq "$#" ]; then
    7851        TEST_LANG="$1"
     
    9063
    9164case "$TEST_CASE" in
    92 raise-empty)
    93         CFAT="./throw-empty $ITERS_1M $STACK_HEIGHT"
    94         CFAR="./resume-empty $ITERS_10M $STACK_HEIGHT"
    95         CPP="./throw-empty-cpp $ITERS_1M $STACK_HEIGHT"
    96         JAVA="java ThrowEmpty $ITERS_1M $STACK_HEIGHT"
    97         PYTHON="./throw-empty.py $ITERS_1M $STACK_HEIGHT"
     65cond-match-all)
     66        CFAT="./cond-catch $ITERATIONS 1"
     67        CFAR="./cond-fixup $ITERATIONS 1"
     68        CPP="./cond-catch-cpp $ITERATIONS 1"
     69        JAVA="java CondCatch $ITERATIONS 1"
     70        PYTHON="./cond_catch.py $ITERATIONS 1"
     71        ;;
     72cond-match-none)
     73        CFAT="./cond-catch $ITERATIONS 0"
     74        CFAR="./cond-fixup $ITERATIONS 0"
     75        CPP="./cond-catch-cpp $ITERATIONS 0"
     76        JAVA="java CondCatch $ITERATIONS 0"
     77        PYTHON="./cond_catch.py $ITERATIONS 0"
     78        ;;
     79cross-catch)
     80        CFAT="./cross-catch $ITERATIONS"
     81        CFAR="./cross-resume $ITERATIONS"
     82        CPP="./cross-catch-cpp $ITERATIONS"
     83        JAVA="java CrossCatch $ITERATIONS"
     84        PYTHON="./cross_catch.py $ITERATIONS"
     85        ;;
     86cross-finally)
     87        CFAT="./cross-finally $ITERATIONS"
     88        CFAR=unsupported
     89        CPP=unsupported
     90        JAVA="java CrossFinally $ITERATIONS"
     91        PYTHON="./cross_finally.py $ITERATIONS"
    9892        ;;
    9993raise-detor)
    100         CFAT="./throw-detor $ITERS_1M $STACK_HEIGHT"
    101         CFAR="./resume-detor $ITERS_10M $STACK_HEIGHT"
    102         CPP="./throw-detor-cpp $ITERS_1M $STACK_HEIGHT"
     94        CFAT="./throw-detor $ITERATIONS $STACK_HEIGHT"
     95        CFAR="./resume-detor $ITERATIONS $STACK_HEIGHT"
     96        CPP="./throw-detor-cpp $ITERATIONS $STACK_HEIGHT"
    10397        JAVA=unsupported
    10498        PYTHON=unsupported
    10599        ;;
     100raise-empty)
     101        CFAT="./throw-empty $ITERATIONS $STACK_HEIGHT"
     102        CFAR="./resume-empty $ITERATIONS $STACK_HEIGHT"
     103        CPP="./throw-empty-cpp $ITERATIONS $STACK_HEIGHT"
     104        JAVA="java ThrowEmpty $ITERATIONS $STACK_HEIGHT"
     105        PYTHON="./throw_empty.py $ITERATIONS $STACK_HEIGHT"
     106        ;;
    106107raise-finally)
    107         CFAT="./throw-finally $ITERS_1M $STACK_HEIGHT"
    108         CFAR="./resume-finally $ITERS_10M $STACK_HEIGHT"
     108        CFAT="./throw-finally $ITERATIONS $STACK_HEIGHT"
     109        CFAR="./resume-finally $ITERATIONS $STACK_HEIGHT"
    109110        CPP=unsupported
    110         JAVA="java ThrowFinally $ITERS_1M $STACK_HEIGHT"
    111         PYTHON="./throw-finally.py $ITERS_1M $STACK_HEIGHT"
     111        JAVA="java ThrowFinally $ITERATIONS $STACK_HEIGHT"
     112        PYTHON="./throw_finally.py $ITERATIONS $STACK_HEIGHT"
    112113        ;;
    113114raise-other)
    114         CFAT="./throw-other $ITERS_1M $STACK_HEIGHT"
    115         CFAR="./resume-other $ITERS_10M $STACK_HEIGHT"
    116         CPP="./throw-other-cpp $ITERS_1M $STACK_HEIGHT"
    117         JAVA="java ThrowOther $ITERS_1M $STACK_HEIGHT"
    118         PYTHON="./throw-other.py $ITERS_1M $STACK_HEIGHT"
    119         ;;
    120 try-catch)
    121         CFAT="./try-catch $ITERS_1000M"
    122         CFAR="./try-resume $ITERS_1000M"
    123         CPP="./try-catch-cpp $ITERS_1000M"
    124         JAVA="java TryCatch $ITERS_1000M"
    125         PYTHON="./try-catch.py $ITERS_1000M"
    126         ;;
    127 try-finally)
    128         CFAT="./try-finally $ITERS_1000M"
    129         CFAR=unsupported
    130         CPP=unsupported
    131         JAVA="java TryFinally $ITERS_1000M"
    132         PYTHON="./try-finally.py $ITERS_1000M"
    133         ;;
    134 cond-match-all)
    135         CFAT="./cond-catch $ITERS_10M 1"
    136         CFAR="./cond-fixup $ITERS_100M 1"
    137         CPP="./cond-catch-cpp $ITERS_10M 1"
    138         JAVA="java CondCatch $ITERS_10M 1"
    139         PYTHON="./cond-catch.py $ITERS_10M 1"
    140         ;;
    141 cond-match-none)
    142         CFAT="./cond-catch $ITERS_10M 0"
    143         CFAR="./cond-fixup $ITERS_100M 0"
    144         CPP="./cond-catch-cpp $ITERS_10M 0"
    145         JAVA="java CondCatch $ITERS_10M 0"
    146         PYTHON="./cond-catch.py $ITERS_10M 0"
    147         ;;
    148 fixup-empty)
    149         CFAT="./fixup-empty-f $ITERS_10M $STACK_HEIGHT"
    150         CFAR="./fixup-empty-r $ITERS_10M $STACK_HEIGHT"
    151         CPP="./fixup-empty-cpp $ITERS_10M $STACK_HEIGHT"
    152         JAVA="java FixupEmpty $ITERS_10M $STACK_HEIGHT"
    153         PYTHON="./fixup-empty.py $ITERS_10M $STACK_HEIGHT"
    154         ;;
    155 fixup-other)
    156         CFAT="./fixup-other-f $ITERS_10M $STACK_HEIGHT"
    157         CFAR="./fixup-other-r $ITERS_10M $STACK_HEIGHT"
    158         CPP="./fixup-other-cpp $ITERS_10M $STACK_HEIGHT"
    159         JAVA="java FixupOther $ITERS_10M $STACK_HEIGHT"
    160         PYTHON="./fixup-other.py $ITERS_10M $STACK_HEIGHT"
     115        CFAT="./throw-other $ITERATIONS $STACK_HEIGHT"
     116        CFAR="./resume-other $ITERATIONS $STACK_HEIGHT"
     117        CPP="./throw-other-cpp $ITERATIONS $STACK_HEIGHT"
     118        JAVA="java ThrowOther $ITERATIONS $STACK_HEIGHT"
     119        PYTHON="./throw_other.py $ITERATIONS $STACK_HEIGHT"
    161120        ;;
    162121*)
     
    181140
    182141if [ -n "$VIEW_FILE" ]; then
    183         grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time.*: !!;T;p'
    184         exit
     142    grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time (ns): !!;T;p'
     143    exit
    185144fi
    186145
  • doc/theses/andrew_beach_MMath/code/throw-detor.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
     8
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    910
    1011struct WithDestructor {};
     
    2728        unsigned int total_frames = 1;
    2829        if (1 < argc) {
    29                 times = strto(argv[1], 0p, 10);
     30                times = strtol(argv[1], 0p, 10);
    3031        }
    3132        if (2 < argc) {
    32                 total_frames = strto(argv[2], 0p, 10);
     33                total_frames = strtol(argv[2], 0p, 10);
    3334        }
    3435
     
    4243        }
    4344        Time end_time = timeHiRes();
    44         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     45        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    4546}
  • doc/theses/andrew_beach_MMath/code/throw-detor.cpp

    rb7fd9daf rf95634e  
    44#include <exception>
    55#include <iostream>
    6 #include <iomanip>
    76
    8 using namespace std;
    97using namespace std::chrono;
    108
     
    4644        time_point<steady_clock> end_time = steady_clock::now();
    4745        nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time);
    48         cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl;
     46        std::cout << "Run-Time (ns): " << duration.count() << std::endl;
    4947}
  • doc/theses/andrew_beach_MMath/code/throw-empty.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
     8
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    910
    1011void unwind_empty(unsigned int frames) {
    1112        if (frames) {
    1213                unwind_empty(frames - 1);
    13                 if ( frames == -1 ) printf( "42" );                             // prevent recursion optimizations
    1414        } else {
    1515                throw (empty_exception){&empty_vt};
     
    2121        unsigned int total_frames = 1;
    2222        if (1 < argc) {
    23                 times = strto(argv[1], 0p, 10);
     23                times = strtol(argv[1], 0p, 10);
    2424        }
    2525        if (2 < argc) {
    26                 total_frames = strto(argv[2], 0p, 10);
     26                total_frames = strtol(argv[2], 0p, 10);
    2727        }
    2828
     
    3636        }
    3737        Time end_time = timeHiRes();
    38         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     38        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    3939}
  • doc/theses/andrew_beach_MMath/code/throw-empty.cpp

    rb7fd9daf rf95634e  
    11// Throw Across Empty Function
    22#include <chrono>
    3 #include <cstdio>
    43#include <cstdlib>
    54#include <exception>
    65#include <iostream>
    7 #include <iomanip>
    86
    9 using namespace std;
    107using namespace std::chrono;
    118
     
    1512        if (frames) {
    1613                unwind_empty(frames - 1);
    17                 if (-1 == frames) printf("~");
    1814        } else {
    1915                throw (EmptyException){};
     
    4137        time_point<steady_clock> end_time = steady_clock::now();
    4238        nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time);
    43         cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl;
     39        std::cout << "Run-Time (ns): " << duration.count() << std::endl;
    4440}
  • doc/theses/andrew_beach_MMath/code/throw-finally.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
     7EHM_EXCEPTION(empty_exception)();
    98
    10 unsigned int frames;                                                                    // use global because of gcc thunk problem
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    1110
    12 void unwind_finally(unsigned int dummy) {
     11void unwind_finally(unsigned int frames) {
    1312        if (frames) {
    14                 frames -= 1;
    1513                try {
    16                         unwind_finally(42);
     14                        unwind_finally(frames - 1);
    1715                } finally {
    1816                        asm volatile ("# finally block");
    1917                }
    2018        } else {
    21                 dummy = 42;
    2219                throw (empty_exception){&empty_vt};
    2320        }
     
    2825        unsigned int total_frames = 1;
    2926        if (1 < argc) {
    30                 times = strto(argv[1], 0p, 10);
     27                times = strtol(argv[1], 0p, 10);
    3128        }
    3229        if (2 < argc) {
    33                 total_frames = strto(argv[2], 0p, 10);
     30                total_frames = strtol(argv[2], 0p, 10);
    3431        }
    35         frames = total_frames;
    3632
    3733        Time start_time = timeHiRes();
    3834        for (int count = 0 ; count < times ; ++count) {
    3935                try {
    40                         unwind_finally(42);
     36                        unwind_finally(total_frames);
    4137                } catch (empty_exception *) {
    4238                        asm volatile ("# catch block");
     
    4440        }
    4541        Time end_time = timeHiRes();
    46         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     42        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    4743}
  • doc/theses/andrew_beach_MMath/code/throw-other.cfa

    rb7fd9daf rf95634e  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>                                                                   // strto
     5#include <stdlib.hfa>
    66
    7 exception empty_exception;
    8 vtable(empty_exception) empty_vt;
    9 exception not_raised_exception;
     7EHM_EXCEPTION(empty_exception)();
    108
    11 unsigned int frames;                                                                    // use global because of gcc thunk problem
     9EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    1210
    13 void unwind_other(unsigned int dummy) {
     11EHM_EXCEPTION(not_raised_exception)();
     12
     13void unwind_other(unsigned int frames) {
    1414        if (frames) {
    15                 frames -= 1;
    1615                try {
    17                         unwind_other(42);
     16                        unwind_other(frames - 1);
    1817                } catch (not_raised_exception *) {
    1918                        asm volatile ("# catch block (stack)");
    2019                }
    2120        } else {
    22                 dummy = 42;
    2321                throw (empty_exception){&empty_vt};
    2422        }
     
    2927        unsigned int total_frames = 1;
    3028        if (1 < argc) {
    31                 times = strto(argv[1], 0p, 10);
     29                times = strtol(argv[1], 0p, 10);
    3230        }
    3331        if (2 < argc) {
    34                 total_frames = strto(argv[2], 0p, 10);
     32                total_frames = strtol(argv[2], 0p, 10);
    3533        }
    36         frames = total_frames;
    3734
    3835        Time start_time = timeHiRes();
    3936        for (int count = 0 ; count < times ; ++count) {
    4037                try {
    41                         unwind_other(42);
     38                        unwind_other(total_frames);
    4239                } catch (empty_exception *) {
    4340                        asm volatile ("# catch block (base)");
     
    4542        }
    4643        Time end_time = timeHiRes();
    47         sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
     44        sout | "Run-Time (ns): " | (end_time - start_time)`ns;
    4845}
  • doc/theses/andrew_beach_MMath/code/throw-other.cpp

    rb7fd9daf rf95634e  
    44#include <exception>
    55#include <iostream>
    6 #include <iomanip>
    76
    8 using namespace std;
    97using namespace std::chrono;
    108
     
    4543        time_point<steady_clock> end_time = steady_clock::now();
    4644        nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time);
    47         cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl;
     45        std::cout << "Run-Time (ns): " << duration.count() << std::endl;
    4846}
  • doc/theses/andrew_beach_MMath/conclusion.tex

    rb7fd9daf rf95634e  
    11\chapter{Conclusion}
    2 \label{c:conclusion}
    32% Just a little knot to tie the paper together.
    43
    5 In the previous chapters, this thesis presents the design and implementation
     4In the previous chapters this thesis presents the design and implementation
    65of \CFA's exception handling mechanism (EHM).
    7 Both the design and implementation are based off of tools and
    8 techniques developed for other programming languages but they were adapted to
    9 better fit \CFA's feature set and add a few features that do not exist in
    10 other EHMs,
    11 including conditional matching, default handlers for unhandled exceptions
    12 and cancellation though coroutines and threads back to the program main stack.
     6Both the design and implementation are based off of tools and techniques
     7developed for other programming languages but they were adapted to better fit
     8\CFA's feature set.
    139
    1410The resulting features cover all of the major use cases of the most popular
    1511termination EHMs of today, along with reintroducing resumption exceptions and
    16 creating some new features that fit with \CFA's larger programming patterns,
    17 such as virtuals independent of traditional objects.
     12creating some new features that fix with \CFA's larger programming patterns.
    1813
    19 The \CFA project's test suite has been expanded to test the EHM.
    20 The implementation's performance has also been
    21 compared to other implementations with a small set of targeted
    22 micro-benchmarks.
     14The implementation has been tested and compared to other implementations.
    2315The results, while not cutting edge, are good enough for prototyping, which
    24 is \CFA's current stage of development.
     16is \CFA's stage of development.
    2517
    26 This initial EHM will bring valuable new features to \CFA in its own right
    27 but also serves as a tool and motivation for other developments in the
    28 language.
     18This is a valuable new feature for \CFA in its own right but also serves
     19as a tool (and motivation) for other developments in the language.
  • doc/theses/andrew_beach_MMath/exception-layout.fig

    rb7fd9daf rf95634e  
    2828        0 0 1.00 240.00 240.00
    2929         360 405 360 2070
    30 4 0 0 50 -1 0 12 0.0000 0 135 1080 2700 585 Fixed Header\001
    31 4 0 0 50 -1 0 12 0.0000 0 135 1575 540 990 Cforall Information\001
    32 4 0 0 50 -1 0 12 0.0000 0 180 1695 540 585 _Unwind_Exception\001
    33 4 0 0 50 -1 0 12 0.0000 0 180 1245 540 1530 User Exception\001
    34 4 0 0 50 -1 0 12 0.0000 0 180 1185 2655 1530 Variable Body\001
    35 4 0 0 50 -1 0 12 0.0000 0 165 1110 2655 1215 (Fixed Offset)\001
     304 0 0 50 -1 0 12 0.0000 4 135 1080 2700 585 Fixed Header\001
     314 0 0 50 -1 0 12 0.0000 4 135 1710 540 990 Cforall Information\001
     324 0 0 50 -1 0 12 0.0000 4 165 1530 540 585 _Unwind_Exception\001
     334 0 0 50 -1 0 12 0.0000 4 165 1260 540 1530 User Exception\001
     344 0 0 50 -1 0 12 0.0000 4 165 1170 2655 1530 Variable Body\001
     354 0 0 50 -1 0 12 0.0000 4 165 1260 2655 1215 (Fixed Offset)\001
  • doc/theses/andrew_beach_MMath/existing.tex

    rb7fd9daf rf95634e  
    66compatibility with C and its programmers.  \CFA is designed to have an
    77orthogonal feature-set based closely on the C programming paradigm
    8 (non-object-oriented), and these features can be added incrementally to an
    9 existing C code-base,
    10 allowing programmers to learn \CFA on an as-needed basis.
     8(non-object-oriented) and these features can be added incrementally to an
     9existing C code-base allowing programmers to learn \CFA on an as-needed basis.
    1110
    1211Only those \CFA features pertaining to this thesis are discussed.
     12% Also, only new features of \CFA will be discussed,
    1313A familiarity with
    1414C or C-like languages is assumed.
     
    1717\CFA has extensive overloading, allowing multiple definitions of the same name
    1818to be defined~\cite{Moss18}.
    19 \begin{cfa}
    20 char i; int i; double i;
    21 int f(); double f();
    22 void g( int ); void g( double );
    23 \end{cfa}
     19\begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
     20char @i@; int @i@; double @i@;
     21int @f@(); double @f@();
     22void @g@( int ); void @g@( double );
     23\end{lstlisting}
    2424This feature requires name mangling so the assembly symbols are unique for
    2525different overloads. For compatibility with names in C, there is also a syntax
     
    4646\CFA adds a reference type to C as an auto-dereferencing pointer.
    4747They work very similarly to pointers.
    48 Reference-types are written the same way as pointer-types, but each
     48Reference-types are written the same way as a pointer-type but each
    4949asterisk (@*@) is replaced with a ampersand (@&@);
    50 this includes cv-qualifiers (\snake{const} and \snake{volatile})
    51 and multiple levels of reference.
    52 
    53 Generally, references act like pointers with an implicit dereferencing
     50this includes cv-qualifiers and multiple levels of reference.
     51
     52Generally, references act like pointers with an implicate dereferencing
    5453operation added to each use of the variable.
    5554These automatic dereferences may be disabled with the address-of operator
     
    6463int && rri = ri;
    6564rri = 3;
    66 &ri = &j;
     65&ri = &j; // rebindable
    6766ri = 5;
    6867\end{cfa}
     
    8079\end{minipage}
    8180
    82 References are intended to be used when the indirection of a pointer is
    83 required, but the address is not as important as the value and dereferencing
    84 is the common usage.
     81References are intended for pointer situations where dereferencing is the common usage,
     82\ie the value is more important than the pointer.
    8583Mutable references may be assigned to by converting them to a pointer
    86 with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above.
     84with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above
    8785
    8886\section{Operators}
    8987
    9088\CFA implements operator overloading by providing special names, where
    91 operator expressions are translated into function calls using these names.
     89operator usages are translated into function calls using these names.
    9290An operator name is created by taking the operator symbols and joining them with
    9391@?@s to show where the arguments go.
    9492For example,
    9593infixed multiplication is @?*?@, while prefix dereference is @*?@.
    96 This syntax makes it easy to tell the difference between prefix operations
    97 (such as @++?@) and postfix operations (@?++@).
    98 
    99 As an example, here are the addition and equality operators for a point type.
     94This syntax make it easy to tell the difference between prefix operations
     95(such as @++?@) and post-fix operations (@?++@).
     96For example, plus and equality operators are defined for a point type.
    10097\begin{cfa}
    10198point ?+?(point a, point b) { return point{a.x + b.x, a.y + b.y}; }
     
    105102}
    106103\end{cfa}
    107 Note that this syntax works effectively as a textual transformation;
    108 the compiler converts all operators into functions and then resolves them
    109 normally. This means any combination of types may be used,
    110 although nonsensical ones (like @double ?==?(point, int);@) are discouraged.
    111 This feature is also used for all builtin operators as well,
    112 although those are implicitly provided by the language.
     104Note these special names are not limited to builtin
     105operators, and hence, may be used with arbitrary types.
     106\begin{cfa}
     107double ?+?( int x, point y ); // arbitrary types
     108\end{cfa}
     109% Some ``near misses", that are that do not match an operator form but looks like
     110% it may have been supposed to, will generate warning but otherwise they are
     111% left alone.
     112Because operators are never part of the type definition they may be added
     113at any time, including on built-in types.
    113114
    114115%\subsection{Constructors and Destructors}
    115 In \CFA, constructors and destructors are operators, which means they are
    116 functions with special operator names, rather than type names as in \Cpp.
    117 Both constructors and destructors can be implicity called by the compiler,
    118 however the operator names allow explicit calls.
    119 % Placement new means that this is actually equivant to C++.
     116
     117\CFA also provides constructors and destructors as operators, which means they
     118are functions with special operator names rather than type names in \Cpp.
     119While constructors and destructions are normally called implicitly by the compiler,
     120the special operator names, allow explicit calls.
     121
     122% Placement new means that this is actually equivalent to C++.
    120123
    121124The special name for a constructor is @?{}@, which comes from the
     
    126129struct Example { ... };
    127130void ?{}(Example & this) { ... }
    128 {
    129         Example a;
    130         Example b = {};
    131 }
    132131void ?{}(Example & this, char first, int num) { ... }
    133 {
    134         Example c = {'a', 2};
    135 }
    136 \end{cfa}
    137 Both @a@ and @b@ will be initalized with the first constructor,
    138 @b@ because of the explicit call and @a@ implicitly.
    139 @c@ will be initalized with the second constructor.
    140 Currently, there is no general way to skip initialization.
    141 % I don't use @= anywhere in the thesis.
    142 
     132Example a;              // implicit constructor calls
     133Example b = {};
     134Example c = {'a', 2};
     135\end{cfa}
     136Both @a@ and @b@ are initialized with the first constructor,
     137while @c@ is initialized with the second.
     138Constructor calls can be replaced with C initialization using special operator \lstinline{@=}.
     139\begin{cfa}
     140Example d @= {42};
     141\end{cfa}
    143142% I don't like the \^{} symbol but $^\wedge$ isn't better.
    144143Similarly, destructors use the special name @^?{}@ (the @^@ has no special
    145144meaning).
     145% These are a normally called implicitly called on a variable when it goes out
     146% of scope. They can be called explicitly as well.
    146147\begin{cfa}
    147148void ^?{}(Example & this) { ... }
    148149{
    149         Example d;
    150         ^?{}(d);
    151 
    152         Example e;
    153 } // Implicit call of ^?{}(e);
     150        Example e;      // implicit constructor call
     151        ^?{}(e);                // explicit destructor call
     152        ?{}(e);         // explicit constructor call
     153} // implicit destructor call
    154154\end{cfa}
    155155
     
    203203do_twice(i);
    204204\end{cfa}
    205 Any value with a type fulfilling the assertion may be passed as an argument to
     205Any object with a type fulfilling the assertion may be passed as an argument to
    206206a @do_twice@ call.
    207207
     
    223223function. The matched assertion function is then passed as a function pointer
    224224to @do_twice@ and called within it.
    225 The global definition of @do_once@ is ignored, however if @quadruple@ took a
     225The global definition of @do_once@ is ignored, however if quadruple took a
    226226@double@ argument, then the global definition would be used instead as it
    227 would then be a better match.\cite{Moss19}
    228 
    229 To avoid typing long lists of assertions, constraints can be collected into
    230 a convenient package called a @trait@, which can then be used in an assertion
     227is a better match.
     228% Aaron's thesis might be a good reference here.
     229
     230To avoid typing long lists of assertions, constraints can be collect into
     231convenient package called a @trait@, which can then be used in an assertion
    231232instead of the individual constraints.
    232233\begin{cfa}
     
    242243functions and variables, and are usually used to create a shorthand for, and
    243244give descriptive names to, common groupings of assertions describing a certain
    244 functionality, like @summable@, @listable@, \etc.
     245functionality, like @sumable@, @listable@, \etc.
    245246
    246247Polymorphic structures and unions are defined by qualifying an aggregate type
    247248with @forall@. The type variables work the same except they are used in field
    248 declarations instead of parameters, returns and local variable declarations.
     249declarations instead of parameters, returns, and local variable declarations.
    249250\begin{cfa}
    250251forall(dtype T)
     
    252253        node(T) * next;
    253254        T * data;
    254 };
     255}
    255256node(int) inode;
    256257\end{cfa}
     
    262263
    263264\section{Control Flow}
    264 \CFA has a number of advanced control-flow features: @generator@, @coroutine@,
    265 @monitor@, @mutex@ parameters, and @thread@.
     265\CFA has a number of advanced control-flow features: @generator@, @coroutine@, @monitor@, @mutex@ parameters, and @thread@.
    266266The two features that interact with
    267267the exception system are @coroutine@ and @thread@; they and their supporting
     
    270270\subsection{Coroutine}
    271271A coroutine is a type with associated functions, where the functions are not
    272 required to finish execution when control is handed back to the caller.
    273 Instead,
     272required to finish execution when control is handed back to the caller. Instead
    274273they may suspend execution at any time and be resumed later at the point of
    275 last suspension.
    276 Coroutine
     274last suspension. (Generators are stackless and coroutines are stackful.) These
    277275types are not concurrent but share some similarities along with common
    278 underpinnings, so they are combined with the \CFA threading library.
    279 % I had mention of generators, but they don't actually matter here.
     276underpinnings, so they are combined with the \CFA threading library. Further
     277discussion in this section only refers to the coroutine because generators are
     278similar.
    280279
    281280In \CFA, a coroutine is created using the @coroutine@ keyword, which is an
     
    294293};
    295294CountUp countup;
     295for (10) sout | resume(countup).next; // print 10 values
    296296\end{cfa}
    297297Each coroutine has a @main@ function, which takes a reference to a coroutine
    298298object and returns @void@.
    299299%[numbers=left] Why numbers on this one?
    300 \begin{cfa}
     300\begin{cfa}[numbers=left,numberstyle=\scriptsize\sf]
    301301void main(CountUp & this) {
    302         for (unsigned int next = 0 ; true ; ++next) {
    303                 this.next = next;
     302        for (unsigned int up = 0;; ++up) {
     303                this.next = up;
    304304                suspend;$\label{suspend}$
    305305        }
     
    307307\end{cfa}
    308308In this function, or functions called by this function (helper functions), the
    309 @suspend@ statement is used to return execution to the coroutine's caller
    310 without terminating the coroutine's function.
     309@suspend@ statement is used to return execution to the coroutine's resumer
     310without terminating the coroutine's function(s).
    311311
    312312A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@.
    313313The first resume calls the @main@ function at the top. Thereafter, resume calls
    314314continue a coroutine in the last suspended function after the @suspend@
    315 statement. In this case there is only one and, hence, the difference between
    316 subsequent calls is the state of variables inside the function and the
    317 coroutine object.
    318 The return value of @resume@ is a reference to the coroutine, to make it
    319 convent to access fields of the coroutine in the same expression.
    320 Here is a simple example in a helper function:
    321 \begin{cfa}
    322 unsigned int get_next(CountUp & this) {
    323         return resume(this).next;
    324 }
    325 \end{cfa}
    326 
    327 When the main function returns, the coroutine halts and can no longer be
    328 resumed.
     315statement, in this case @main@ line~\ref{suspend}.  The @resume@ function takes
     316a reference to the coroutine structure and returns the same reference. The
     317return value allows easy access to communication variables defined in the
     318coroutine object. For example, the @next@ value for coroutine object @countup@
     319is both generated and collected in the single expression:
     320@resume(countup).next@.
    329321
    330322\subsection{Monitor and Mutex Parameter}
    331 Concurrency does not guarantee ordering; without ordering, results are
     323Concurrency does not guarantee ordering; without ordering results are
    332324non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@
    333325(mutual exclusion) parameters. A monitor is another kind of aggregate, where
     
    335327@mutex@ parameters.
    336328
    337 A function that requires deterministic (ordered) execution acquires mutual
     329A function that requires deterministic (ordered) execution, acquires mutual
    338330exclusion on a monitor object by qualifying an object reference parameter with
    339 the @mutex@ qualifier.
    340 \begin{cfa}
    341 void example(MonitorA & mutex argA, MonitorB & mutex argB);
    342 \end{cfa}
     331@mutex@.
     332\begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
     333void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB);
     334\end{lstlisting}
    343335When the function is called, it implicitly acquires the monitor lock for all of
    344336the mutex parameters without deadlock.  This semantics means all functions with
     
    347339
    348340\subsection{Thread}
    349 Functions, generators and coroutines are sequential, so there is only a single
     341Functions, generators, and coroutines are sequential so there is only a single
    350342(but potentially sophisticated) execution path in a program. Threads introduce
    351343multiple execution paths that continue independently.
    352344
    353345For threads to work safely with objects requires mutual exclusion using
    354 monitors and mutex parameters. For threads to work safely with other threads
     346monitors and mutex parameters. For threads to work safely with other threads,
    355347also requires mutual exclusion in the form of a communication rendezvous, which
    356348also supports internal synchronization as for mutex objects. For exceptions,
     
    370362{
    371363        StringWorker stringworker; // fork thread running in "main"
    372 } // Implicit call to join(stringworker), waits for completion.
     364} // implicitly join with thread / wait for completion
    373365\end{cfa}
    374366The thread main is where a new thread starts execution after a fork operation
  • doc/theses/andrew_beach_MMath/features.tex

    rb7fd9daf rf95634e  
    55and begins with a general overview of EHMs. It is not a strict
    66definition of all EHMs nor an exhaustive list of all possible features.
    7 However, it does cover the most common structure and features found in them.
     7However it does cover the most common structure and features found in them.
    88
    99\section{Overview of EHMs}
     
    1919
    2020\paragraph{Raise}
    21 The raise is the starting point for exception handling,
     21The raise is the starting point for exception handling
    2222by raising an exception, which passes it to
    2323the EHM.
     
    3030\paragraph{Handle}
    3131The primary purpose of an EHM is to run some user code to handle a raised
    32 exception. This code is given, along with some other information,
    33 in a handler.
     32exception. This code is given, with some other information, in a handler.
    3433
    3534A handler has three common features: the previously mentioned user code, a
    36 region of code it guards and an exception label/condition that matches
    37 against the raised exception.
     35region of code it guards, and an exception label/condition that matches
     36the raised exception.
    3837Only raises inside the guarded region and raising exceptions that match the
    3938label can be handled by a given handler.
     
    4241
    4342The @try@ statements of \Cpp, Java and Python are common examples. All three
    44 also show another common feature of handlers: they are grouped by the guarded
    45 region.
     43show the common features of guarded region, raise, matching and handler.
     44\begin{cfa}
     45try {                           // guarded region
     46        ...     
     47        throw exception;        // raise
     48        ...     
     49} catch( exception ) {  // matching condition, with exception label
     50        ...                             // handler code
     51}
     52\end{cfa}
    4653
    4754\subsection{Propagation}
    4855After an exception is raised comes what is usually the biggest step for the
    49 EHM: finding and setting up the handler for execution.
    50 The propagation from raise to
     56EHM: finding and setting up the handler for execution. The propagation from raise to
    5157handler can be broken up into three different tasks: searching for a handler,
    5258matching against the handler and installing the handler.
     
    5460\paragraph{Searching}
    5561The EHM begins by searching for handlers that might be used to handle
    56 the exception.
    57 The search will find handlers that have the raise site in their guarded
     62the exception. The search is restricted to
     63handlers that have the raise site in their guarded
    5864region.
    5965The search includes handlers in the current function, as well as any in
     
    6167
    6268\paragraph{Matching}
    63 Each handler found is with the raised exception. The exception
     69Each handler found is matched with the raised exception. The exception
    6470label defines a condition that is used with the exception and decides if
    6571there is a match or not.
    66 %
    6772In languages where the first match is used, this step is intertwined with
    6873searching; a match check is performed immediately after the search finds
     
    7984different course of action for this case.
    8085This situation only occurs with unchecked exceptions as checked exceptions
    81 (such as in Java) can make the guarantee.
     86(such as in Java) are guaranteed to find a matching handler.
    8287The unhandled action is usually very general, such as aborting the program.
    8388
    8489\paragraph{Hierarchy}
    8590A common way to organize exceptions is in a hierarchical structure.
    86 This pattern comes from object-oriented languages where the
     91This pattern comes from object-orientated languages where the
    8792exception hierarchy is a natural extension of the object hierarchy.
    8893
     
    9398A handler labeled with any given exception can handle exceptions of that
    9499type or any child type of that exception. The root of the exception hierarchy
    95 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types
     100(here \code{C}{exception}) acts as a catch-all, leaf types catch single types,
    96101and the exceptions in the middle can be used to catch different groups of
    97102related exceptions.
    98103
    99104This system has some notable advantages, such as multiple levels of grouping,
    100 the ability for libraries to add new exception types and the isolation
     105the ability for libraries to add new exception types, and the isolation
    101106between different sub-hierarchies.
    102107This design is used in \CFA even though it is not a object-orientated
    103 language, so different tools are used to create the hierarchy.
     108language; so different tools are used to create the hierarchy.
    104109
    105110% Could I cite the rational for the Python IO exception rework?
     
    119124from the raise to the handler and back again.
    120125So far, only communication of the exception's identity is covered.
    121 A common communication method for adding information to an exception
    122 is putting fields into the exception instance
     126A common communication method for passing more information is putting fields into the exception instance
    123127and giving the handler access to them.
    124 % You can either have pointers/references in the exception, or have p/rs to
    125 % the exception when it doesn't have to be copied.
    126 Passing references or pointers allows data at the raise location to be
    127 updated, passing information in both directions.
     128Using reference fields pointing to data at the raise location allows data to be
     129passed in both directions.
    128130
    129131\section{Virtuals}
    130 \label{s:virtuals}
    131 A common feature in many programming languages is a tool to pair code
    132 (behaviour) with data.
    133 In \CFA, this is done with the virtual system,
    134 which allow type information to be abstracted away, recovered and allow
    135 operations to be performed on the abstract objects.
    136 
     132\label{s:Virtuals}
    137133Virtual types and casts are not part of \CFA's EHM nor are they required for
    138134an EHM.
    139135However, one of the best ways to support an exception hierarchy
    140136is via a virtual hierarchy and dispatch system.
    141 Ideally, the virtual system would have been part of \CFA before the work
     137Ideally, the virtual system should have been part of \CFA before the work
    142138on exception handling began, but unfortunately it was not.
    143139Hence, only the features and framework needed for the EHM were
    144 designed and implemented for this thesis.
    145 Other features were considered to ensure that
     140designed and implemented for this thesis. Other features were considered to ensure that
    146141the structure could accommodate other desirable features in the future
    147142but are not implemented.
    148143The rest of this section only discusses the implemented subset of the
    149 virtual system design.
     144virtual-system design.
    150145
    151146The virtual system supports multiple ``trees" of types. Each tree is
     
    154149number of children.
    155150Any type that belongs to any of these trees is called a virtual type.
     151For example, the following hypothetical syntax creates two virtual-type trees.
     152\begin{flushleft}
     153\lstDeleteShortInline@
     154\begin{tabular}{@{\hspace{20pt}}l@{\hspace{20pt}}l}
     155\begin{cfa}
     156vtype V0, V1(V0), V2(V0);
     157vtype W0, W1(W0), W2(W1);
     158\end{cfa}
     159&
     160\raisebox{-0.6\totalheight}{\input{vtable}}
     161\end{tabular}
     162\lstMakeShortInline@
     163\end{flushleft}
    156164% A type's ancestors are its parent and its parent's ancestors.
    157165% The root type has no ancestors.
    158166% A type's descendants are its children and its children's descendants.
    159 
    160 For the purposes of illustration, a proposed, but unimplemented, syntax
    161 will be used. Each virtual type is represented by a trait with an annotation
    162 that makes it a virtual type. This annotation is empty for a root type, which
    163 creates a new tree:
    164 \begin{cfa}
    165 trait root_type(T) virtual() {}
    166 \end{cfa}
    167 The annotation may also refer to any existing virtual type to make this new
    168 type a child of that type and part of the same tree. The parent may itself
    169 be a child or a root type and may have any number of existing children.
    170 
    171 % OK, for some reason the b and t positioning options are reversed here.
    172 \begin{minipage}[b]{0.6\textwidth}
    173 \begin{cfa}
    174 trait child_a(T) virtual(root_type) {}
    175 trait grandchild(T) virtual(child_a) {}
    176 trait child_b(T) virtual(root_type) {}
    177 \end{cfa}
    178 \end{minipage}
    179 \begin{minipage}{0.4\textwidth}
    180 \begin{center}
    181 \input{virtual-tree}
    182 \end{center}
    183 \end{minipage}
    184 
    185 Every virtual type also has a list of virtual members and a unique id.
    186 Both are stored in a virtual table.
    187 Every instance of a virtual type also has a pointer to a virtual table stored
    188 in it, although there is no per-type virtual table as in many other languages.
    189 
    190 The list of virtual members is accumulated from the root type down the tree.
    191 Every virtual type
    192 inherits the list of virtual members from its parent and may add more
    193 virtual members to the end of the list which are passed on to its children.
    194 Again, using the unimplemented syntax this might look like:
    195 \begin{cfa}
    196 trait root_type(T) virtual() {
    197         const char * to_string(T const & this);
    198         unsigned int size;
    199 }
    200 
    201 trait child_type(T) virtual(root_type) {
    202         char * irrelevant_function(int, char);
    203 }
    204 \end{cfa}
    205 % Consider adding a diagram, but we might be good with the explanation.
    206 
    207 As @child_type@ is a child of @root_type@, it has the virtual members of
    208 @root_type@ (@to_string@ and @size@) as well as the one it declared
    209 (@irrelevant_function@).
    210 
    211 It is important to note that these are virtual members, and may contain   
    212 arbitrary fields, functions or otherwise.
    213 The names ``size" and ``align" are reserved for the size and alignment of the
    214 virtual type, and are always automatically initialized as such.
    215 The other special case is uses of the trait's polymorphic argument
    216 (@T@ in the example), which are always updated to refer to the current
    217 virtual type. This allows functions that refer to the polymorphic argument
    218 to act as traditional virtual methods (@to_string@ in the example), as the
    219 object can always be passed to a virtual method in its virtual table.
    220 
    221 Up until this point, the virtual system is similar to ones found in
    222 object-oriented languages, but this is where \CFA diverges.
    223 Objects encapsulate a single set of methods in each type,
    224 universally across the entire program,
    225 and indeed all programs that use that type definition.
    226 The only way to change any method is to inherit and define a new type with
    227 its own universal implementation. In this sense,
    228 these object-oriented types are ``closed" and cannot be altered.
    229 % Because really they are class oriented.
    230 
    231 In \CFA, types do not encapsulate any code.
    232 Whether or not a type satisfies any given assertion, and hence any trait, is
    233 context sensitive. Types can begin to satisfy a trait, stop satisfying it or
    234 satisfy the same trait at any lexical location in the program.
    235 In this sense, a type's implementation in the set of functions and variables
    236 that allow it to satisfy a trait is ``open" and can change
    237 throughout the program.
     167Every virtual type (tree node) has a pointer to a virtual table with a unique
     168@Id@ and a list of virtual members (see \autoref{s:VirtualSystem} for
     169details). Children inherit their parent's list of virtual members but may add
     170and/or replace members.  For example,
     171\begin{cfa}
     172vtable W0 | { int ?<?( int, int ); int ?+?( int, int ); }
     173vtable W1 | { int ?+?( int, int ); int w, int ?-?( int, int ); }
     174\end{cfa}
     175creates a virtual table for @W0@ initialized with the matching @<@ and @+@
     176operations visible at this declaration context.  Similarly, @W1@ is initialized
     177with @<@ from inheritance with @W0@, @+@ is replaced, and @-@ is added, where
     178both operations are matched at this declaration context. It is important to
     179note that these are virtual members, not virtual methods of object-orientated
     180programming, and can be of any type. Finally, trait names can be used to
     181specify the list of virtual members.
     182
     183\PAB{Need to look at these when done.
     184
     185\CFA still supports virtual methods as a special case of virtual members.
     186Function pointers that take a pointer to the virtual type are modified
     187with each level of inheritance so that refers to the new type.
     188This means an object can always be passed to a function in its virtual table
     189as if it were a method.
     190\todo{Clarify (with an example) virtual methods.}
     191}%
     192
     193Up until this point the virtual system is similar to ones found in
     194object-orientated languages but this is where \CFA diverges. Objects encapsulate a
     195single set of methods in each type, universally across the entire program,
     196and indeed all programs that use that type definition. Even if a type inherits and adds methods, it still encapsulate a
     197single set of methods. In this sense,
     198object-oriented types are ``closed" and cannot be altered.
     199
     200In \CFA, types do not encapsulate any code. Traits are local for each function and
     201types can satisfy a local trait, stop satisfying it or, satisfy the same
     202trait in a different way at any lexical location in the program where a function is call.
     203In this sense, the set of functions/variables that satisfy a trait for a type is ``open" as the set can change at every call site.
    238204This capability means it is impossible to pick a single set of functions
    239205that represent a type's implementation across a program.
     
    242208type. A user can define virtual tables that are filled in at their
    243209declaration and given a name. Anywhere that name is visible, even if it is
    244 defined locally inside a function (although in this case the user must ensure
    245 it outlives any objects that use it), it can be used.
     210defined locally inside a function \PAB{What does this mean? (although that means it does not have a
     211static lifetime)}, it can be used.
    246212Specifically, a virtual type is ``bound" to a virtual table that
    247213sets the virtual members for that object. The virtual members can be accessed
    248214through the object.
    249215
    250 This means virtual tables are declared and named in \CFA.
    251 They are declared as variables, using the type
    252 @vtable(VIRTUAL_TYPE)@ and any valid name. For example:
    253 \begin{cfa}
    254 vtable(virtual_type_name) table_name;
    255 \end{cfa}
    256 
    257 Like any variable, they may be forward declared with the @extern@ keyword.
    258 Forward declaring virtual tables is relatively common.
    259 Many virtual types have an ``obvious" implementation that works in most
    260 cases.
    261 A pattern that has appeared in the early work using virtuals is to
    262 implement a virtual table with the the obvious definition and place a forward
    263 declaration of it in the header beside the definition of the virtual type.
    264 
    265 Even on the full declaration, no initializer should be used.
    266 Initialization is automatic.
    267 The type id and special virtual members ``size" and ``align" only depend on
    268 the virtual type, which is fixed given the type of the virtual table, and
    269 so the compiler fills in a fixed value.
    270 The other virtual members are resolved using the best match to the member's
    271 name and type, in the same context as the virtual table is declared using
    272 \CFA's normal resolution rules.
    273 
    274 While much of the virtual infrastructure has been created,
    275 it is currently only used
     216While much of the virtual infrastructure is created, it is currently only used
    276217internally for exception handling. The only user-level feature is the virtual
    277218cast, which is the same as the \Cpp \code{C++}{dynamic_cast}.
     
    282223Note, the syntax and semantics matches a C-cast, rather than the function-like
    283224\Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be
    284 pointers to virtual types.
     225a pointer to a virtual type.
    285226The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type
    286227of @TYPE@, and if true, returns a pointer to the
    287228@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
    288 This allows the expression to be used as both a cast and a type check.
    289 
    290 \section{Exceptions}
    291 
    292 The syntax for declaring an exception is the same as declaring a structure
    293 except the keyword:
    294 \begin{cfa}
    295 exception TYPE_NAME {
    296         FIELDS
    297 };
    298 \end{cfa}
    299 
    300 Fields are filled in the same way as a structure as well. However, an extra
    301 field is added that contains the pointer to the virtual table.
    302 It must be explicitly initialized by the user when the exception is
    303 constructed.
    304 
    305 Here is an example of declaring an exception type along with a virtual table,
    306 assuming the exception has an ``obvious" implementation and a default
    307 virtual table makes sense.
    308 
    309 \begin{minipage}[t]{0.4\textwidth}
    310 Header (.hfa):
    311 \begin{cfa}
    312 exception Example {
    313         int data;
    314 };
    315 
    316 extern vtable(Example)
    317         example_base_vtable;
    318 \end{cfa}
    319 \end{minipage}
    320 \begin{minipage}[t]{0.6\textwidth}
    321 Implementation (.cfa):
    322 \begin{cfa}
    323 vtable(Example) example_base_vtable
    324 \end{cfa}
    325 \vfil
    326 \end{minipage}
    327 
    328 %\subsection{Exception Details}
    329 This is the only interface needed when raising and handling exceptions.
    330 However, it is actually a shorthand for a more complex
    331 trait-based interface.
    332 
    333 The language views exceptions through a series of traits.
    334 If a type satisfies them, then it can be used as an exception. The following
     229
     230\section{Exception}
     231% Leaving until later, hopefully it can talk about actual syntax instead
     232% of my many strange macros. Syntax aside I will also have to talk about the
     233% features all exceptions support.
     234
     235Exceptions are defined by the trait system; there are a series of traits, and
     236if a type satisfies them, then it can be used as an exception. The following
    335237is the base trait all exceptions need to match.
    336238\begin{cfa}
     
    339241};
    340242\end{cfa}
    341 The trait is defined over two types: the exception type and the virtual table
     243The trait is defined over two types, the exception type and the virtual table
    342244type. Each exception type should have a single virtual table type.
    343245There are no actual assertions in this trait because the trait system
     
    345247completing the virtual system). The imaginary assertions would probably come
    346248from a trait defined by the virtual system, and state that the exception type
    347 is a virtual type,
    348 that that the type is a descendant of @exception_t@ (the base exception type)
    349 and allow the user to find the virtual table type.
     249is a virtual type, is a descendant of @exception_t@ (the base exception type),
     250and note its virtual table type.
    350251
    351252% I did have a note about how it is the programmer's responsibility to make
     
    365266};
    366267\end{cfa}
    367 Both traits ensure a pair of types is an exception type and
    368 its virtual table type,
     268Both traits ensure a pair of types are an exception type, its virtual table
     269type,
    369270and defines one of the two default handlers. The default handlers are used
    370 as fallbacks and are discussed in detail in \autoref{s:ExceptionHandling}.
     271as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}.
    371272
    372273However, all three of these traits can be tricky to use directly.
    373274While there is a bit of repetition required,
    374275the largest issue is that the virtual table type is mangled and not in a user
    375 facing way. So, these three macros are provided to wrap these traits to
     276facing way. So these three macros are provided to wrap these traits to
    376277simplify referring to the names:
    377 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.
     278@IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@, and @IS_RESUMPTION_EXCEPTION@.
    378279
    379280All three take one or two arguments. The first argument is the name of the
     
    381282The second (optional) argument is a parenthesized list of polymorphic
    382283arguments. This argument is only used with polymorphic exceptions and the
    383 list is passed to both types.
     284list is be passed to both types.
    384285In the current set-up, the two types always have the same polymorphic
    385 arguments, so these macros can be used without losing flexibility.
    386 
    387 For example, consider a function that is polymorphic over types that have a
     286arguments so these macros can be used without losing flexibility.
     287
     288For example consider a function that is polymorphic over types that have a
    388289defined arithmetic exception:
    389290\begin{cfa}
     
    402303Both operations follow the same set of steps.
    403304First, a user raises an exception.
    404 Second, the exception propagates up the stack, searching for a handler.
     305Second, the exception propagates up the stack.
    405306Third, if a handler is found, the exception is caught and the handler is run.
    406307After that control continues at a raise-dependent location.
    407 As an alternate to the third step,
    408 if a handler is not found, a default handler is run and, if it returns,
    409 then control
     308Fourth, if a handler is not found, a default handler is run and, if it returns, then control
    410309continues after the raise.
    411310
    412 The differences between the two operations include how propagation is
    413 performed, where execution continues after an exception is handled
    414 and which default handler is run.
     311%This general description covers what the two kinds have in common.
     312The differences in the two operations include how propagation is performed, where execution continues
     313after an exception is caught and handled, and which default handler is run.
    415314
    416315\subsection{Termination}
    417316\label{s:Termination}
    418 Termination handling is the familiar kind of handling
    419 used in most programming
     317Termination handling is the familiar EHM and used in most programming
    420318languages with exception handling.
    421319It is a dynamic, non-local goto. If the raised exception is matched and
     
    449347Then propagation starts with the search. \CFA uses a ``first match" rule so
    450348matching is performed with the copied exception as the search key.
    451 It starts from the raise site and proceeds towards base of the stack,
     349It starts from the raise in the throwing function and proceeds towards the base of the stack,
    452350from callee to caller.
    453351At each stack frame, a check is made for termination handlers defined by the
     
    463361\end{cfa}
    464362When viewed on its own, a try statement simply executes the statements
    465 in the \snake{GUARDED_BLOCK} and when those are finished,
     363in the \snake{GUARDED_BLOCK}, and when those are finished,
    466364the try statement finishes.
    467365
     
    489387termination exception types.
    490388The global default termination handler performs a cancellation
    491 (as described in \vref{s:Cancellation})
    492 on the current stack with the copied exception.
    493 Since it is so general, a more specific handler can be defined,
    494 overriding the default behaviour for the specific exception types.
    495 
    496 For example, consider an error reading a configuration file.
    497 This is most likely a problem with the configuration file (@config_error@),
    498 but the function could have been passed the wrong file name (@arg_error@).
    499 In this case the function could raise one exception and then, if it is
    500 unhandled, raise the other.
    501 This is not usual behaviour for either exception so changing the
    502 default handler will be done locally:
    503 \begin{cfa}
    504 {
    505         void defaultTerminationHandler(config_error &) {
    506                 throw (arg_error){arg_vt};
    507         }
    508         throw (config_error){config_vt};
    509 }
    510 \end{cfa}
     389(see \vref{s:Cancellation} for the justification) on the current stack with the copied exception.
     390Since it is so general, a more specific handler is usually
     391defined, possibly with a detailed message, and used for specific exception type, effectively overriding the default handler.
    511392
    512393\subsection{Resumption}
    513394\label{s:Resumption}
    514395
    515 Resumption exception handling is less familar form of exception handling,
    516 but is
     396Resumption exception handling is the less familar EHM, but is
    517397just as old~\cite{Goodenough75} and is simpler in many ways.
    518398It is a dynamic, non-local function call. If the raised exception is
     
    523403function once the error is corrected, and
    524404ignorable events, such as logging where nothing needs to happen and control
    525 should always continue from the raise site.
    526 
    527 Except for the changes to fit into that pattern, resumption exception
    528 handling is symmetric with termination exception handling, by design
    529 (see \autoref{s:Termination}).
     405should always continue from the raise point.
    530406
    531407A resumption raise is started with the @throwResume@ statement:
     
    533409throwResume EXPRESSION;
    534410\end{cfa}
    535 % The new keywords are currently ``experimental" and not used in this work.
    536 It works much the same way as the termination raise, except the
    537 type must satisfy the \snake{is_resumption_exception} that uses the
    538 default handler: \defaultResumptionHandler.
    539 This can be specialized for particular exception types.
    540 
    541 At run-time, no exception copy is made. Since
     411\todo{Decide on a final set of keywords and use them everywhere.}
     412It works much the same way as the termination throw.
     413The expression must return a reference to a resumption exception,
     414where the resumption exception is any type that satisfies the trait
     415@is_resumption_exception@ at the call site.
     416The assertions from this trait are available to
     417the exception system while handling the exception.
     418
     419At run-time, no exception copy is made, since
    542420resumption does not unwind the stack nor otherwise remove values from the
    543 current scope, there is no need to manage memory to keep the exception
    544 allocated.
    545 
    546 Then propagation starts with the search,
    547 following the same search path as termination,
    548 from the raise site to the base of stack and top of try statement to bottom.
    549 However, the handlers on try statements are defined by @catchResume@ clauses.
     421current scope, so there is no need to manage memory to keep the exception in scope.
     422
     423Then propagation starts with the search. It starts from the raise in the
     424resuming function and proceeds towards the base of the stack,
     425from callee to caller.
     426At each stack frame, a check is made for resumption handlers defined by the
     427@catchResume@ clauses of a @try@ statement.
    550428\begin{cfa}
    551429try {
     
    557435}
    558436\end{cfa}
    559 Note that termination handlers and resumption handlers may be used together
     437% PAB, you say this above.
     438% When a try statement is executed, it simply executes the statements in the
     439% @GUARDED_BLOCK@ and then finishes.
     440%
     441% However, while the guarded statements are being executed, including any
     442% invoked functions, all the handlers in these statements are included in the
     443% search path.
     444% Hence, if a resumption exception is raised, these handlers may be matched
     445% against the exception and may handle it.
     446%
     447% Exception matching checks the handler in each catch clause in the order
     448% they appear, top to bottom. If the representation of the raised exception type
     449% is the same or a descendant of @EXCEPTION_TYPE@$_i$, then @NAME@$_i$
     450% (if provided) is bound to a pointer to the exception and the statements in
     451% @HANDLER_BLOCK@$_i$ are executed.
     452% If control reaches the end of the handler, execution continues after the
     453% the raise statement that raised the handled exception.
     454%
     455% Like termination, if no resumption handler is found during the search,
     456% then the default handler (\defaultResumptionHandler) visible at the raise
     457% statement is called. It will use the best match at the raise sight according
     458% to \CFA's overloading rules. The default handler is
     459% passed the exception given to the raise. When the default handler finishes
     460% execution continues after the raise statement.
     461%
     462% There is a global @defaultResumptionHandler{} is polymorphic over all
     463% resumption exceptions and performs a termination throw on the exception.
     464% The \defaultTerminationHandler{} can be overridden by providing a new
     465% function that is a better match.
     466
     467The @GUARDED_BLOCK@ and its associated nested guarded statements work the same
     468for resumption as for termination, as does exception matching at each
     469@catchResume@. Similarly, if no resumption handler is found during the search,
     470then the currently visible default handler (\defaultResumptionHandler) is
     471called and control continues after the raise statement if it returns. Finally,
     472there is also a global @defaultResumptionHandler@, which can be overridden,
     473that is polymorphic over all resumption exceptions but performs a termination
     474throw on the exception rather than a cancellation.
     475
     476Throwing the exception in @defaultResumptionHandler@ has the positive effect of
     477walking the stack a second time for a recovery handler. Hence, a programmer has
     478two chances for help with a problem, fixup or recovery, should either kind of
     479handler appear on the stack. However, this dual stack walk leads to following
     480apparent anomaly:
     481\begin{cfa}
     482try {
     483        throwResume E;
     484} catch (E) {
     485        // this handler runs
     486}
     487\end{cfa}
     488because the @catch@ appears to handle a @throwResume@, but a @throwResume@ only
     489matches with @catchResume@. The anomaly results because the unmatched
     490@catchResuem@, calls @defaultResumptionHandler@, which in turn throws @E@.
     491
     492% I wonder if there would be some good central place for this.
     493Note, termination and resumption handlers may be used together
    560494in a single try statement, intermixing @catch@ and @catchResume@ freely.
    561495Each type of handler only interacts with exceptions from the matching
    562496kind of raise.
    563 Like @catch@ clauses, @catchResume@ clauses have no effect if an exception
    564 is not raised.
    565 
    566 The matching rules are exactly the same as well.
    567 The first major difference here is that after
    568 @EXCEPTION_TYPE@$_i$ is matched and @NAME@$_i$ is bound to the exception,
    569 @HANDLER_BLOCK@$_i$ is executed right away without first unwinding the stack.
    570 After the block has finished running, control jumps to the raise site, where
    571 the just handled exception came from, and continues executing after it,
    572 not after the try statement.
    573 
    574 For instance, a resumption used to send messages to the logger may not
    575 need to be handled at all. Putting the following default handler
    576 at the global scope can make handling that exception optional by default.
    577 \begin{cfa}
    578 void defaultResumptionHandler(log_message &) {
    579     // Nothing, it is fine not to handle logging.
    580 }
    581 // ... No change at raise sites. ...
    582 throwResume (log_message){strlit_log, "Begin event processing."}
    583 \end{cfa}
    584497
    585498\subsubsection{Resumption Marking}
     
    588501not unwind the stack. A side effect is that, when a handler is matched
    589502and run, its try block (the guarded statements) and every try statement
    590 searched before it are still on the stack. Their presence can lead to
    591 the recursive resumption problem.\cite{Buhr00a}
    592 % Other possible citation is MacLaren77, but the form is different.
     503searched before it are still on the stack. There presence can lead to
     504the \emph{recursive resumption problem}.
    593505
    594506The recursive resumption problem is any situation where a resumption handler
     
    604516When this code is executed, the guarded @throwResume@ starts a
    605517search and matches the handler in the @catchResume@ clause. This
    606 call is placed on the stack above the try-block.
    607 Now the second raise in the handler searches the same try block,
    608 matches again and then puts another instance of the
     518call is placed on the stack above the try-block. Now the second raise in the handler
     519searches the same try block, matches, and puts another instance of the
    609520same handler on the stack leading to infinite recursion.
    610521
    611 While this situation is trivial and easy to avoid, much more complex cycles
    612 can form with multiple handlers and different exception types.
     522While this situation is trivial and easy to avoid, much more complex cycles can
     523form with multiple handlers and different exception types.  The key point is
     524that the programmer's intuition expects every raise in a handler to start
     525searching \emph{below} the @try@ statement, making it difficult to understand
     526and fix the problem.
     527
    613528To prevent all of these cases, each try statement is ``marked" from the
    614 time the exception search reaches it to either when a handler completes
    615 handling that exception or when the search reaches the base
     529time the exception search reaches it to either when a matching handler
     530completes or when the search reaches the base
    616531of the stack.
    617532While a try statement is marked, its handlers are never matched, effectively
     
    622537\end{center}
    623538
    624 There are other sets of marking rules that could be used.
    625 For instance, marking just the handlers that caught the exception
     539There are other sets of marking rules that could be used,
     540for instance, marking just the handlers that caught the exception,
    626541would also prevent recursive resumption.
    627 However, the rules selected mirror what happens with termination,
    628 so this reduces the amount of rules and patterns a programmer has to know.
    629 
    630 The marked try statements are the ones that would be removed from
     542However, the rule selected mirrors what happens with termination,
     543and hence, matches programmer intuition that a raise searches below a try.
     544
     545In detail, the marked try statements are the ones that would be removed from
    631546the stack for a termination exception, \ie those on the stack
    632547between the handler and the raise statement.
     
    665580// Handle a failure relating to f2 further down the stack.
    666581\end{cfa}
    667 In this example, the file that experienced the IO error is used to decide
     582In this example the file that experienced the IO error is used to decide
    668583which handler should be run, if any at all.
    669584
     
    694609
    695610\subsection{Comparison with Reraising}
    696 In languages without conditional catch -- that is, no ability to match an
    697 exception based on something other than its type -- it can be mimicked
    698 by matching all exceptions of the right type, checking any additional
    699 conditions inside the handler and re-raising the exception if it does not
    700 match those.
    701 
    702 Here is a minimal example comparing both patterns, using @throw;@
    703 (no operand) to start a re-raise.
     611Without conditional catch, the only approach to match in more detail is to reraise
     612the exception after it has been caught, if it could not be handled.
    704613\begin{center}
    705 \begin{tabular}{l r}
    706 \begin{cfa}
    707 try {
    708     do_work_may_throw();
    709 } catch(exception_t * exc ;
    710                 can_handle(exc)) {
    711     handle(exc);
    712 }
    713 
    714 
    715 
     614\begin{tabular}{l|l}
     615\begin{cfa}
     616try {
     617        do_work_may_throw();
     618} catch(excep_t * ex; can_handle(ex)) {
     619
     620        handle(ex);
     621
     622
     623
     624}
    716625\end{cfa}
    717626&
    718627\begin{cfa}
    719628try {
    720     do_work_may_throw();
    721 } catch(exception_t * exc) {
    722     if (can_handle(exc)) {
    723         handle(exc);
    724     } else {
    725         throw;
    726     }
    727 }
    728 \end{cfa}
    729 \end{tabular}
    730 \end{center}
    731 At first glance, catch-and-reraise may appear to just be a quality-of-life
    732 feature, but there are some significant differences between the two
    733 strategies.
    734 
    735 A simple difference that is more important for \CFA than many other languages
    736 is that the raise site changes with a re-raise, but does not with a
    737 conditional catch.
    738 This is important in \CFA because control returns to the raise site to run
    739 the per-site default handler. Because of this, only a conditional catch can
    740 allow the original raise to continue.
    741 
    742 The more complex issue comes from the difference in how conditional
    743 catches and re-raises handle multiple handlers attached to a single try
    744 statement. A conditional catch will continue checking later handlers while
    745 a re-raise will skip them.
    746 If the different handlers could handle some of the same exceptions,
    747 translating a try statement that uses one to use the other can quickly
    748 become non-trivial:
    749 
    750 \noindent
    751 Original, with conditional catch:
    752 \begin{cfa}
    753 ...
    754 } catch (an_exception * e ; check_a(e)) {
    755         handle_a(e);
    756 } catch (exception_t * e ; check_b(e)) {
    757         handle_b(e);
    758 }
    759 \end{cfa}
    760 Translated, with re-raise:
    761 \begin{cfa}
    762 ...
    763 } catch (exception_t * e) {
    764         an_exception * an_e = (virtual an_exception *)e;
    765         if (an_e && check_a(an_e)) {
    766                 handle_a(an_e);
    767         } else if (check_b(e)) {
    768                 handle_b(e);
     629        do_work_may_throw();
     630} catch(excep_t * ex) {
     631        if (can_handle(ex)) {
     632                handle(ex);
    769633        } else {
    770634                throw;
     
    772636}
    773637\end{cfa}
    774 (There is a simpler solution if @handle_a@ never raises exceptions,
    775 using nested try statements.)
    776 
    777 % } catch (an_exception * e ; check_a(e)) {
    778 %     handle_a(e);
    779 % } catch (exception_t * e ; !(virtual an_exception *)e && check_b(e)) {
    780 %     handle_b(e);
    781 % }
     638\end{tabular}
     639\end{center}
     640Notice catch-and-reraise increases complexity by adding additional data and
     641code to the exception process. Nevertheless, catch-and-reraise can simulate
     642conditional catch straightforwardly, when exceptions are disjoint, \ie no
     643inheritance.
     644
     645However, catch-and-reraise simulation becomes unusable for exception inheritance.
     646\begin{flushleft}
     647\begin{cfa}[xleftmargin=6pt]
     648exception E1;
     649exception E2(E1); // inheritance
     650\end{cfa}
     651\begin{tabular}{l|l}
     652\begin{cfa}
     653try {
     654        ... foo(); ... // raise E1/E2
     655        ... bar(); ... // raise E1/E2
     656} catch( E2 e; e.rtn == foo ) {
     657        ...
     658} catch( E1 e; e.rtn == foo ) {
     659        ...
     660} catch( E1 e; e.rtn == bar ) {
     661        ...
     662}
     663
     664\end{cfa}
     665&
     666\begin{cfa}
     667try {
     668        ... foo(); ...
     669        ... bar(); ...
     670} catch( E2 e ) {
     671        if ( e.rtn == foo ) { ...
     672        } else throw; // reraise
     673} catch( E1 e ) {
     674        if (e.rtn == foo) { ...
     675        } else if (e.rtn == bar) { ...
     676        else throw; // reraise
     677}
     678\end{cfa}
     679\end{tabular}
     680\end{flushleft}
     681The derived exception @E2@ must be ordered first in the catch list, otherwise
     682the base exception @E1@ catches both exceptions. In the catch-and-reraise code
     683(right), the @E2@ handler catches exceptions from both @foo@ and
     684@bar@. However, the reraise misses the following catch clause. To fix this
     685problem, an enclosing @try@ statement is need to catch @E2@ for @bar@ from the
     686reraise, and its handler must duplicate the inner handler code for @bar@. To
     687generalize, this fix for any amount of inheritance and complexity of try
     688statement requires a technique called \emph{try-block
     689splitting}~\cite{Krischer02}, which is not discussed in this thesis. It is
     690sufficient to state that conditional catch is more expressive than
     691catch-and-reraise in terms of complexity.
     692
     693\begin{comment}
     694That is, they have the same behaviour in isolation.
     695Two things can expose differences between these cases.
     696
     697One is the existence of multiple handlers on a single try statement.
     698A reraise skips all later handlers for a try statement but a conditional
     699catch does not.
     700% Hence, if an earlier handler contains a reraise later handlers are
     701% implicitly skipped, with a conditional catch they are not.
     702Still, they are equivalently powerful,
     703both can be used two mimic the behaviour of the other,
     704as reraise can pack arbitrary code in the handler and conditional catches
     705can put arbitrary code in the predicate.
     706% I was struggling with a long explanation about some simple solutions,
     707% like repeating a condition on later handlers, and the general solution of
     708% merging everything together. I don't think it is useful though unless its
     709% for a proof.
     710% https://en.cppreference.com/w/cpp/language/throw
     711
     712The question then becomes ``Which is a better default?"
     713We believe that not skipping possibly useful handlers is a better default.
     714If a handler can handle an exception it should and if the handler can not
     715handle the exception then it is probably safer to have that explicitly
     716described in the handler itself instead of implicitly described by its
     717ordering with other handlers.
     718% Or you could just alter the semantics of the throw statement. The handler
     719% index is in the exception so you could use it to know where to start
     720% searching from in the current try statement.
     721% No place for the `goto else;` metaphor.
     722
     723The other issue is all of the discussion above assumes that the only
     724way to tell apart two raises is the exception being raised and the remaining
     725search path.
     726This is not true generally, the current state of the stack can matter in
     727a number of cases, even only for a stack trace after an program abort.
     728But \CFA has a much more significant need of the rest of the stack, the
     729default handlers for both termination and resumption.
     730
     731% For resumption it turns out it is possible continue a raise after the
     732% exception has been caught, as if it hadn't been caught in the first place.
     733This becomes a problem combined with the stack unwinding used in termination
     734exception handling.
     735The stack is unwound before the handler is installed, and hence before any
     736reraises can run. So if a reraise happens the previous stack is gone,
     737the place on the stack where the default handler was supposed to run is gone,
     738if the default handler was a local function it may have been unwound too.
     739There is no reasonable way to restore that information, so the reraise has
     740to be considered as a new raise.
     741This is the strongest advantage conditional catches have over reraising,
     742they happen before stack unwinding and avoid this problem.
     743
     744% The one possible disadvantage of conditional catch is that it runs user
     745% code during the exception search. While this is a new place that user code
     746% can be run destructors and finally clauses are already run during the stack
     747% unwinding.
    782748%
    783 % } catch (an_exception * e)
    784 %   if (check_a(e)) {
    785 %     handle_a(e);
    786 %   } else throw;
    787 % } catch (exception_t * e)
    788 %   if (check_b(e)) {
    789 %     handle_b(e);
    790 %   } else throw;
    791 % }
    792 In similar simple examples, translating from re-raise to conditional catch
    793 takes less code but it does not have a general, trivial solution either.
    794 
    795 So, given that the two patterns do not trivially translate into each other,
    796 it becomes a matter of which on should be encouraged and made the default.
    797 From the premise that if a handler could handle an exception then it
    798 should, it follows that checking as many handlers as possible is preferred.
    799 So, conditional catch and checking later handlers is a good default.
     749% https://www.cplusplus.com/reference/exception/current_exception/
     750%   `exception_ptr current_exception() noexcept;`
     751% https://www.python.org/dev/peps/pep-0343/
     752\end{comment}
    800753
    801754\section{Finally Clauses}
    802755\label{s:FinallyClauses}
    803 Finally clauses are used to perform unconditional cleanup when leaving a
     756Finally clauses are used to preform unconditional clean-up when leaving a
    804757scope and are placed at the end of a try statement after any handler clauses:
    805758\begin{cfa}
     
    813766The @FINALLY_BLOCK@ is executed when the try statement is removed from the
    814767stack, including when the @GUARDED_BLOCK@ finishes, any termination handler
    815 finishes or during an unwind.
     768finishes, or during an unwind.
    816769The only time the block is not executed is if the program is exited before
    817770the stack is unwound.
     
    819772Execution of the finally block should always finish, meaning control runs off
    820773the end of the block. This requirement ensures control always continues as if
    821 the finally clause is not present, \ie finally is for cleanup, not changing
     774the finally clause is not present, \ie finally is for cleanup not changing
    822775control flow.
    823776Because of this requirement, local control flow out of the finally block
    824777is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or
    825778@return@ that causes control to leave the finally block. Other ways to leave
    826 the finally block, such as a @longjmp@ or termination are much harder to check,
    827 and at best require additional run-time overhead, and so are only
     779the finally block, such as a long jump or termination are much harder to check,
     780and at best requiring additional run-time overhead, and so are only
    828781discouraged.
    829782
    830 Not all languages with unwinding have finally clauses. Notably, \Cpp does
     783Not all languages with unwinding have finally clauses. Notably \Cpp does
    831784without it as destructors, and the RAII design pattern, serve a similar role.
    832785Although destructors and finally clauses can be used for the same cases,
    833786they have their own strengths, similar to top-level function and lambda
    834787functions with closures.
    835 Destructors take more work to create, but if there is clean-up code
     788Destructors take more work for their creation, but if there is clean-up code
    836789that needs to be run every time a type is used, they are much easier
    837 to set up for each use. % It's automatic.
    838 On the other hand, finally clauses capture the local context, so are easy to
    839 use when the cleanup is not dependent on the type of a variable or requires
     790to set-up.
     791On the other hand finally clauses capture the local context, so is easy to
     792use when the clean-up is not dependent on the type of a variable or requires
    840793information from multiple variables.
    841794
     
    844797Cancellation is a stack-level abort, which can be thought of as as an
    845798uncatchable termination. It unwinds the entire current stack, and if
    846 possible, forwards the cancellation exception to a different stack.
     799possible forwards the cancellation exception to a different stack.
    847800
    848801Cancellation is not an exception operation like termination or resumption.
    849802There is no special statement for starting a cancellation; instead the standard
    850 library function @cancel_stack@ is called, passing an exception. Unlike a
    851 raise, this exception is not used in matching, only to pass information about
     803library function @cancel_stack@ is called passing an exception. Unlike a
     804raise, this exception is not used in matching only to pass information about
    852805the cause of the cancellation.
    853 Finally, as no handler is provided, there is no default handler.
    854 
    855 After @cancel_stack@ is called, the exception is copied into the EHM's memory
     806Finaly, since a cancellation only unwinds and forwards, there is no default handler.
     807
     808After @cancel_stack@ is called the exception is copied into the EHM's memory
    856809and the current stack is unwound.
    857810The behaviour after that depends on the kind of stack being cancelled.
    858811
    859812\paragraph{Main Stack}
    860 The main stack is the one used by
    861 the program's main function at the start of execution,
     813The main stack is the one used by the program main at the start of execution,
    862814and is the only stack in a sequential program.
    863 After the main stack is unwound, there is a program-level abort.
    864 
    865 The first reason for this behaviour is for sequential programs where there
    866 is only one stack, and hence no stack to pass information to.
    867 Second, even in concurrent programs, the main stack has no dependency
    868 on another stack and no reliable way to find another living stack.
    869 Finally, keeping the same behaviour in both sequential and concurrent
    870 programs is simple and easy to understand.
     815After the main stack is unwound there is a program-level abort.
     816
     817The reasons for this semantics in a sequential program is that there is no more code to execute.
     818This semantics also applies to concurrent programs, too, even if threads are running.
     819That is, if any threads starts a cancellation, it implies all threads terminate.
     820Keeping the same behaviour in sequential and concurrent programs is simple.
     821Also, even in concurrent programs there may not currently be any other stacks
     822and even if other stacks do exist, main has no way to know where they are.
    871823
    872824\paragraph{Thread Stack}
     
    880832and an implicit join (from a destructor call). The explicit join takes the
    881833default handler (@defaultResumptionHandler@) from its calling context while
    882 the implicit join provides its own, which does a program abort if the
     834the implicit join provides its own; which does a program abort if the
    883835@ThreadCancelled@ exception cannot be handled.
    884836
     
    898850
    899851With explicit join and a default handler that triggers a cancellation, it is
    900 possible to cascade an error across any number of threads,
    901 alternating between the resumption (possibly termination) and cancellation,
    902 cleaning up each
     852possible to cascade an error across any number of threads, cleaning up each
    903853in turn, until the error is handled or the main thread is reached.
    904854
     
    908858After a coroutine stack is unwound, control returns to the @resume@ function
    909859that most recently resumed it. @resume@ reports a
    910 @CoroutineCancelled@ exception, which contains a reference to the cancelled
     860@CoroutineCancelled@ exception, which contains a references to the cancelled
    911861coroutine and the exception used to cancel it.
    912862The @resume@ function also takes the \defaultResumptionHandler{} from the
    913863caller's context and passes it to the internal report.
    914864
    915 A coroutine only knows of two other coroutines,
    916 its starter and its last resumer.
     865A coroutine only knows of two other coroutines, its starter and its last resumer.
    917866The starter has a much more distant connection, while the last resumer just
    918867(in terms of coroutine state) called resume on this coroutine, so the message
     
    920869
    921870With a default handler that triggers a cancellation, it is possible to
    922 cascade an error across any number of coroutines,
    923 alternating between the resumption (possibly termination) and cancellation,
    924 cleaning up each in turn,
     871cascade an error across any number of coroutines, cleaning up each in turn,
    925872until the error is handled or a thread stack is reached.
     873
     874\PAB{Part of this I do not understand. A cancellation cannot be caught. But you
     875talk about handling a cancellation in the last sentence. Which is correct?}
  • doc/theses/andrew_beach_MMath/future.tex

    rb7fd9daf rf95634e  
    22\label{c:future}
    33
    4 The following discussion covers both possible interesting research
    5 that could follow from this work as well as simple implementation
    6 improvements.
    7 
    84\section{Language Improvements}
    9 
     5\todo{Future/Language Improvements seems to have gotten mixed up. It is
     6presented as ``waiting on language improvements" but really its more
     7non-research based impovements.}
    108\CFA is a developing programming language. As such, there are partially or
    11 unimplemented features (including several broken components)
    12 that I had to work around while building the EHM largely in
    13 the \CFA language (some C components). Below are a few of these issues
    14 and how implementing/fixing them would affect the EHM.
    15 In addition, there are some simple improvements that had no interesting
    16 research attached to them but would make using the language easier.
     9unimplemented features of the language (including several broken components)
     10that I had to workaround while building an exception handling system largely in
     11the \CFA language (some C components).  The following are a few of these
     12issues, and once implemented/fixed, how they would affect the exception system.
    1713\begin{itemize}
     14\item
     15The implementation of termination is not portable because it includes
     16hand-crafted assembly statements.
     17The existing compilers cannot translate that for other platforms and those
     18sections must be ported by hand to
     19support more hardware architectures, such as the ARM processor.
    1820\item
    1921Due to a type-system problem, the catch clause cannot bind the exception to a
     
    2224result in little or no change in the exception system but simplify usage.
    2325\item
    24 The @copy@ function in the exception virtual table is an adapter to address
    25 some limitations in the \CFA copy constructor. If the copy constructor is
    26 improved it can be used directly without the adapter.
    27 \item
    2826Termination handlers cannot use local control-flow transfers, \eg by @break@,
    2927@return@, \etc. The reason is that current code generation hoists a handler
    30 into a nested function for convenience (versus assembly-code generation at the
    31 try statement). Hence, when the handler runs, it can still access local
    32 variables in the lexical scope of the try statement. Still, it does mean
    33 that seemingly local control flow is not in fact local and crosses a function
    34 boundary.
    35 Making the termination handler's code within the surrounding
    36 function would remove this limitation.
    37 % Try blocks are much more difficult to do practically (requires our own
    38 % assembly) and resumption handlers have some theoretical complexity.
     28into a nested function for convenience (versus assemble-code generation at the
     29@try@ statement). Hence, when the handler runs, its code is not in the lexical
     30scope of the @try@ statement, where the local control-flow transfers are
     31meaningful.
    3932\item
    40 There is no detection of colliding unwinds. It is possible for cleanup code
    41 run during an unwind to trigger another unwind that escapes the cleanup code
    42 itself, such as a termination exception caught further down the stack or a
    43 cancellation. There do exist ways to handle this case, but currently there is
    44 no detection and the first unwind will simply be forgotten, often leaving
     33There is no detection of colliding unwinds. It is possible for clean-up code
     34run during an unwind to trigger another unwind that escapes the clean-up code
     35itself; such as a termination exception caught further down the stack or a
     36cancellation. There do exist ways to handle this but currently they are not
     37even detected and the first unwind will simply be forgotten, often leaving
    4538it in a bad state.
    4639\item
    47 Finally, the exception system has not had a lot of programmer testing.
    48 More time with encouraged usage will reveal new
    49 quality of life upgrades that can be made.
     40Also the exception system did not have a lot of time to be tried and tested.
     41So just letting people use the exception system more will reveal new
     42quality of life upgrades that can be made with time.
    5043\end{itemize}
    5144
     
    5447project, but was thrust upon it to do exception inheritance; hence, only
    5548minimal work is done. A draft for a complete virtual system is available but
    56 not finalized. A future \CFA project is to complete that work and then
     49it is not finalized. A future \CFA project is to complete that work and then
    5750update the exception system that uses the current version.
    5851
     
    6053exception traits. The most important one is an assertion to check one virtual
    6154type is a child of another. This check precisely captures many of the
    62 current ad-hoc correctness requirements.
    63 
    64 Other features of the virtual system could also remove some of the
    65 special cases around exception virtual tables, such as the generation
    66 of the @msg@ function.
     55correctness requirements.
    6756
    6857The full virtual system might also include other improvement like associated
    6958types to allow traits to refer to types not listed in their header. This
    7059feature allows exception traits to not refer to the virtual-table type
    71 explicitly, removing the need for the current interface macros,
    72 such as @EHM_IS_EXCEPTION@.
     60explicitly, removing the need for the current interface macros.
    7361
    7462\section{Additional Raises}
    7563Several other kinds of exception raises were considered beyond termination
    76 (@throw@), resumption (@throwResume@), and re-raise.
     64(@throw@), resumption (@throwResume@), and reraise.
    7765
    7866The first is a non-local/concurrent raise providing asynchronous exceptions,
     
    8674Non-local/concurrent raise requires more
    8775coordination between the concurrency system
    88 and the exception system. Many of the interesting design decisions center
     76and the exception system. Many of the interesting design decisions centre
    8977around masking, \ie controlling which exceptions may be thrown at a stack. It
    9078would likely require more of the virtual system and would also effect how
     
    10593Checked exceptions make exceptions part of a function's type by adding an
    10694exception signature. An exception signature must declare all checked
    107 exceptions that could propagate from the function, either because they were
    108 raised inside the function or came from a sub-function. This improves safety
     95exceptions that could propagate from the function (either because they were
     96raised inside the function or came from a sub-function). This improves safety
    10997by making sure every checked exception is either handled or consciously
    11098passed on.
    11199
    112 Checked exceptions were never seriously considered for this project
    113 because they have significant trade-offs in usability and code reuse in
     100However checked exceptions were never seriously considered for this project
     101because they have significant trade-offs in usablity and code reuse in
    114102exchange for the increased safety.
    115103These trade-offs are most problematic when trying to pass exceptions through
    116104higher-order functions from the functions the user passed into the
    117105higher-order function. There are no well known solutions to this problem
    118 that were satisfactory for \CFA (which carries some of C's
    119 flexibility-over-safety design) so additional research is needed.
     106that were satisfactory for \CFA (which carries some of C's flexibility
     107over safety design) so additional research is needed.
    120108
    121109Follow-up work might add some form of checked exceptions to \CFA,
     
    140128Zero-cost resumptions is still an open problem. First, because libunwind does
    141129not support a successful-exiting stack-search without doing an unwind.
    142 Workarounds are possible but awkward. Ideally, an extension to libunwind could
    143 be made, but that would either require separate maintenance or gaining enough
    144 support to have it folded into the official library itself.
     130Workarounds are possible but awkward. Ideally an extension to libunwind could
     131be made, but that would either require separate maintenance or gain enough
     132support to have it folded into the standard.
    145133
    146 Also, new techniques to skip previously searched parts of the stack need to be
     134Also new techniques to skip previously searched parts of the stack need to be
    147135developed to handle the recursive resume problem and support advanced algebraic
    148136effects.
     
    170158to leave the handler.
    171159Currently, mimicking this behaviour in \CFA is possible by throwing a
    172 termination exception inside a resumption handler.
     160termination inside a resumption handler.
    173161
    174162% Maybe talk about the escape; and escape CONTROL_STMT; statements or how
  • doc/theses/andrew_beach_MMath/implement.tex

    rb7fd9daf rf95634e  
    1414\label{s:VirtualSystem}
    1515% Virtual table rules. Virtual tables, the pointer to them and the cast.
    16 While the \CFA virtual system currently has only two public features, virtual
    17 cast and virtual tables,
    18 substantial structure is required to support them,
     16While the \CFA virtual system currently has only one public feature, virtual
     17cast (see the virtual cast feature \vpageref{p:VirtualCast}),
     18substantial structure is required to support it,
    1919and provide features for exception handling and the standard library.
    2020
    2121\subsection{Virtual Type}
    22 A virtual type~(see \autoref{s:virtuals}) has a pointer to a virtual table,
    23 called the \emph{virtual-table pointer},
    24 which binds each instance of a virtual type to a virtual table.
    25 Internally, the field is called \snake{virtual_table}
    26 and is fixed after construction.
    27 This pointer is also the table's id and how the system accesses the
     22Virtual types only have one change to their structure: the addition of a
     23pointer to the virtual table, which is called the \emph{virtual-table pointer}.
     24Internally, the field is called \snake{virtual_table}.
     25The field is fixed after construction. It is always the first field in the
     26structure so that its location is always known.
     27\todo{Talk about constructors for virtual types (after they are working).}
     28
     29The virtual table pointer binds an instance of a virtual type
     30to a virtual table.
     31The pointer is also the table's id and how the system accesses the
    2832virtual table and the virtual members there.
    29 It is always the first field in the
    30 structure so that its location is always known.
    31 
    32 % We have no special rules for these constructors.
    33 Virtual table pointers are passed to the constructors of virtual types
    34 as part of field-by-field construction.
    35 
    36 \subsection{Type ID}
    37 Every virtual type has a unique ID.
    38 These are used in type equality, to check if the representation of two values
    39 are the same, and to access the type's type information.
    40 This uniqueness means across a program composed of multiple translation
    41 units (TU), not uniqueness across all programs or even across multiple
    42 processes on the same machine.
    43 
    44 Our approach for program uniqueness is using a static declaration for each
    45 type ID, where the run-time storage address of that variable is guaranteed to
    46 be unique during program execution.
    47 The type ID storage can also be used for other purposes,
    48 and is used for type information.
    49 
    50 The problem is that a type ID may appear in multiple TUs that compose a
    51 program (see \autoref{ss:VirtualTable}), so the initial solution would seem
    52 to be make it external in each translation unit. However, the type ID must
    53 have a declaration in (exactly) one of the TUs to create the storage.
    54 No other declaration related to the virtual type has this property, so doing
    55 this through standard C declarations would require the user to do it manually.
    56 
    57 Instead, the linker is used to handle this problem.
    58 % I did not base anything off of C++17; they are solving the same problem.
    59 A new feature has been added to \CFA for this purpose, the special attribute
    60 \snake{cfa_linkonce}, which uses the special section @.gnu.linkonce@.
    61 When used as a prefix (\eg @.gnu.linkonce.example@), the linker does
    62 not combine these sections, but instead discards all but one with the same
    63 full name.
    64 
    65 So, each type ID must be given a unique section name with the \snake{linkonce}
    66 prefix. Luckily, \CFA already has a way to get unique names, the name mangler.
    67 For example, this could be written directly in \CFA:
    68 \begin{cfa}
    69 __attribute__((cfa_linkonce)) void f() {}
    70 \end{cfa}
    71 This is translated to:
    72 \begin{cfa}
    73 __attribute__((section(".gnu.linkonce._X1fFv___1"))) void _X1fFv___1() {}
    74 \end{cfa}
    75 This is done internally to access the name mangler.
    76 This attribute is useful for other purposes, any other place a unique
    77 instance required, and should eventually be made part of a public and
    78 stable feature in \CFA.
    79 
    80 \subsection{Type Information}
    81 
    82 There is data stored at the type ID's declaration, the type information.
    83 The type information currently is only the parent's type ID or, if the
     33
     34\subsection{Type Id}
     35Every virtual type has a unique id.
     36Type ids can be compared for equality,
     37which checks if the types reperented are the same,
     38or used to access the type's type information.
     39The type information currently is only the parent's type id or, if the
    8440type has no parent, the null pointer.
    85 The ancestors of a virtual type are found by traversing type IDs through
     41
     42The id's are implemented as pointers to the type's type information instance.
     43Dereferencing the pointer gets the type information.
     44The ancestors of a virtual type are found by traversing type ids through
    8645the type information.
    87 An example using helper macros looks like:
     46The information pushes the issue of creating a unique value (for
     47the type id) to the problem of creating a unique instance (for type
     48information), which the linker can solve.
     49
     50The advanced linker support is used here to avoid having to create
     51a new declaration to attach this data to.
     52With C/\CFA's header/implementation file divide for something to appear
     53exactly once it must come from a declaration that appears in exactly one
     54implementation file; the declarations in header files may exist only once
     55they can be included in many different translation units.
     56Therefore, structure's declaration will not work.
     57Neither will attaching the type information to the virtual table -- although
     58a vtable declarations are in implemention files they are not unique, see
     59\autoref{ss:VirtualTable}.
     60Instead the same type information is generated multiple times and then
     61the new attribute \snake{cfa_linkone} is used to removed duplicates.
     62
     63Type information is constructed as follows:
     64\begin{enumerate}
     65\item
     66Use the type's name to generate a name for the type information structure.
     67This is saved so it may be reused.
     68\item
     69Generate a new structure definition to store the type
     70information. The layout is the same in each case, just the parent's type id,
     71but the types used change from instance to instance.
     72The generated name is used for both this structure and, if relivant, the
     73parent pointer.
     74If the virtual type is polymorphic then the type information structure is
     75polymorphic as well, with the same polymorphic arguments.
     76\item
     77A seperate name for instances is generated from the type's name.
     78\item
     79The definition is generated and initialised.
     80The parent id is set to the null pointer or to the address of the parent's
     81type information instance. Name resolution handles the rest.
     82\item
     83\CFA's name mangler does its regular name mangling encoding the type of
     84the declaration into the instance name. This gives a completely unique name
     85including different instances of the same polymorphic type.
     86\end{enumerate}
     87\todo{The list is making me realise, some of this isn't ordered.}
     88
     89Writing that code manually, with helper macros for the early name mangling,
     90would look like this:
    8891\begin{cfa}
    8992struct INFO_TYPE(TYPE) {
     
    97100\end{cfa}
    98101
    99 Type information is constructed as follows:
    100 \begin{enumerate}[nosep]
    101 \item
    102 Use the type's name to generate a name for the type information structure,
    103 which is saved so it can be reused.
    104 \item
    105 Generate a new structure definition to store the type
    106 information. The layout is the same in each case, just the parent's type ID,
    107 but the types used change from instance to instance.
    108 The generated name is used for both this structure and, if relevant, the
    109 parent pointer.
    110 If the virtual type is polymorphic then the type information structure is
    111 polymorphic as well, with the same polymorphic arguments.
    112 \item
    113 A separate name for instances is generated from the type's name.
    114 \item
    115 The definition is generated and initialized.
    116 The parent ID is set to the null pointer or to the address of the parent's
    117 type information instance. Name resolution handles the rest.
    118 \item
    119 \CFA's name mangler does its regular name mangling encoding the type of
    120 the declaration into the instance name.
    121 This process gives a completely unique name
    122 including different instances of the same polymorphic type.
    123 \end{enumerate}
    124 
    125 Writing that code manually, with helper macros for the early name mangling,
    126 would look like this:
    127 \begin{cfa}
    128 struct INFO_TYPE(TYPE) {
    129         INFO_TYPE(PARENT) const * parent;
    130 };
    131 
    132 __attribute__((cfa_linkonce))
    133 INFO_TYPE(TYPE) const INFO_NAME(TYPE) = {
    134         &INFO_NAME(PARENT),
    135 };
    136 \end{cfa}
    137 
    138 \begin{comment}
    139102\subsubsection{\lstinline{cfa\_linkonce} Attribute}
    140 % I just realized: This is an extension of the inline keyword.
     103% I just realised: This is an extension of the inline keyword.
    141104% An extension of C's at least, it is very similar to C++'s.
    142105Another feature added to \CFA is a new attribute: \texttt{cfa\_linkonce}.
     
    150113file as if it was a forward declaration, except no definition is required.
    151114
    152 This technique is used for type ID instances. A link-once definition is
     115This technique is used for type-id instances. A link-once definition is
    153116generated each time the structure is seen. This will result in multiple
    154117copies but the link-once attribute ensures all but one are removed for a
     
    163126everything that comes after the special prefix, then only one is used
    164127and the other is discarded.
    165 \end{comment}
    166128
    167129\subsection{Virtual Table}
    168130\label{ss:VirtualTable}
    169 Each virtual type has a virtual table type that stores its type ID and
     131Each virtual type has a virtual table type that stores its type id and
    170132virtual members.
    171 An instance of a virtual type is bound to a virtual table instance,
    172 which have the values of the virtual members.
    173 Both the layout of the fields (in the virtual table type)
    174 and their value (in the virtual table instance) are decided by the rules given
     133Each virtual type instance is bound to a table instance that is filled with
     134the values of virtual members.
     135Both the layout of the fields and their value are decided by the rules given
    175136below.
    176137
    177 The layout always comes in three parts (see \autoref{f:VirtualTableLayout}).
    178 The first section is just the type ID at the head of the table. It is always
     138The layout always comes in three parts.
     139\todo{Add labels to the virtual table layout figure.}
     140The first section is just the type id at the head of the table. It is always
    179141there to ensure that it can be found even when the accessing code does not
    180142know which virtual type it has.
    181 The second section is all the virtual members of the parent, in the same
     143The second section are all the virtual members of the parent, in the same
    182144order as they appear in the parent's virtual table. Note that the type may
    183 change slightly as references to the ``this" change. This is limited to
     145change slightly as references to the ``this" will change. This is limited to
    184146inside pointers/references and via function pointers so that the size (and
    185147hence the offsets) are the same.
     
    188150
    189151\begin{figure}
    190 \begin{center}
    191152\input{vtable-layout}
    192 \end{center}
    193153\caption{Virtual Table Layout}
    194154\label{f:VirtualTableLayout}
     155\todo*{Improve the Virtual Table Layout diagram.}
    195156\end{figure}
    196157
     
    199160This, combined with the fixed offset to the virtual table pointer, means that
    200161for any virtual type, it is always safe to access its virtual table and,
    201 from there, it is safe to check the type ID to identify the exact type of the
     162from there, it is safe to check the type id to identify the exact type of the
    202163underlying object, access any of the virtual members and pass the object to
    203164any of the method-like virtual members.
     
    207168the context of the declaration.
    208169
    209 The type ID is always fixed, with each virtual table type having
    210 exactly one possible type ID.
     170The type id is always fixed; with each virtual table type having
     171exactly one possible type id.
    211172The virtual members are usually filled in by type resolution.
    212173The best match for a given name and type at the declaration site is used.
    213174There are two exceptions to that rule: the @size@ field, the type's size,
    214 is set using a @sizeof@ expression, and the @align@ field, the
     175is set using a @sizeof@ expression and the @align@ field, the
    215176type's alignment, is set using an @alignof@ expression.
    216177
    217 Most of these tools are already inside the compiler. Using simple
    218 code transformations early on in compilation allows most of that work to be
    219 handed off to the existing tools. \autoref{f:VirtualTableTransformation}
    220 shows an example transformation; this example shows an exception virtual table.
    221 It also shows the transformation on the full declaration.
    222 For a forward declaration, the @extern@ keyword is preserved and the
    223 initializer is not added.
    224 
    225 \begin{figure}[htb]
    226 \begin{cfa}
    227 vtable(example_type) example_name;
    228 \end{cfa}
    229 \transformline
    230 % Check mangling.
    231 \begin{cfa}
    232 const struct example_type_vtable example_name = {
    233         .__cfavir_typeid : &__cfatid_example_type,
    234         .size : sizeof(example_type),
    235         .copy : copy,
    236         .^?{} : ^?{},
    237         .msg : msg,
    238 };
    239 \end{cfa}
    240 \caption{Virtual Table Transformation}
    241 \label{f:VirtualTableTransformation}
    242 \end{figure}
    243 
    244 \subsection{Concurrency Integration}
     178\subsubsection{Concurrency Integration}
    245179Coroutines and threads need instances of @CoroutineCancelled@ and
    246180@ThreadCancelled@ respectively to use all of their functionality. When a new
     
    249183at the definition of the main function.
    250184
    251 These transformations are shown through code re-writing in
    252 \autoref{f:CoroutineTypeTransformation} and
    253 \autoref{f:CoroutineMainTransformation}.
    254 Threads use the same pattern, with some names and types changed.
    255 In both cases, the original declaration is not modified,
     185This is showned through code re-writing in
     186\autoref{f:ConcurrencyTypeTransformation} and
     187\autoref{f:ConcurrencyMainTransformation}.
     188In both cases the original declaration is not modified,
    256189only new ones are added.
    257190
    258 \begin{figure}[htb]
     191\begin{figure}
    259192\begin{cfa}
    260193coroutine Example {
     
    274207extern CoroutineCancelled_vtable & _default_vtable;
    275208\end{cfa}
    276 \caption{Coroutine Type Transformation}
    277 \label{f:CoroutineTypeTransformation}
     209\caption{Concurrency Type Transformation}
     210\label{f:ConcurrencyTypeTransformation}
    278211\end{figure}
    279212
    280 \begin{figure}[htb]
     213\begin{figure}
    281214\begin{cfa}
    282215void main(Example & this) {
     
    296229        &_default_vtable_object_declaration;
    297230\end{cfa}
    298 \caption{Coroutine Main Transformation}
    299 \label{f:CoroutineMainTransformation}
     231\caption{Concurrency Main Transformation}
     232\label{f:ConcurrencyMainTransformation}
    300233\end{figure}
    301234
     
    309242\begin{cfa}
    310243void * __cfa__virtual_cast(
    311         struct __cfavir_type_id * parent,
    312         struct __cfavir_type_id * const * child );
    313 \end{cfa}
    314 The type ID for the target type of the virtual cast is passed in as
    315 @parent@ and
     244        struct __cfavir_type_td parent,
     245        struct __cfavir_type_id const * child );
     246\end{cfa}
     247The type id of target type of the virtual cast is passed in as @parent@ and
    316248the cast target is passed in as @child@.
    317 The generated C code wraps both arguments and the result with type casts.
     249
     250For generated C code wraps both arguments and the result with type casts.
    318251There is also an internal check inside the compiler to make sure that the
    319252target type is a virtual type.
     
    322255The virtual cast either returns the original pointer or the null pointer
    323256as the new type.
    324 The function does the parent check and returns the appropriate value.
    325 The parent check is a simple linear search of the child's ancestors using the
     257So the function does the parent check and returns the appropriate value.
     258The parent check is a simple linear search of child's ancestors using the
    326259type information.
    327260
    328261\section{Exceptions}
    329 % The implementation of exception types.
    330 
    331 Creating exceptions can be roughly divided into two parts:
    332 the exceptions themselves and the virtual system interactions.
    333 
    334 Creating an exception type is just a matter of prepending the field 
    335 with the virtual table pointer to the list of the fields
    336 (see \autoref{f:ExceptionTypeTransformation}).
    337 
    338 \begin{figure}[htb]
    339 \begin{cfa}
    340 exception new_exception {
    341         // EXISTING FIELDS
    342 };
    343 \end{cfa}
    344 \transformline
    345 \begin{cfa}
    346 struct new_exception {
    347         struct new_exception_vtable const * virtual_table;
    348         // EXISTING FIELDS
    349 };
    350 \end{cfa}
    351 \caption{Exception Type Transformation}
    352 \label{f:ExceptionTypeTransformation}
    353 \end{figure}
    354 
    355 The integration between exceptions and the virtual system is a bit more
    356 complex simply because of the nature of the virtual system prototype.
    357 The primary issue is that the virtual system has no way to detect when it
    358 should generate any of its internal types and data. This is handled by
    359 the exception code, which tells the virtual system when to generate
    360 its components.
    361 
    362 All types associated with a virtual type,
    363 the types of the virtual table and the type ID,
    364 are generated when the virtual type (the exception) is first found.
    365 The type ID (the instance) is generated with the exception, if it is
    366 a monomorphic type.
    367 However, if the exception is polymorphic, then a different type ID has to
    368 be generated for every instance. In this case, generation is delayed
    369 until a virtual table is created.
    370 % There are actually some problems with this, which is why it is not used
    371 % for monomorphic types.
    372 When a virtual table is created and initialized, two functions are created
    373 to fill in the list of virtual members.
    374 The first is the @copy@ function that adapts the exception's copy constructor
    375 to work with pointers, avoiding some issues with the current copy constructor
    376 interface.
    377 Second is the @msg@ function that returns a C-string with the type's name,
    378 including any polymorphic parameters.
     262% Anything about exception construction.
    379263
    380264\section{Unwinding}
     
    390274stack. On function entry and return, unwinding is handled directly by the
    391275call/return code embedded in the function.
    392 
    393 % Discussing normal stack unwinding:
     276In many cases, the position of the instruction pointer (relative to parameter
     277and local declarations) is enough to know the current size of the stack
     278frame.
     279
    394280Usually, the stack-frame size is known statically based on parameter and
    395 local variable declarations. Even for a dynamic stack-size, the information
     281local variable declarations. Even with dynamic stack-size, the information
    396282to determine how much of the stack has to be removed is still contained
    397283within the function.
     
    399285bumping the hardware stack-pointer up or down as needed.
    400286Constructing/destructing values within a stack frame has
    401 a similar complexity but larger constants.
    402 
    403 % Discussing multiple frame stack unwinding:
    404 Unwinding across multiple stack frames is more complex, because that
     287a similar complexity but can add additional work and take longer.
     288
     289Unwinding across multiple stack frames is more complex because that
    405290information is no longer contained within the current function.
    406 With separate compilation,
    407 a function does not know its callers nor their frame layout.
    408 Even using the return address, that information is encoded in terms of
    409 actions in code, intermixed with the actions required to finish the function.
    410 Without changing the main code path it is impossible to select one of those
    411 two groups of actions at the return site.
    412 
    413 The traditional unwinding mechanism for C is implemented by saving a snapshot
    414 of a function's state with @setjmp@ and restoring that snapshot with
     291With seperate compilation a function has no way of knowing what its callers
     292are so it can't know how large those frames are.
     293Without altering the main code path it is also hard to pass that work off
     294to the caller.
     295
     296The traditional unwinding mechanism for C is implemented by saving a snap-shot
     297of a function's state with @setjmp@ and restoring that snap-shot with
    415298@longjmp@. This approach bypasses the need to know stack details by simply
    416 resetting to a snapshot of an arbitrary but existing function frame on the
    417 stack. It is up to the programmer to ensure the snapshot is valid when it is
    418 reset and that all required cleanup from the unwound stacks is performed.
    419 Because it does not automate or check any of this cleanup,
    420 it can be easy to make mistakes and always must be handled manually.
    421 
    422 With respect to the extra work in the surrounding code,
    423 many languages define cleanup actions that must be taken when certain
    424 sections of the stack are removed, such as when the storage for a variable
    425 is removed from the stack, possibly requiring a destructor call,
    426 or when a try statement with a finally clause is
     299reseting to a snap-shot of an arbitrary but existing function frame on the
     300stack. It is up to the programmer to ensure the snap-shot is valid when it is
     301reset and that all required clean-up from the unwound stacks is performed.
     302This approach is fragile and requires extra work in the surrounding code.
     303
     304With respect to the extra work in the surounding code,
     305many languages define clean-up actions that must be taken when certain
     306sections of the stack are removed. Such as when the storage for a variable
     307is removed from the stack or when a try statement with a finally clause is
    427308(conceptually) popped from the stack.
    428 None of these cases should be handled by the user -- that would contradict the
    429 intention of these features -- so they need to be handled automatically.
     309None of these should be handled by the user --- that would contradict the
     310intention of these features --- so they need to be handled automatically.
    430311
    431312To safely remove sections of the stack, the language must be able to find and
    432 run these cleanup actions even when removing multiple functions unknown at
     313run these clean-up actions even when removing multiple functions unknown at
    433314the beginning of the unwinding.
    434315
     
    436317library that provides tools for stack walking, handler execution, and
    437318unwinding. What follows is an overview of all the relevant features of
    438 libunwind needed for this work.
    439 Following that is the description of the \CFA code that uses libunwind
    440 to implement termination.
     319libunwind needed for this work, and how \CFA uses them to implement exception
     320handling.
    441321
    442322\subsection{libunwind Usage}
     
    468348In plain C (which \CFA currently compiles down to) this
    469349flag only handles the cleanup attribute:
    470 %\label{code:cleanup}
    471350\begin{cfa}
    472351void clean_up( int * var ) { ... }
     
    476355in this case @clean_up@, run when the variable goes out of scope.
    477356This feature is enough to mimic destructors,
    478 but not try statements that affect
     357but not try statements which can effect
    479358the unwinding.
    480359
    481360To get full unwinding support, all of these features must be handled directly
    482 in assembly and assembler directives; particularly the cfi directives
     361in assembly and assembler directives; partiularly the cfi directives
    483362\snake{.cfi_lsda} and \snake{.cfi_personality}.
    484363
     
    520399@_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs
    521400the cleanup phase and uses a different means to decide when to stop
    522 (see \autoref{s:ForcedUnwind}).
     401(see \vref{s:ForcedUnwind}).
    523402\end{enumerate}
    524403
    525404The @exception_class@ argument is a copy of the
    526405\code{C}{exception}'s @exception_class@ field,
    527 which is a number that identifies the EHM
     406which is a number that identifies the exception handling mechanism
    528407that created the exception.
    529408
     
    531410provided storage object. It has two public fields: the @exception_class@,
    532411which is described above, and the @exception_cleanup@ function.
    533 The cleanup function is used by the EHM to clean up the exception. If it
     412The clean-up function is used by the EHM to clean-up the exception, if it
    534413should need to be freed at an unusual time, it takes an argument that says
    535414why it had to be cleaned up.
     
    553432of the most recent stack frame. It continues to call personality functions
    554433traversing the stack from newest to oldest until a function finds a handler or
    555 the end of the stack is reached. In the latter case,
    556 @_Unwind_RaiseException@ returns @_URC_END_OF_STACK@.
    557 
    558 Second, when a handler is matched, @_Unwind_RaiseException@
    559 moves to the cleanup phase and walks the stack a second time.
     434the end of the stack is reached. In the latter case, raise exception returns
     435@_URC_END_OF_STACK@.
     436
     437Second, when a handler is matched, raise exception moves to the clean-up
     438phase and walks the stack a second time.
    560439Once again, it calls the personality functions of each stack frame from newest
    561440to oldest. This pass stops at the stack frame containing the matching handler.
    562 If that personality function has not installed a handler, it is an error.
    563 
    564 If an error is encountered, @_Unwind_RaiseException@ returns either
     441If that personality function has not install a handler, it is an error.
     442
     443If an error is encountered, raise exception returns either
    565444@_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the
    566445error occurred.
     
    573452        _Unwind_Stop_Fn, void *);
    574453\end{cfa}
    575 It also unwinds the stack but it does not use the search phase. Instead,
    576 another
     454It also unwinds the stack but it does not use the search phase. Instead another
    577455function, the stop function, is used to stop searching. The exception is the
    578 same as the one passed to @_Unwind_RaiseException@.
    579 The extra arguments are the stop
     456same as the one passed to raise exception. The extra arguments are the stop
    580457function and the stop parameter. The stop function has a similar interface as a
    581458personality function, except it is also passed the stop parameter.
     
    617494needs its own exception context.
    618495
    619 The current exception context should be retrieved by calling the function
     496The exception context should be retrieved by calling the function
    620497\snake{this_exception_context}.
    621498For sequential execution, this function is defined as
     
    642519The first step of a termination raise is to copy the exception into memory
    643520managed by the exception system. Currently, the system uses @malloc@, rather
    644 than reserved memory or the stack top. The EHM manages
     521than reserved memory or the stack top. The exception handling mechanism manages
    645522memory for the exception as well as memory for libunwind and the system's own
    646523per-exception storage.
     
    677554\newsavebox{\stackBox}
    678555\begin{lrbox}{\codeBox}
    679 \begin{cfa}
     556\begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
    680557unsigned num_exceptions = 0;
    681558void throws() {
     
    696573    throws();
    697574}
    698 \end{cfa}
     575\end{lstlisting}
    699576\end{lrbox}
    700577
    701578\begin{lrbox}{\stackBox}
    702579\begin{lstlisting}
    703 | finally block (Example)
    704 | try block
     580| try-finally
     581| try-catch (Example)
    705582throws()
    706 | finally block (Example)
    707 | try block
     583| try-finally
     584| try-catch (Example)
    708585throws()
    709 | finally block (Example)
    710 | try block
     586| try-finally
     587| try-catch (Example)
    711588throws()
    712589main()
     
    721598\label{f:MultipleExceptions}
    722599\end{figure}
     600\todo*{Work on multiple exceptions code sample.}
    723601
    724602All exceptions are stored in nodes, which are then linked together in lists
    725603one list per stack, with the
    726604list head stored in the exception context. Within each linked list, the most
    727 recently thrown exception is at the head, followed by older thrown
     605recently thrown exception is at the head followed by older thrown
    728606exceptions. This format allows exceptions to be thrown, while a different
    729607exception is being handled. The exception at the head of the list is currently
     
    736614exception into managed memory. After the exception is handled, the free
    737615function is used to clean up the exception and then the entire node is
    738 passed to @free@, returning the memory back to the heap.
     616passed to free, returning the memory back to the heap.
    739617
    740618\subsection{Try Statements and Catch Clauses}
    741619The try statement with termination handlers is complex because it must
    742 compensate for the C code-generation versus proper
     620compensate for the C code-generation versus
    743621assembly-code generated from \CFA. Libunwind
    744622requires an LSDA and personality function for control to unwind across a
    745623function. The LSDA in particular is hard to mimic in generated C code.
    746624
    747 The workaround is a function called \snake{__cfaehm_try_terminate} in the
    748 standard \CFA library. The contents of a try block and the termination
    749 handlers are converted into nested functions. These are then passed to the
    750 try terminate function and it calls them, appropriately.
     625The workaround is a function called @__cfaehm_try_terminate@ in the standard
     626library. The contents of a try block and the termination handlers are converted
     627into functions. These are then passed to the try terminate function and it
     628calls them.
    751629Because this function is known and fixed (and not an arbitrary function that
    752 happens to contain a try statement), its LSDA can be generated ahead
     630happens to contain a try statement), the LSDA can be generated ahead
    753631of time.
    754632
    755 Both the LSDA and the personality function for \snake{__cfaehm_try_terminate}
    756 are set ahead of time using
     633Both the LSDA and the personality function are set ahead of time using
    757634embedded assembly. This assembly code is handcrafted using C @asm@ statements
    758635and contains
    759 enough information for the single try statement the function represents.
     636enough information for a single try statement the function repersents.
    760637
    761638The three functions passed to try terminate are:
    762639\begin{description}
    763 \item[try function:] This function is the try block. It is where all the code
     640\item[try function:] This function is the try block, it is where all the code
    764641from inside the try block is placed. It takes no parameters and has no
    765642return value. This function is called during regular execution to run the try
     
    769646decides if a catch clause matches the termination exception. It is constructed
    770647from the conditional part of each handler and runs each check, top to bottom,
    771 in turn, to see if the exception matches this handler.
    772 The match is performed in two steps: first, a virtual cast is used to check
    773 if the raised exception is an instance of the declared exception type or
    774 one of its descendant types, and then the condition is evaluated, if
    775 present.
    776 The match function takes a pointer to the exception and returns 0 if the
    777 exception is not handled here. Otherwise, the return value is the ID of the
     648in turn, first checking to see if the exception type matches and then if the
     649condition is true. It takes a pointer to the exception and returns 0 if the
     650exception is not handled here. Otherwise the return value is the id of the
    778651handler that matches the exception.
    779652
     
    786659\end{description}
    787660All three functions are created with GCC nested functions. GCC nested functions
    788 can be used to create closures;
    789 in other words,
    790 functions that can refer to variables in their lexical scope even though
    791 those variables are part of a different function.
    792 This approach allows the functions to refer to all the
     661can be used to create closures,
     662in other words functions that can refer to the state of other
     663functions on the stack. This approach allows the functions to refer to all the
    793664variables in scope for the function containing the @try@ statement. These
    794665nested functions and all other functions besides @__cfaehm_try_terminate@ in
     
    798669
    799670\autoref{f:TerminationTransformation} shows the pattern used to transform
    800 a \CFA try statement with catch clauses into the appropriate C functions.
     671a \CFA try statement with catch clauses into the approprate C functions.
     672\todo{Explain the Termination Transformation figure.}
    801673
    802674\begin{figure}
     
    856728\caption{Termination Transformation}
    857729\label{f:TerminationTransformation}
     730\todo*{Improve (compress?) Termination Transformations.}
    858731\end{figure}
    859732
     
    865738Instead of storing the data in a special area using assembly,
    866739there is just a linked list of possible handlers for each stack,
    867 with each node on the list representing a try statement on the stack.
     740with each node on the list reperenting a try statement on the stack.
    868741
    869742The head of the list is stored in the exception context.
     
    871744to the head of the list.
    872745Instead of traversing the stack, resumption handling traverses the list.
    873 At each node, the EHM checks to see if the try statement the node represents
     746At each node, the EHM checks to see if the try statement the node repersents
    874747can handle the exception. If it can, then the exception is handled and
    875 the operation finishes; otherwise, the search continues to the next node.
     748the operation finishes, otherwise the search continues to the next node.
    876749If the search reaches the end of the list without finding a try statement
    877 with a handler clause
    878 that can handle the exception, the default handler is executed.
    879 If the default handler returns, control continues after the raise statement.
     750that can handle the exception, the default handler is executed and the
     751operation finishes.
    880752
    881753Each node has a handler function that does most of the work.
    882754The handler function is passed the raised exception and returns true
    883755if the exception is handled and false otherwise.
     756
    884757The handler function checks each of its internal handlers in order,
    885 top-to-bottom, until it finds a match. If a match is found that handler is
     758top-to-bottom, until it funds a match. If a match is found that handler is
    886759run, after which the function returns true, ignoring all remaining handlers.
    887760If no match is found the function returns false.
    888 The match is performed in two steps. First a virtual cast is used to see
    889 if the raised exception is an instance of the declared exception type or one
    890 of its descendant types, if so, then the second step is to see if the
    891 exception passes the custom predicate
    892 if one is defined.
    893 % You need to make sure the type is correct before running the predicate
    894 % because the predicate can depend on that.
     761The match is performed in two steps, first a virtual cast is used to see
     762if the thrown exception is an instance of the declared exception or one of
     763its descendant type, then check to see if passes the custom predicate if one
     764is defined. This ordering gives the type guarantee used in the predicate.
    895765
    896766\autoref{f:ResumptionTransformation} shows the pattern used to transform
    897 a \CFA try statement with catchResume clauses into the appropriate
    898 C functions.
     767a \CFA try statement with catch clauses into the approprate C functions.
     768\todo{Explain the Resumption Transformation figure.}
    899769
    900770\begin{figure}
     
    937807\caption{Resumption Transformation}
    938808\label{f:ResumptionTransformation}
     809\todo*{Improve (compress?) Resumption Transformations.}
    939810\end{figure}
    940811
    941812% Recursive Resumption Stuff:
    942813\autoref{f:ResumptionMarking} shows search skipping
    943 (see \autoref{s:ResumptionMarking}), which ignores parts of
     814(see \vpageref{s:ResumptionMarking}), which ignores parts of
    944815the stack
    945 already examined, and is accomplished by updating the front of the list as
    946 the search continues.
    947 Before the handler is called at a matching node, the head of the list
     816already examined, is accomplished by updating the front of the list as the
     817search continues. Before the handler at a node is called, the head of the list
    948818is updated to the next node of the current node. After the search is complete,
    949819successful or not, the head of the list is reset.
     
    952822been checked are not on the list while a handler is run. If a resumption is
    953823thrown during the handling of another resumption, the active handlers and all
    954 the other handlers checked up to this point are not checked again.
     824the other handler checked up to this point are not checked again.
    955825% No paragraph?
    956826This structure also supports new handlers added while the resumption is being
    957827handled. These are added to the front of the list, pointing back along the
    958 stack -- the first one points over all the checked handlers --
     828stack --- the first one points over all the checked handlers ---
    959829and the ordering is maintained.
    960830
    961831\begin{figure}
    962 \centering
    963832\input{resumption-marking}
    964833\caption{Resumption Marking}
    965834\label{f:ResumptionMarking}
     835\todo*{Label Resumption Marking to aid clarity.}
    966836\end{figure}
    967837
     
    981851\section{Finally}
    982852% Uses destructors and GCC nested functions.
    983 
    984 %\autoref{code:cleanup}
    985 A finally clause is handled by converting it into a once-off destructor.
    986 The code inside the clause is placed into a GCC nested-function
    987 with a unique name, and no arguments or return values.
    988 This nested function is
    989 then set as the cleanup function of an empty object that is declared at the
    990 beginning of a block placed around the context of the associated try
    991 statement, as shown in \autoref{f:FinallyTransformation}.
    992 
    993 \begin{figure}
    994 \begin{cfa}
    995 try {
    996         // TRY BLOCK
    997 } finally {
    998         // FINALLY BLOCK
    999 }
    1000 \end{cfa}
    1001 
    1002 \transformline
    1003 
    1004 \begin{cfa}
    1005 {
    1006         void finally(void *__hook){
    1007                 // FINALLY BLOCK
    1008         }
    1009         __attribute__ ((cleanup(finally)))
    1010         struct __cfaehm_cleanup_hook __finally_hook;
    1011         {
    1012                 // TRY BLOCK
    1013         }
    1014 }
    1015 \end{cfa}
    1016 
    1017 \caption{Finally Transformation}
    1018 \label{f:FinallyTransformation}
    1019 \end{figure}
    1020 
    1021 The rest is handled by GCC.
    1022 The TRY BLOCK
    1023 contains the try block itself as well as all code generated for handlers.
    1024 Once that code has completed,
    1025 control exits the block and the empty object is cleaned
     853A finally clause is placed into a GCC nested-function with a unique name,
     854and no arguments or return values.
     855This nested function is then set as the cleanup
     856function of an empty object that is declared at the beginning of a block placed
     857around the context of the associated @try@ statement.
     858
     859The rest is handled by GCC. The try block and all handlers are inside this
     860block. At completion, control exits the block and the empty object is cleaned
    1026861up, which runs the function that contains the finally code.
    1027862
     
    1029864% Stack selections, the three internal unwind functions.
    1030865
    1031 Cancellation also uses libunwind to do its stack traversal and unwinding.
    1032 However, it uses a different primary function: @_Unwind_ForcedUnwind@. Details
    1033 of its interface can be found in Section~\vref{s:ForcedUnwind}.
     866Cancellation also uses libunwind to do its stack traversal and unwinding,
     867however it uses a different primary function: @_Unwind_ForcedUnwind@. Details
     868of its interface can be found in the Section~\vref{s:ForcedUnwind}.
    1034869
    1035870The first step of cancellation is to find the cancelled stack and its type:
     
    1052887passed to the forced-unwind function. The general pattern of all three stop
    1053888functions is the same: continue unwinding until the end of stack and
    1054 then perform the appropriate transfer.
     889then preform the appropriate transfer.
    1055890
    1056891For main stack cancellation, the transfer is just a program abort.
  • doc/theses/andrew_beach_MMath/intro.tex

    rb7fd9daf rf95634e  
    1111
    1212% Now take a step back and explain what exceptions are generally.
     13A language's EHM is a combination of language syntax and run-time
     14components that are used to construct, raise, and handle exceptions,
     15including all control flow.
     16Exceptions are an active mechanism for replacing passive error/return codes and return unions (Go and Rust).
    1317Exception handling provides dynamic inter-function control flow.
    14 A language's EHM is a combination of language syntax and run-time
    15 components that construct, raise, propagate and handle exceptions,
    16 to provide all of that control flow.
    1718There are two forms of exception handling covered in this thesis:
    1819termination, which acts as a multi-level return,
    1920and resumption, which is a dynamic function call.
    20 % About other works:
    21 Often, when this separation is not made, termination exceptions are assumed
    22 as they are more common and may be the only form of handling provided in
    23 a language.
    24 
    25 All types of exception handling link a raise with a handler.
    26 Both operations are usually language primitives, although raises can be
    27 treated as a function that takes an exception argument.
    28 Handlers are more complex, as they are added to and removed from the stack
    29 during execution, must specify what they can handle and must give the code to
    30 handle the exception.
    31 
    32 Exceptions work with different execution models but for the descriptions
    33 that follow a simple call stack, with functions added and removed in a
    34 first-in-last-out order, is assumed.
    35 
    36 Termination exception handling searches the stack for the handler, then
    37 unwinds the stack to where the handler was found before calling it.
    38 The handler is run inside the function that defined it and when it finishes
    39 it returns control to that function.
     21% PAB: Maybe this sentence was suppose to be deleted?
     22Termination handling is much more common,
     23to the extent that it is often seen as the only form of handling.
     24% PAB: I like this sentence better than the next sentence.
     25% This separation is uncommon because termination exception handling is so
     26% much more common that it is often assumed.
     27% WHY: Mention other forms of continuation and \cite{CommonLisp} here?
     28
     29Exception handling relies on the concept of nested functions to create handlers that deal with exceptions.
    4030\begin{center}
    41 %\input{termination}
    42 %
    43 %\medskip
    44 \input{termhandle.pstex_t}
    45 % I hate these diagrams, but I can't access xfig to fix them and they are
    46 % better than the alternative.
     31\begin{tabular}[t]{ll}
     32\begin{lstlisting}[aboveskip=0pt,belowskip=0pt,language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
     33void f( void (*hp)() ) {
     34        hp();
     35}
     36void g( void (*hp)() ) {
     37        f( hp );
     38}
     39void h( int @i@, void (*hp)() ) {
     40        void @handler@() { // nested
     41                printf( "%d\n", @i@ );
     42        }
     43        if ( i == 1 ) hp = handler;
     44        if ( i > 0 ) h( i - 1, hp );
     45        else g( hp );
     46}
     47h( 2, 0 );
     48\end{lstlisting}
     49&
     50\raisebox{-0.5\totalheight}{\input{handler}}
     51\end{tabular}
    4752\end{center}
    48 
    49 Resumption exception handling searches the stack for a handler and then calls
    50 it without removing any other stack frames.
    51 The handler is run on top of the existing stack, often as a new function or
    52 closure capturing the context in which the handler was defined.
    53 After the handler has finished running, it returns control to the function
    54 that preformed the raise, usually starting after the raise.
     53The nested function @handler@ in the second stack frame is explicitly passed to function @f@.
     54When this handler is called in @f@, it uses the parameter @i@ in the second stack frame, which is accessible by an implicit lexical-link pointer.
     55Setting @hp@ in @h@ at different points in the recursion, results in invoking a different handler.
     56Exception handling extends this idea by eliminating explicit handler passing, and instead, performing a stack search for a handler that matches some criteria (conditional dynamic call), and calls the handler at the top of the stack.
     57It is the runtime search $O(N)$ that differentiates an EHM call (raise) from normal dynamic call $O(1)$ via a function or virtual-member pointer.
     58
     59Termination exception handling searches the stack for a handler, unwinds the stack to the frame containing the matching handler, and calling the handler at the top of the stack.
    5560\begin{center}
    56 %\input{resumption}
    57 %
    58 %\medskip
    59 \input{resumhandle.pstex_t}
    60 % The other one.
     61\input{termination}
    6162\end{center}
     63Note, since the handler can reference variables in @h@, @h@ must remain on the stack for the handler call.
     64After the handler returns, control continues after the lexical location of the handler in @h@ (static return)~\cite[p.~108]{Tennent77}.
     65Unwinding allows recover to any previous
     66function on the stack, skipping any functions between it and the
     67function containing the matching handler.
     68
     69Resumption exception handling searches the stack for a handler, does \emph{not} unwind the stack to the frame containing the matching handler, and calls the handler at the top of the stack.
     70\begin{center}
     71\input{resumption}
     72\end{center}
     73After the handler returns, control continues after the resume in @f@ (dynamic return).
     74Not unwinding allows fix up of the problem in @f@ by any previous function on the stack, without disrupting the current set of stack frames.
    6275
    6376Although a powerful feature, exception handling tends to be complex to set up
    64 and expensive to use,
     77and expensive to use
    6578so it is often limited to unusual or ``exceptional" cases.
    66 The classic example is error handling; exceptions can be used to
    67 remove error handling logic from the main execution path, and pay
     79The classic example is error handling, where exceptions are used to
     80remove error handling logic from the main execution path, while paying
    6881most of the cost only when the error actually occurs.
    6982
     
    7285The \CFA EHM implements all of the common exception features (or an
    7386equivalent) found in most other EHMs and adds some features of its own.
    74 The design of all the features had to be adapted to \CFA's feature set, as
     87The design of all the features had to be adapted to \CFA's feature set as
    7588some of the underlying tools used to implement and express exception handling
    7689in other languages are absent in \CFA.
    77 Still, the resulting syntax resembles that of other languages:
    78 \begin{cfa}
    79 try {
     90Still the resulting basic syntax resembles that of other languages:
     91\begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
     92@try@ {
    8093        ...
    8194        T * object = malloc(request_size);
    8295        if (!object) {
    83                 throw OutOfMemory{fixed_allocation, request_size};
     96                @throw@ OutOfMemory{fixed_allocation, request_size};
    8497        }
    8598        ...
    86 } catch (OutOfMemory * error) {
     99} @catch@ (OutOfMemory * error) {
    87100        ...
    88101}
    89 \end{cfa}
     102\end{lstlisting}
    90103% A note that yes, that was a very fast overview.
    91104The design and implementation of all of \CFA's EHM's features are
     
    94107
    95108% The current state of the project and what it contributes.
    96 All of these features have been implemented in \CFA,
    97 covering both changes to the compiler and the run-time.
    98 In addition, a suite of test cases and performance benchmarks were created
    99 alongside the implementation.
    100 The implementation techniques are generally applicable in other programming
     109The majority of the \CFA EHM is implemented in \CFA, except for a small amount of assembler code.
     110In addition,
     111a suite of tests and performance benchmarks were created as part of this project.
     112The \CFA implementation techniques are generally applicable in other programming
    101113languages and much of the design is as well.
    102 Some parts of the EHM use other features unique to \CFA and would be
    103 harder to replicate in other programming languages.
     114Some parts of the EHM use features unique to \CFA, and hence,
     115are harder to replicate in other programming languages.
     116% Talk about other programming languages.
     117Three well known programming languages with EHMs, %/exception handling
     118C++, Java and Python are examined in the performance work. However, these languages focus on termination
     119exceptions, so there is no comparison with resumption.
    104120
    105121The contributions of this work are:
    106122\begin{enumerate}
    107123\item Designing \CFA's exception handling mechanism, adapting designs from
    108 other programming languages and creating new features.
    109 \item Implementing stack unwinding and the \CFA EHM, including updating
    110 the \CFA compiler and the run-time environment.
     124other programming languages, and creating new features.
     125\item Implementing stack unwinding for the \CFA EHM, including updating
     126the \CFA compiler and run-time environment to generate and execute the EHM code.
    111127\item Designing and implementing a prototype virtual system.
    112128% I think the virtual system and per-call site default handlers are the only
    113129% "new" features, everything else is a matter of implementation.
    114 \item Creating tests to check the behaviour of the EHM.
    115 \item Creating benchmarks to check the performance of the EHM,
    116 as compared to other languages.
     130\item Creating tests and performance benchmarks to compare with EHM's in other languages.
    117131\end{enumerate}
    118132
    119 The rest of this thesis is organized as follows.
    120 The current state of exceptions is covered in \autoref{s:background}.
    121 The existing state of \CFA is covered in \autoref{c:existing}.
    122 New EHM features are introduced in \autoref{c:features},
     133%\todo{I can't figure out a good lead-in to the roadmap.}
     134The thesis is organization as follows.
     135The next section and parts of \autoref{c:existing} cover existing EHMs.
     136New \CFA EHM features are introduced in \autoref{c:features},
    123137covering their usage and design.
    124138That is followed by the implementation of these features in
    125139\autoref{c:implement}.
    126 Performance results are examined in \autoref{c:performance}.
    127 Possibilities to extend this project are discussed in \autoref{c:future}.
    128 Finally, the project is summarized in \autoref{c:conclusion}.
     140Performance results are presented in \autoref{c:performance}.
     141Summing up and possibilities for extending this project are discussed in \autoref{c:future}.
    129142
    130143\section{Background}
    131144\label{s:background}
    132145
    133 Exception handling has been examined before in programming languages,
    134 with papers on the subject dating back 70s.\cite{Goodenough75}
     146Exception handling is a well examined area in programming languages,
     147with papers on the subject dating back the 70s~\cite{Goodenough75}.
    135148Early exceptions were often treated as signals, which carried no information
    136 except their identity.
    137 Ada originally used this system\cite{Ada}, but now allows for a string
    138 message as a payload\cite{Ada12}.
    139 
    140 The modern flagship for termination exceptions -- if one exists -- is \Cpp,
     149except their identity. Ada~\cite{Ada} still uses this system.
     150
     151The modern flag-ship for termination exceptions is \Cpp,
    141152which added them in its first major wave of non-object-orientated features
    142 in 1990.\cite{CppHistory}
    143 Many EHMs have special exception types,
    144 however \Cpp has the ability to use any type as an exception.
    145 These were found to be not very useful and have been pushed aside for classes
    146 inheriting from
     153in 1990.
     154% https://en.cppreference.com/w/cpp/language/history
     155While many EHMs have special exception types,
     156\Cpp has the ability to use any type as an exception.
     157However, this generality is not particularly useful, and has been pushed aside for classes, with a convention of inheriting from
    147158\code{C++}{std::exception}.
    148 Although there is a special catch-all syntax (@catch(...)@), there are no
    149 operations that can be performed on the caught value, not even type inspection.
    150 Instead, the base exception-type \code{C++}{std::exception} defines common
    151 functionality (such as
    152 the ability to describe the reason the exception was raised) and all
     159While \Cpp has a special catch-all syntax @catch(...)@, there is no way to discriminate its exception type, so nothing can
     160be done with the caught value because nothing is known about it.
     161Instead the base exception-type \code{C++}{std::exception} is defined with common functionality (such as
     162the ability to print a message when the exception is raised but not caught) and all
    153163exceptions have this functionality.
    154 That trade-off, restricting usable types to gain guaranteed functionality,
    155 is almost universal now, as without some common functionality it is almost
    156 impossible to actually handle any errors.
    157 
    158 Java was the next popular language to use exceptions.\cite{Java8}
    159 Its exception system largely reflects that of \Cpp, except that it requires
    160 you throw a child type of \code{Java}{java.lang.Throwable}
     164Having a root exception-type seems to be the standard now, as the guaranteed functionality is worth
     165any lost in flexibility from limiting exceptions types to classes.
     166
     167Java~\cite{Java} was the next popular language to use exceptions.
     168Its exception system largely reflects that of \Cpp, except it requires
     169exceptions to be a subtype of \code{Java}{java.lang.Throwable}
    161170and it uses checked exceptions.
    162 Checked exceptions are part of a function's interface,
    163 the exception signature of the function.
    164 Every exception that could be raised from a function, either directly or
    165 because it is not handled from a called function, is given.
    166 Using this information, it is possible to statically verify if any given
    167 exception is handled, and guarantee that no exception will go unhandled.
    168 Making exception information explicit improves clarity and safety,
    169 but can slow down or restrict programming.
    170 For example, programming high-order functions becomes much more complex
    171 if the argument functions could raise exceptions.
    172 However, as odd it may seem, the worst problems are rooted in the simple
    173 inconvenience of writing and updating exception signatures.
    174 This has caused Java programmers to develop multiple programming ``hacks''
    175 to circumvent checked exceptions, negating their advantages.
    176 One particularly problematic example is the ``catch-and-ignore'' pattern,
    177 where an empty handler is used to handle an exception without doing any
    178 recovery or repair. In theory that could be good enough to properly handle
    179 the exception, but more often is used to ignore an exception that the       
    180 programmer does not feel is worth the effort of handling, for instance if
    181 they do not believe it will ever be raised.
    182 If they are incorrect, the exception will be silenced, while in a similar
    183 situation with unchecked exceptions the exception would at least activate   
    184 the language's unhandled exception code (usually, a program abort with an
    185 error message).
     171Checked exceptions are part of a function's interface defining all exceptions it or its called functions raise.
     172Using this information, it is possible to statically verify if a handler exists for all raised exception, \ie no uncaught exceptions.
     173Making exception information explicit, improves clarity and
     174safety, but can slow down programming.
     175For example, programming complexity increases when dealing with high-order methods or an overly specified
     176throws clause. However some of the issues are more
     177programming annoyances, such as writing/updating many exception signatures after adding or remove calls.
     178Java programmers have developed multiple programming ``hacks'' to circumvent checked exceptions negating the robustness it is suppose to provide.
     179For example, the ``catch-and-ignore" pattern, where the handler is empty because the exception does not appear relevant to the programmer versus
     180repairing or recovering from the exception.
    186181
    187182%\subsection
    188183Resumption exceptions are less popular,
    189 although resumption is as old as termination; that is, few
     184although resumption is as old as termination;
     185hence, few
    190186programming languages have implemented them.
    191187% http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/parc/techReports/
    192188%   CSL-79-3_Mesa_Language_Manual_Version_5.0.pdf
    193 Mesa is one programming language that did.\cite{Mesa} Experience with Mesa
    194 is quoted as being one of the reasons resumptions were not
     189Mesa~\cite{Mesa} is one programming languages that did. Experience with Mesa
     190is quoted as being one of the reasons resumptions are not
    195191included in the \Cpp standard.
    196192% https://en.wikipedia.org/wiki/Exception_handling
    197 Since then, resumptions have been ignored in mainstream programming languages.
    198 However, resumption is being revisited in the context of decades of other
    199 developments in programming languages.
    200 While rejecting resumption may have been the right decision in the past,
    201 the situation has changed since then.
    202 Some developments, such as the functional programming equivalent to resumptions,
    203 algebraic effects\cite{Zhang19}, are enjoying success.
    204 A complete reexamination of resumption is beyond this thesis,
    205 but their reemergence is enough reason to try them in \CFA.
     193As a result, resumption has ignored in main-stream programming languages.
     194However, ``what goes around comes around'' and resumption is being revisited now (like user-level threading).
     195While rejecting resumption might have been the right decision in the past, there are decades
     196of developments in computer science that have changed the situation.
     197Some of these developments, such as functional programming's resumption
     198equivalent, algebraic effects\cite{Zhang19}, are enjoying significant success.
     199A complete reexamination of resumptions is beyond this thesis, but their re-emergence is
     200enough to try them in \CFA.
    206201% Especially considering how much easier they are to implement than
    207 % termination exceptions and how much Peter likes them.
    208 
    209 %\subsection
    210 Functional languages tend to use other solutions for their primary error
    211 handling mechanism, but exception-like constructs still appear.
    212 Termination appears in the error construct, which marks the result of an
    213 expression as an error; then the result of any expression that tries to use
    214 it also results in an error, and so on until an appropriate handler is reached.
     202% termination exceptions.
     203
     204%\subsection
     205Functional languages tend to use other solutions for their primary EHM,
     206but exception-like constructs still appear.
     207Termination appears in error construct, which marks the result of an
     208expression as an error; thereafter, the result of any expression that tries to use it is also an
     209error, and so on until an appropriate handler is reached.
    215210Resumption appears in algebraic effects, where a function dispatches its
    216211side-effects to its caller for handling.
    217212
    218213%\subsection
    219 More recently, exceptions seem to be vanishing from newer programming
    220 languages, replaced by ``panic".
    221 In Rust, a panic is just a program level abort that may be implemented by
    222 unwinding the stack like in termination exception
    223 handling.\cite{RustPanicMacro}\cite{RustPanicModule}
    224 Go's panic though is very similar to a termination, except it only supports
     214Some programming languages have moved to a restricted kind of EHM
     215called ``panic".
     216In Rust~\cite{Rust}, a panic is just a program level abort that may be implemented by
     217unwinding the stack like in termination exception handling.
     218% https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
     219In Go~\cite{Go}, a panic is very similar to a termination, except it only supports
    225220a catch-all by calling \code{Go}{recover()}, simplifying the interface at
    226 the cost of flexibility.\cite{Go:2021}
    227 
    228 %\subsection
    229 As exception handling's most common use cases are in error handling,
    230 here are some other ways to handle errors with comparisons with exceptions.
     221the cost of flexibility.
     222
     223%\subsection
     224While exception handling's most common use cases are in error handling,
     225here are other ways to handle errors with comparisons to exceptions.
    231226\begin{itemize}
    232227\item\emph{Error Codes}:
    233 This pattern has a function return an enumeration (or just a set of fixed
    234 values) to indicate if an error has occurred and possibly which error it was.
    235 
    236 Error codes mix exceptional/error and normal values, enlarging the range of
    237 possible return values. This can be addressed with multiple return values
    238 (or a tuple) or a tagged union.
    239 However, the main issue with error codes is forgetting to check them,
     228This pattern has a function return an enumeration (or just a set of fixed values) to indicate
     229if an error occurred and possibly which error it was.
     230
     231Error codes mix exceptional and normal values, artificially enlarging the type and/or value range.
     232Some languages address this issue by returning multiple values or a tuple, separating the error code from the function result.
     233However, the main issue with error codes is forgetting to checking them,
    240234which leads to an error being quietly and implicitly ignored.
    241 Some new languages and tools will try to issue warnings when an error code
    242 is discarded to avoid this problem.
    243 Checking error codes also bloats the main execution path,
    244 especially if the error is not handled immediately and has to be passed
    245 through multiple functions before it is addressed.
    246 
    247 Here is an example of the pattern in Bash, where commands can only  ``return"
    248 numbers and most output is done through streams of text.
    249 \begin{lstlisting}[language=bash,escapechar={}]
    250 # Immediately after running a command:
    251 case $? in
    252 0)
    253         # Success
    254         ;;
    255 1)
    256         # Error Code 1
    257         ;;
    258 2|3)
    259         # Error Code 2 or Error Code 3
    260         ;;
    261 # Add more cases as needed.
    262 asac
    263 \end{lstlisting}
     235Some new languages have tools that issue warnings, if the error code is
     236discarded to avoid this problem.
     237Checking error codes also results in bloating the main execution path, especially if an error is not dealt with locally and has to be cascaded down the call stack to a higher-level function..
    264238
    265239\item\emph{Special Return with Global Store}:
    266 Similar to the error codes pattern but the function itself only returns
    267 that there was an error,
    268 and stores the reason for the error in a fixed global location.
    269 For example, many routines in the C standard library will only return some
    270 error value (such as -1 or a null pointer) and the error code is written into
    271 the standard variable @errno@.
    272 
    273 This approach avoids the multiple results issue encountered with straight
    274 error codes as only a single error value has to be returned,
    275 but otherwise has the same disadvantages and more.
    276 Every function that reads or writes to the global store must agree on all
    277 possible errors and managing it becomes more complex with concurrency.
    278 
    279 This example shows some of what has to be done to robustly handle a C
    280 standard library function that reports errors this way.
    281 \begin{lstlisting}[language=C]
    282 // Now a library function can set the error.
    283 int handle = open(path_name, flags);
    284 if (-1 == handle) {
    285         switch (errno) {
    286     case ENAMETOOLONG:
    287                 // path_name is a bad argument.
    288                 break;
    289         case ENFILE:
    290                 // A system resource has been exausted.
    291                 break;
    292         // And many more...
    293     }
    294 }
    295 \end{lstlisting}
    296 % cite open man page?
     240Some functions only return a boolean indicating success or failure
     241and store the exact reason for the error in a fixed global location.
     242For example, many C routines return non-zero or -1, indicating success or failure,
     243and write error details into the C standard variable @errno@.
     244
     245This approach avoids the multiple results issue encountered with straight error codes
     246but otherwise has many (if not more) of the disadvantages.
     247For example, everything that uses the global location must agree on all possible errors and global variable are unsafe with concurrency.
    297248
    298249\item\emph{Return Union}:
     
    300251Success is one tag and the errors are another.
    301252It is also possible to make each possible error its own tag and carry its own
    302 additional information, but the two-branch format is easy to make generic
     253additional information, but the two branch format is easy to make generic
    303254so that one type can be used everywhere in error handling code.
    304255
    305 This pattern is very popular in any functional or semi-functional language
    306 with primitive support for tagged unions (or algebraic data types).
    307 Return unions can also be expressed as monads (evaluation in a context)
    308 and often are in languages with special syntax for monadic evaluation,
    309 such as Haskell's \code{haskell}{do} blocks.
    310 
    311 The main advantage is that an arbitrary object can be used to represent an
    312 error, so it can include a lot more information than a simple error code.
    313 The disadvantages include that the it does have to be checked along the main
    314 execution, and if there aren't primitive tagged unions proper, usage can be
    315 hard to enforce.
    316 % We need listing Rust/rust to format code snippets from it.
     256This pattern is very popular in functional or any semi-functional language with
     257primitive support for tagged unions (or algebraic data types).
     258% We need listing Rust/rust to format code snipits from it.
    317259% Rust's \code{rust}{Result<T, E>}
    318 
    319 This is a simple example of examining the result of a failing function in
    320 Haskell, using its \code{haskell}{Either} type.
    321 Examining \code{haskell}{error} further would likely involve more matching,
    322 but the type of \code{haskell}{error} is user defined so there are no
    323 general cases.
    324 \begin{lstlisting}[language=haskell]
    325 case failingFunction argA argB of
    326     Right value -> -- Use the successful computed value.
    327     Left error -> -- Handle the produced error.
    328 \end{lstlisting}
    329 
    330 Return unions as monads will result in the same code, but can hide most
    331 of the work to propagate errors in simple cases. The code to actually handle
    332 the errors, or to interact with other monads (a common case in these
    333 languages) still has to be written by hand.
    334 
    335 If \code{haskell}{failingFunction} is implemented with two helpers that
    336 use the same error type, then it can be implemented with a \code{haskell}{do}
    337 block.
    338 \begin{lstlisting}[language=haskell,literate={}]
    339 failingFunction x y = do
    340         z <- helperOne x
    341         helperTwo y z
    342 \end{lstlisting}
     260The main advantage is providing for more information about an
     261error, other than one of a fix-set of ids.
     262While some languages use checked union access to force error-code checking,
     263it is still possible to bypass the checking.
     264The main disadvantage is again significant error code on the main execution path and cascading through called functions.
    343265
    344266\item\emph{Handler Functions}:
    345 This pattern associates errors with functions.
    346 On error, the function that produced the error calls another function to
     267This pattern implicitly associates functions with errors.
     268On error, the function that produced the error implicitly calls another function to
    347269handle it.
    348270The handler function can be provided locally (passed in as an argument,
    349271either directly as as a field of a structure/object) or globally (a global
    350272variable).
    351 C++ uses this approach as its fallback system if exception handling fails,
    352 such as \snake{std::terminate} and, for a time,
    353 \snake{std::unexpected}.\footnote{\snake{std::unexpected} was part of the
    354 Dynamic Exception Specification, which has been removed from the standard
    355 as of C++20.\cite{CppExceptSpec}}
    356 
    357 Handler functions work a lot like resumption exceptions,
    358 but without the dynamic search for a handler.
    359 Since setting up the handler can be more complex/expensive,
    360 especially when the handler has to be passed through multiple layers of
    361 function calls, but cheaper (constant time) to call,
    362 they are more suited to more frequent (less exceptional) situations.
    363 Although, in \Cpp and other languages that do not have checked exceptions,
    364 they can actually be enforced by the type system be more reliable.
    365 
    366 This is a more local example in \Cpp, using a function to provide
    367 a default value for a mapping.
    368 \begin{lstlisting}[language=C++]
    369 ValueT Map::key_or_default(KeyT key, ValueT(*make_default)(KeyT)) {
    370         ValueT * value = find_value(key);
    371         if (nullptr != value) {
    372                 return *value;
    373         } else {
    374                 return make_default(key);
    375         }
    376 }
    377 \end{lstlisting}
     273C++ uses this approach as its fallback system if exception handling fails, \eg
     274\snake{std::terminate_handler} and for a time \snake{std::unexpected_handler}
     275
     276Handler functions work a lot like resumption exceptions, without the dynamic handler search.
     277Therefore, setting setting up the handler can be more complex/expensive, especially if the handle must be passed through multiple function calls, but cheaper to call $O(1)$, and hence,
     278are more suited to frequent exceptional situations.
     279% The exception being global handlers if they are rarely change as the time
     280% in both cases shrinks towards zero.
    378281\end{itemize}
    379282
    380283%\subsection
    381284Because of their cost, exceptions are rarely used for hot paths of execution.
    382 Hence, there is an element of self-fulfilling prophecy as implementation
    383 techniques have been focused on making them cheap to set up,
    384 happily making them expensive to use in exchange.
    385 This difference is less important in higher-level scripting languages,
    386 where using exceptions for other tasks is more common.
    387 An iconic example is Python's
    388 \code{Python}{StopIteration}\cite{PythonExceptions} exception, that
    389 is thrown by an iterator to indicate that it is exhausted.
    390 When paired with Python's iterator-based for-loop, this will be thrown every
    391 time the end of the loop is reached.\cite{PythonForLoop}
     285Therefore, there is an element of self-fulfilling prophecy for implementation
     286techniques to make exceptions cheap to set-up at the cost
     287of expensive usage.
     288This cost differential is less important in higher-level scripting languages, where use of exceptions for other tasks is more common.
     289An iconic example is Python's @StopIteration@ exception that is thrown by
     290an iterator to indicate that it is exhausted, especially when combined with Python's heavy
     291use of the iterator-based for-loop.
     292% https://docs.python.org/3/library/exceptions.html#StopIteration
  • doc/theses/andrew_beach_MMath/performance.tex

    rb7fd9daf rf95634e  
    33
    44Performance is of secondary importance for most of this project.
    5 Instead, the focus was to get the features working. The only performance
     5Instead, the focus is to get the features working. The only performance
    66requirement is to ensure the tests for correctness run in a reasonable
    7 amount of time. Hence, only a few basic performance tests were performed to
    8 check this requirement.
     7amount of time.
    98
    109\section{Test Set-Up}
    1110Tests were run in \CFA, C++, Java and Python.
    1211In addition there are two sets of tests for \CFA,
    13 one with termination and one with resumption.
    14 
    15 GCC C++ is the most comparable language because both it and \CFA use the same
     12one for termination and one for resumption exceptions.
     13
     14C++ is the most comparable language because both it and \CFA use the same
    1615framework, libunwind.
    17 In fact, the comparison is almost entirely in quality of implementation.
    18 Specifically, \CFA's EHM has had significantly less time to be optimized and
     16In fact, the comparison is almost entirely a quality of implementation
     17comparison: \CFA's EHM has had significantly less time to be optimized and
    1918does not generate its own assembly. It does have a slight advantage in that
    20 \Cpp has to do some extra bookkeeping to support its utility functions,
    21 but otherwise \Cpp should have a significant advantage.
    22 
    23 Java, a popular language with similar termination semantics,
    24 is implemented in a very different environment, a virtual machine with
     19there are some features it handles directly instead of through utility functions,
     20but otherwise \Cpp has a significant advantage.
     21
     22Java is another very popular language with similar termination semantics.
     23It is implemented in a very different environment, a virtual machine with
    2524garbage collection.
    26 It also implements the finally clause on try blocks allowing for a direct
     25It also implements the @finally@ clause on @try@ blocks allowing for a direct
    2726feature-to-feature comparison.
    28 As with \Cpp, Java's implementation is mature, has more optimizations
    29 and extra features as compared to \CFA.
    30 
    31 Python is used as an alternative comparison because of the \CFA EHM's
    32 current performance goals, which is to not be prohibitively slow while the
     27As with \Cpp, Java's implementation is mature, optimizations
     28and has extra features.
     29
     30Python is used as an alternative point of comparison because of the \CFA EHM's
     31current performance goals, which is not to be prohibitively slow while the
    3332features are designed and examined. Python has similar performance goals for
    3433creating quick scripts and its wide use suggests it has achieved those goals.
     
    3736resumption exceptions. Even the older programming languages with resumption
    3837seem to be notable only for having resumption.
    39 On the other hand, the functional equivalents to resumption are too new.
    40 There does not seem to be any standard implementations in well-known
    41 languages; so far, they seem confined to extensions and research languages.
    42 % There was some maybe interesting comparison to an OCaml extension
    43 % but I'm not sure how to get that working if it is interesting.
    44 Instead, resumption is compared to its simulation in other programming
    45 languages: fixup functions that are explicitly passed into a function.
     38So instead, resumption is compared to a less similar but much more familiar
     39feature, termination exceptions.
    4640
    4741All tests are run inside a main loop that repeatedly performs a test.
    4842This approach avoids start-up or tear-down time from
    4943affecting the timing results.
    50 The number of times the loop is run is configurable from the command line;
    51 the number used in the timing runs is given with the results per test.
    52 The Java tests run the main loop 1000 times before
    53 beginning the actual test to ``warm up" the JVM.
    54 % All other languages are precompiled or interpreted.
     44Each test is run a million times.
     45The Java versions of the test run this loop an extra 1000 times before
     46beginning to actual test to ``warm-up" the JVM.
    5547
    5648Timing is done internally, with time measured immediately before and
     
    5951unhandled exceptions in \Cpp and Java as that would cause the process to
    6052terminate.
    61 Luckily, performance on the ``give up and kill the process" path is not
     53Luckily, performance on the ``give-up and kill the process" path is not
    6254critical.
    6355
    6456The exceptions used in these tests are always based off of
    65 the base exception for the language.
    66 This requirement minimizes performance differences based
     57a base exception. This requirement minimizes performance differences based
    6758on the object model used to represent the exception.
    6859
     
    7162For example, empty inline assembly blocks are used in \CFA and \Cpp to
    7263prevent excessive optimizations while adding no actual work.
     64Each test was run eleven times. The top three and bottom three results were
     65discarded and the remaining five values are averaged.
     66
     67The tests are compiled with gcc-10 for \CFA and g++-10 for \Cpp. Java is
     68compiled with 11.0.11. Python with 3.8. The tests were run on:
     69\begin{itemize}[nosep]
     70\item
     71ARM 2280 Kunpeng 920 48-core 2$\times$socket \lstinline{@} 2.6 GHz running Linux v5.11.0-25
     72\item
     73AMD 6380 Abu Dhabi 16-core 4$\times$socket \lstinline{@} 2.5 GHz running Linux v5.11.0-25
     74\end{itemize}
    7375
    7476% We don't use catch-alls but if we did:
    7577% Catch-alls are done by catching the root exception type (not using \Cpp's
    7678% \code{C++}{catch(...)}).
    77 
    78 When collecting data, each test is run eleven times. The top three and bottom
    79 three results are discarded and the remaining five values are averaged.
    80 The test are run with the latest (still pre-release) \CFA compiler,
    81 using gcc-10 10.3.0 as a backend.
    82 g++-10 10.3.0 is used for \Cpp.
    83 Java tests are complied and run with Oracle OpenJDK version 11.0.11.
    84 Python used CPython version 3.8.10.
    85 The machines used to run the tests are:
    86 \begin{itemize}[nosep]
    87 \item ARM 2280 Kunpeng 920 48-core 2$\times$socket
    88       \lstinline{@} 2.6 GHz running Linux v5.11.0-25
    89 \item AMD 6380 Abu Dhabi 16-core 4$\times$socket
    90       \lstinline{@} 2.5 GHz running Linux v5.11.0-25
    91 \end{itemize}
    92 These represent the two major families of hardware architecture.
    9379
    9480\section{Tests}
     
    9783They should provide a guide as to where the EHM's costs are found.
    9884
    99 \paragraph{Stack Traversal}
    100 This group of tests measures the cost of traversing the stack
    101 (and in termination, unwinding it).
    102 Inside the main loop is a call to a recursive function.
    103 This function calls itself F times before raising an exception.
    104 F is configurable from the command line, but is usually 100.
    105 This builds up many stack frames, and any contents they may have,
    106 before the raise.
    107 The exception is always handled at the base of the stack.
    108 For example the Empty test for \CFA resumption looks like:
     85\paragraph{Raise and Handle}
     86The first group measures the cost of a try statement when exceptions are raised
     87and \emph{the stack is unwound}.  Each test has has a repeating function like
     88the following
    10989\begin{cfa}
    11090void unwind_empty(unsigned int frames) {
    11191        if (frames) {
    11292                unwind_empty(frames - 1);
    113         } else {
    114                 throwResume (empty_exception){&empty_vt};
    115         }
     93        } else throw (empty_exception){&empty_vt};
    11694}
    11795\end{cfa}
    118 Other test cases have additional code around the recursive call adding
    119 something besides simple stack frames to the stack.
    120 Note that both termination and resumption have to traverse over
    121 the stack but only termination has to unwind it.
    122 \begin{itemize}[nosep]
    123 % \item None:
    124 % Reuses the empty test code (see below) except that the number of frames
    125 % is set to 0 (this is the only test for which the number of frames is not
    126 % 100). This isolates the start-up and shut-down time of a throw.
     96which is called M times, where each call recurses to a depth of N, an
     97exception is raised, the stack is a unwound, and the exception caught.
     98\begin{itemize}[nosep]
    12799\item Empty:
    128 The repeating function is empty except for the necessary control code.
    129 As other traversal tests add to this, it is the baseline for the group
    130 as the cost comes from traversing over and unwinding a stack frame
    131 that has no other interactions with the exception system.
     100This test measures the cost of raising (stack walking) an exception through empty
     101empty stack frames to an empty handler. (see above)
    132102\item Destructor:
    133 The repeating function creates an object with a destructor before calling
    134 itself.
    135 Comparing this to the empty test gives the time to traverse over and
    136 unwind a destructor.
     103
     104This test measures the cost of raising an exception through non-empty frames
     105where each frame has an object requiring destruction, to an empty
     106handler. Hence, there are N destructor calls during unwinding.
     107\begin{cfa}
     108if (frames) {
     109        WithDestructor object;
     110        unwind_empty(frames - 1);
     111\end{cfa}
    137112\item Finally:
    138 The repeating function calls itself inside a try block with a finally clause
    139 attached.
    140 Comparing this to the empty test gives the time to traverse over and
    141 unwind a finally clause.
     113This test measures the cost of establishing a try block with an empty finally
     114clause on the front side of the recursion and running the empty finally clause
     115on the back side of the recursion during stack unwinding.
     116\begin{cfa}
     117if (frames) {
     118        try {
     119                unwind_finally(frames - 1);
     120        } finally {}
     121\end{cfa}
    142122\item Other Handler:
    143 The repeating function calls itself inside a try block with a handler that
    144 does not match the raised exception, but is of the same kind of handler.
    145 This means that the EHM has to check each handler, and continue
    146 over all of them until it reaches the base of the stack.
    147 Comparing this to the empty test gives the time to traverse over and
    148 unwind a handler.
     123This test is like the finally test but the try block has a catch clause for an
     124exception that is not raised, so catch matching is executed during stack
     125unwinding but the match never successes until the catch at the bottom of the
     126stack.
     127\begin{cfa}
     128if (frames) {
     129        try {
     130                unwind_other(frames - 1);
     131        } catch (not_raised_exception *) {}
     132\end{cfa}
    149133\end{itemize}
    150134
    151135\paragraph{Cross Try Statement}
    152 This group of tests measures the cost for setting up exception handling,
    153 if it is
    154 not used because the exceptional case did not occur.
    155 Tests repeatedly cross (enter, execute and leave) a try statement but never
    156 perform a raise.
     136The next group measures just the cost of executing a try statement so
     137\emph{there is no stack unwinding}.  Hence, the program main loops N times
     138around:
     139\begin{cfa}
     140try {
     141} catch (not_raised_exception *) {}
     142\end{cfa}
    157143\begin{itemize}[nosep]
    158144\item Handler:
    159 The try statement has a handler (of the appropriate kind).
     145The try statement has a handler.
    160146\item Finally:
    161 The try statement has a finally clause.
     147The try statement replaces the handler with a finally clause.
    162148\end{itemize}
    163149
    164150\paragraph{Conditional Matching}
    165 This group measures the cost of conditional matching.
     151This final group measures the cost of conditional matching.
    166152Only \CFA implements the language level conditional match,
    167 the other languages mimic it with an ``unconditional" match (it still
    168 checks the exception's type) and conditional re-raise if it is not supposed
     153the other languages must mimic with an ``unconditional" match (it still
     154checks the exception's type) and conditional re-raise if it was not supposed
    169155to handle that exception.
    170 
    171 Here is the pattern shown in \CFA and \Cpp. Java and Python use the same
    172 pattern as \Cpp, but with their own syntax.
    173 
    174 \begin{minipage}{0.45\textwidth}
     156\begin{center}
     157\begin{tabular}{ll}
     158\multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp, Java, Python} \\
    175159\begin{cfa}
    176160try {
    177         ...
    178 } catch (exception_t * e ;
    179                 should_catch(e)) {
    180         ...
     161        throw_exception();
     162} catch (empty_exception * exc;
     163                 should_catch) {
    181164}
    182165\end{cfa}
    183 \end{minipage}
    184 \begin{minipage}{0.55\textwidth}
    185 \begin{lstlisting}[language=C++]
     166&
     167\begin{cfa}
    186168try {
    187         ...
    188 } catch (std::exception & e) {
    189         if (!should_catch(e)) throw;
    190         ...
     169        throw_exception();
     170} catch (EmptyException & exc) {
     171        if (!should_catch) throw;
    191172}
    192 \end{lstlisting}
    193 \end{minipage}
     173\end{cfa}
     174\end{tabular}
     175\end{center}
    194176\begin{itemize}[nosep]
    195177\item Match All:
     
    199181\end{itemize}
    200182
    201 \paragraph{Resumption Simulation}
    202 A slightly altered version of the Empty Traversal test is used when comparing
    203 resumption to fix-up routines.
    204 The handler, the actual resumption handler or the fix-up routine,
    205 always captures a variable at the base of the loop,
    206 and receives a reference to a variable at the raise site, either as a
    207 field on the exception or an argument to the fix-up routine.
    208 % I don't actually know why that is here but not anywhere else.
    209 
    210183%\section{Cost in Size}
    211184%Using exceptions also has a cost in the size of the executable.
     
    219192
    220193\section{Results}
    221 % First, introduce the tables.
    222 \autoref{t:PerformanceTermination},
    223 \autoref{t:PerformanceResumption}
    224 and~\autoref{t:PerformanceFixupRoutines}
    225 show the test results.
    226 In cases where a feature is not supported by a language, the test is skipped
    227 for that language and the result is marked N/A.
    228 There are also cases where the feature is supported but measuring its
    229 cost is impossible. This happened with Java, which uses a JIT that optimizes
    230 away the tests and cannot be stopped.\cite{Dice21}
    231 These tests are marked N/C.
    232 To get results in a consistent range (1 second to 1 minute is ideal,
    233 going higher is better than going low) N, the number of iterations of the
    234 main loop in each test, is varied between tests. It is also given in the
    235 results and has a value in the millions.
    236 
    237 An anomaly in some results came from \CFA's use of GCC nested functions.
    238 These nested functions are used to create closures that can access stack
    239 variables in their lexical scope.
    240 However, if they do so, then they can cause the benchmark's run time to
    241 increase by an order of magnitude.
    242 The simplest solution is to make those values global variables instead
    243 of function-local variables.
    244 % Do we know if editing a global inside nested function is a problem?
    245 Tests that had to be modified to avoid this problem have been marked
    246 with a ``*'' in the results.
    247 
    248 % Now come the tables themselves:
    249 % You might need a wider window for this.
    250 
    251 \begin{table}[htb]
    252 \centering
    253 \caption{Termination Performance Results (sec)}
    254 \label{t:PerformanceTermination}
    255 \begin{tabular}{|r|*{2}{|r r r r|}}
    256 \hline
    257                        & \multicolumn{4}{c||}{AMD}         & \multicolumn{4}{c|}{ARM}  \\
    258 \cline{2-9}
    259 N\hspace{8pt}          & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c||}{Python} &
    260                          \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c|}{Python} \\
    261 \hline
    262 Empty Traversal (1M)   & 23.0  & 9.6   & 17.6  & 23.4      & 30.6  & 13.6  & 15.5  & 14.7  \\
    263 D'tor Traversal (1M)   & 48.1  & 23.5  & N/A   & N/A       & 64.2  & 29.2  & N/A   & N/A   \\
    264 Finally Traversal (1M) & 3.2*  & N/A   & 17.6  & 29.2      & 3.9*  & N/A   & 15.5  & 19.0  \\
    265 Other Traversal (1M)   & 3.3*  & 23.9  & 17.7  & 32.8      & 3.9*  & 24.5  & 15.5  & 21.6  \\
    266 Cross Handler (1B)     & 6.5   & 0.9   & N/C   & 38.0      & 9.6   & 0.8   & N/C   & 32.1  \\
    267 Cross Finally (1B)     & 0.8   & N/A   & N/C   & 44.6      & 0.6   & N/A   & N/C   & 37.3  \\
    268 Match All (10M)        & 30.5  & 20.6  & 11.2  & 3.9       & 36.9  & 24.6  & 10.7  & 3.1   \\
    269 Match None (10M)       & 30.6  & 50.9  & 11.2  & 5.0       & 36.9  & 71.9  & 10.7  & 4.1   \\
     194In cases where a feature is not supported by a language the test is skipped
     195for that language.
     196\PAB{Report all values.
     197
     198Similarly, if a test does not change between resumption
     199and termination in \CFA, then only one test is written and the result
     200was put into the termination column.
     201}
     202
     203% Raw Data:
     204% run-algol-a.sat
     205% ---------------
     206% Raise Empty   &  82687046678 &  291616256 &   3252824847 & 15422937623 & 14736271114 \\
     207% Raise D'tor   & 219933199603 &  297897792 & 223602799362 &         N/A &         N/A \\
     208% Raise Finally & 219703078448 &  298391745 &          N/A &         ... & 18923060958 \\
     209% Raise Other   & 296744104920 & 2854342084 & 112981255103 & 15475924808 & 21293137454 \\
     210% Cross Handler &      9256648 &   13518430 &       769328 &     3486252 &    31790804 \\
     211% Cross Finally &       769319 &        N/A &          N/A &     2272831 &    37491962 \\
     212% Match All     &   3654278402 &   47518560 &   3218907794 &  1296748192 &   624071886 \\
     213% Match None    &   4788861754 &   58418952 &   9458936430 &  1318065020 &   625200906 \\
     214%
     215% run-algol-thr-c
     216% ---------------
     217% Raise Empty   &   3757606400 &   36472972 &   3257803337 & 15439375452 & 14717808642 \\
     218% Raise D'tor   &  64546302019 &  102148375 & 223648121635 &         N/A &         N/A \\
     219% Raise Finally &  64671359172 &  103285005 &          N/A & 15442729458 & 18927008844 \\
     220% Raise Other   & 294143497130 & 2630130385 & 112969055576 & 15448220154 & 21279953424 \\
     221% Cross Handler &      9646462 &   11955668 &       769328 &     3453707 &    31864074 \\
     222% Cross Finally &       773412 &        N/A &          N/A &     2253825 &    37266476 \\
     223% Match All     &   3719462155 &   43294042 &   3223004977 &  1286054154 &   623887874 \\
     224% Match None    &   4971630929 &   55311709 &   9481225467 &  1310251289 &   623752624 \\
     225%
     226% run-algol-04-a
     227% --------------
     228% Raise Empty   & 0.0 & 0.0 &  3250260945 & 0.0 & 0.0 \\
     229% Raise D'tor   & 0.0 & 0.0 & 29017675113 & N/A & N/A \\
     230% Raise Finally & 0.0 & 0.0 &         N/A & 0.0 & 0.0 \\
     231% Raise Other   & 0.0 & 0.0 & 24411823773 & 0.0 & 0.0 \\
     232% Cross Handler & 0.0 & 0.0 &      769334 & 0.0 & 0.0 \\
     233% Cross Finally & 0.0 & N/A &         N/A & 0.0 & 0.0 \\
     234% Match All     & 0.0 & 0.0 &  3254283504 & 0.0 & 0.0 \\
     235% Match None    & 0.0 & 0.0 &  9476060146 & 0.0 & 0.0 \\
     236
     237\begin{tabular}{|l|c c c c c|}
     238\hline
     239              & \CFA (Terminate) & \CFA (Resume) & \Cpp & Java & Python \\
     240\hline
     241Raise Empty   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     242Raise D'tor   & 0.0 & 0.0 & 0.0 & N/A & N/A \\
     243Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\
     244Raise Other   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     245Cross Handler & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     246Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\
     247Match All     & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     248Match None    & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    270249\hline
    271250\end{tabular}
    272 \end{table}
    273 
    274 \begin{table}[htb]
    275 \centering
    276 \caption{Resumption Performance Results (sec)}
    277 \label{t:PerformanceResumption}
    278 \begin{tabular}{|r||r||r|}
    279 \hline
    280 N\hspace{8pt}
    281                         & AMD     & ARM  \\
    282 \hline
    283 Empty Traversal (10M)   & 1.4     & 1.2  \\
    284 D'tor Traversal (10M)   & 1.8     & 1.0  \\
    285 Finally Traversal (10M) & 1.8     & 1.0  \\
    286 Other Traversal (10M)   & 22.6    & 25.8 \\
    287 Cross Handler (1B)      & 9.0     & 11.9 \\
    288 Match All (100M)        & 2.3     & 3.2  \\
    289 Match None (100M)       & 3.0     & 3.8  \\
     251
     252% run-plg7a-a.sat
     253% ---------------
     254% Raise Empty   &  57169011329 &  296612564 &   2788557155 & 17511466039 & 23324548496 \\
     255% Raise D'tor   & 150599858014 &  318443709 & 149651693682 &         N/A &         N/A \\
     256% Raise Finally & 148223145000 &  373325807 &          N/A &         ... & 29074552998 \\
     257% Raise Other   & 189463708732 & 3017109322 &  85819281694 & 17584295487 & 32602686679 \\
     258% Cross Handler &      8001654 &   13584858 &      1555995 &     6626775 &    41927358 \\
     259% Cross Finally &      1002473 &        N/A &          N/A &     4554344 &    51114381 \\
     260% Match All     &   3162460860 &   37315018 &   2649464591 &  1523205769 &   742374509 \\
     261% Match None    &   4054773797 &   47052659 &   7759229131 &  1555373654 &   744656403 \\
     262%
     263% run-plg7a-thr-a
     264% ---------------
     265% Raise Empty   &   3604235388 &   29829965 &   2786931833 & 17576506385 & 23352975105 \\
     266% Raise D'tor   &  46552380948 &  178709605 & 149834207219 &         N/A &         N/A \\
     267% Raise Finally &  46265157775 &  177906320 &          N/A & 17493045092 & 29170962959 \\
     268% Raise Other   & 195659245764 & 2376968982 &  86070431924 & 17552979675 & 32501882918 \\
     269% Cross Handler &    397031776 &   12503552 &      1451225 &     6658628 &    42304965 \\
     270% Cross Finally &      1136746 &        N/A &          N/A &     4468799 &    46155817 \\
     271% Match All     &   3189512499 &   39124453 &   2667795989 &  1525889031 &   733785613 \\
     272% Match None    &   4094675477 &   48749857 &   7850618572 &  1566713577 &   733478963 \\
     273%
     274% run-plg7a-04-a
     275% --------------
     276% 0.0 are unfilled.
     277% Raise Empty   & 0.0 & 0.0 &  2770781479 & 0.0 & 0.0 \\
     278% Raise D'tor   & 0.0 & 0.0 & 23530084907 & N/A & N/A \\
     279% Raise Finally & 0.0 & 0.0 &         N/A & 0.0 & 0.0 \\
     280% Raise Other   & 0.0 & 0.0 & 23816827982 & 0.0 & 0.0 \\
     281% Cross Handler & 0.0 & 0.0 &     1422188 & 0.0 & 0.0 \\
     282% Cross Finally & 0.0 & N/A &         N/A & 0.0 & 0.0 \\
     283% Match All     & 0.0 & 0.0 &  2671989778 & 0.0 & 0.0 \\
     284% Match None    & 0.0 & 0.0 &  7829059869 & 0.0 & 0.0 \\
     285
     286% PLG7A (in seconds)
     287\begin{tabular}{|l|c c c c c|}
     288\hline
     289              & \CFA (Terminate) & \CFA (Resume) & \Cpp & Java & Python \\
     290\hline
     291Raise Empty   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     292Raise D'tor   & 0.0 & 0.0 & 0.0 & N/A & N/A \\
     293Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\
     294Raise Other   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     295Cross Handler & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     296Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\
     297Match All     & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     298Match None    & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    290299\hline
    291300\end{tabular}
    292 \end{table}
    293 
    294 \begin{table}[htb]
    295 \centering
    296 \small
    297 \caption{Resumption/Fixup Routine Comparison (sec)}
    298 \label{t:PerformanceFixupRoutines}
    299 \setlength{\tabcolsep}{5pt}
    300 \begin{tabular}{|r|*{2}{|r r r r r|}}
    301 \hline
    302             & \multicolumn{5}{c||}{AMD}     & \multicolumn{5}{c|}{ARM}  \\
    303 \cline{2-11}
    304 N\hspace{8pt}       & \multicolumn{1}{c}{Raise} & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c||}{Python} &
    305               \multicolumn{1}{c}{Raise} & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c|}{Python} \\
    306 \hline
    307 Resume Empty (10M)  & 1.4 & 1.4 & 15.4 & 2.3 & 178.0  & 1.2 & 1.2 & 8.9 & 1.2 & 118.4 \\
    308 \hline
    309 \end{tabular}
    310 \end{table}
    311 
    312 % Now discuss the results in the tables.
    313 One result not directly related to \CFA but important to keep in mind is that,
    314 for exceptions, the standard intuition about which languages should go
    315 faster often does not hold.
    316 For example, there are a few cases where Python out-performs
    317 \CFA, \Cpp and Java.
    318 % To be exact, the Match All and Match None cases.
    319 The most likely explanation is that
    320 the generally faster languages have made ``common cases fast" at the expense
    321 of the rarer cases. Since exceptions are considered rare, they are made
    322 expensive to help speed up common actions, such as entering and leaving try
    323 statements.
    324 Python, on the other hand, while generally slower than the other languages,
    325 uses exceptions more and has not sacrificed their performance.
    326 In addition, languages with high-level representations have a much
    327 easier time scanning the stack as there is less to decode.
    328 
    329 As stated,
    330 the performance tests are not attempting to show \CFA has a new competitive
    331 way of implementing exception handling.
    332 The only performance requirement is to insure the \CFA EHM has reasonable
    333 performance for prototyping.
    334 Although that may be hard to exactly quantify, I believe it has succeeded
    335 in that regard.
    336 Details on the different test cases follow.
    337 
    338 \subsection{Termination \texorpdfstring{(\autoref{t:PerformanceTermination})}{}}
    339 
    340 \begin{description}
    341 \item[Empty Traversal]
    342 \CFA is slower than \Cpp, but is still faster than the other languages
    343 and closer to \Cpp than other languages.
    344 This result is to be expected,
    345 as \CFA is closer to \Cpp than the other languages.
    346 
    347 \item[D'tor Traversal]
    348 Running destructors causes a huge slowdown in the two languages that support
    349 them. \CFA has a higher proportionate slowdown but it is similar to \Cpp's.
    350 Considering the amount of work done in destructors is effectively zero
    351 (an assembly comment), the cost
    352 must come from the change of context required to run the destructor.
    353 
    354 \item[Finally Traversal]
    355 Performance is similar to Empty Traversal in all languages that support finally
    356 clauses. Only Python seems to have a larger than random noise change in
    357 its run time and it is still not large.
    358 Despite the similarity between finally clauses and destructors,
    359 finally clauses seem to avoid the spike that run time destructors have.
    360 Possibly some optimization removes the cost of changing contexts.
    361 
    362 \item[Other Traversal]
    363 For \Cpp, stopping to check if a handler applies seems to be about as
    364 expensive as stopping to run a destructor.
    365 This results in a significant jump.
    366 
    367 Other languages experience a small increase in run time.
    368 The small increase likely comes from running the checks,
    369 but they could avoid the spike by not having the same kind of overhead for
    370 switching to the check's context.
    371 
    372 \item[Cross Handler]
    373 Here, \CFA falls behind \Cpp by a much more significant margin.
    374 This is likely due to the fact that \CFA has to insert two extra function
    375 calls, while \Cpp does not have to execute any other instructions.
    376 Python is much further behind.
    377 
    378 \item[Cross Finally]
    379 \CFA's performance now matches \Cpp's from Cross Handler.
    380 If the code from the finally clause is being inlined,
    381 which is just an asm comment, than there are no additional instructions
    382 to execute again when exiting the try statement normally.
    383 
    384 \item[Conditional Match]
    385 Both of the conditional matching tests can be considered on their own.
    386 However, for evaluating the value of conditional matching itself, the
    387 comparison of the two sets of results is useful.
    388 Consider the massive jump in run time for \Cpp going from match all to match
    389 none, which none of the other languages have.
    390 Some strange interaction is causing run time to more than double for doing
    391 twice as many raises.
    392 Java and Python avoid this problem and have similar run time for both tests,
    393 possibly through resource reuse or their program representation.
    394 However, \CFA is built like \Cpp, and avoids the problem as well.
    395 This matches
    396 the pattern of the conditional match, which makes the two execution paths
    397 very similar.
    398 
    399 \end{description}
    400 
    401 \subsection{Resumption \texorpdfstring{(\autoref{t:PerformanceResumption})}{}}
    402 
    403 Moving on to resumption, there is one general note:
    404 resumption is \textit{fast}. The only test where it fell
    405 behind termination is Cross Handler.
    406 In every other case, the number of iterations had to be increased by a
    407 factor of 10 to get the run time in an appropriate range
    408 and in some cases resumption still took less time.
    409 
    410 % I tried \paragraph and \subparagraph, maybe if I could adjust spacing
    411 % between paragraphs those would work.
    412 \begin{description}
    413 \item[Empty Traversal]
    414 See above for the general speed-up notes.
    415 This result is not surprising as resumption's linked-list approach
    416 means that traversing over stack frames without a resumption handler is
    417 $O(1)$.
    418 
    419 \item[D'tor Traversal]
    420 Resumption does have the same spike in run time that termination has.
    421 The run time is actually very similar to Finally Traversal.
    422 As resumption does not unwind the stack, both destructors and finally
    423 clauses are run while walking down the stack during the recursive returns.
    424 So it follows their performance is similar.
    425 
    426 \item[Finally Traversal]
    427 Same as D'tor Traversal,
    428 except termination did not have a spike in run time on this test case.
    429 
    430 \item[Other Traversal]
    431 Traversing across handlers reduces resumption's advantage as it actually
    432 has to stop and check each one.
    433 Resumption still came out ahead (adjusting for iterations) but by much less
    434 than the other cases.
    435 
    436 \item[Cross Handler]
    437 The only test case where resumption could not keep up with termination,
    438 although the difference is not as significant as many other cases.
    439 It is simply a matter of where the costs come from:
    440 both termination and resumption have some work to set up or tear down a
    441 handler. It just so happens that resumption's work is slightly slower.
    442 
    443 \item[Conditional Match]
    444 Resumption shows a slight slowdown if the exception is not matched
    445 by the first handler, which follows from the fact the second handler now has
    446 to be checked. However, the difference is not large.
    447 
    448 \end{description}
    449 
    450 \subsection{Resumption/Fixup \texorpdfstring{(\autoref{t:PerformanceFixupRoutines})}{}}
    451 
    452 Finally are the results of the resumption/fixup routine comparison.
    453 These results are surprisingly varied. It is possible that creating a closure
    454 has more to do with performance than passing the argument through layers of
    455 calls.
    456 At 100 stack frames, resumption and manual fixup routines have similar
    457 performance in \CFA.
    458 More experiments could try to tease out the exact trade-offs,
    459 but the prototype's only performance goal is to be reasonable.
    460 It is already in that range, and \CFA's fixup routine simulation is
    461 one of the faster simulations as well.
    462 Plus, exceptions add features and remove syntactic overhead,
    463 so even at similar performance, resumptions have advantages
    464 over fixup routines.
     301
     302One result not directly related to \CFA but important to keep in
     303mind is that, for exceptions, the standard intuition about which languages
     304should go faster often does not hold. For example, there are cases where Python out-performs
     305\Cpp and Java. The most likely explanation is that, since exceptions are
     306rarely considered to be the common case, the more optimized languages
     307make that case expense. In addition, languages with high-level
     308representations have a much easier time scanning the stack as there is less
     309to decode.
     310
     311This observation means that while \CFA does not actually keep up with Python in every
     312case, it is usually no worse than roughly half the speed of \Cpp. This performance is good
     313enough for the prototyping purposes of the project.
     314
     315The test case where \CFA falls short is Raise Other, the case where the
     316stack is unwound including a bunch of non-matching handlers.
     317This slowdown seems to come from missing optimizations.
     318
     319Importantly, there is a huge slowdown in \Cpp's results bringing that brings
     320\CFA's performance back in that roughly half speed area. However many other
     321\CFA benchmarks increase their run-time by a similar amount falling far
     322behind their \Cpp counter-parts.
     323
     324This suggests that the performance issue in Raise Other is just an
     325optimization not being applied. Later versions of gcc may be able to
     326optimize this case further, at least down to the half of \Cpp mark.
     327A \CFA compiler that directly produced assembly could do even better as it
     328would not have to work across some of \CFA's current abstractions, like
     329the try terminate function.
     330
     331Resumption exception handling is also incredibly fast. Often an order of
     332magnitude or two better than the best termination speed.
     333There is a simple explanation for this; traversing a linked list is much   
     334faster than examining and unwinding the stack. When resumption does not do as
     335well its when more try statements are used per raise. Updating the internal
     336linked list is not very expensive but it does add up.
     337
     338The relative speed of the Match All and Match None tests (within each
     339language) can also show the effectiveness conditional matching as compared
     340to catch and rethrow.
     341\begin{itemize}[nosep]
     342\item
     343Java and Python get similar values in both tests.
     344Between the interpreted code, a higher level representation of the call
     345stack and exception reuse it it is possible the cost for a second
     346throw can be folded into the first.
     347% Is this due to optimization?
     348\item
     349Both types of \CFA are slightly slower if there is not a match.
     350For termination this likely comes from unwinding a bit more stack through
     351libunwind instead of executing the code normally.
     352For resumption there is extra work in traversing more of the list and running
     353more checks for a matching exceptions.
     354% Resumption is a bit high for that but this is my best theory.
     355\item
     356Then there is \Cpp, which takes 2--3 times longer to catch and rethrow vs.
     357just the catch. This is very high, but it does have to repeat the same
     358process of unwinding the stack and may have to parse the LSDA of the function
     359with the catch and rethrow twice, once before the catch and once after the
     360rethrow.
     361% I spent a long time thinking of what could push it over twice, this is all
     362% I have to explain it.
     363\end{itemize}
     364The difference in relative performance does show that there are savings to
     365be made by performing the check without catching the exception.
  • doc/theses/andrew_beach_MMath/resumption-marking.fig

    rb7fd9daf rf95634e  
    88-2
    991200 2
     106 5985 1530 6165 3105
     111 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 1620 90 90 6075 1620 6075 1710
     121 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 2340 90 90 6075 2340 6075 2430
     131 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 3015 90 90 6075 3015 6075 3105
     142 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     15        1 1 1.00 60.00 120.00
     16         6075 1755 6075 2205
     172 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     18        1 1 1.00 60.00 120.00
     19         6075 2475 6075 2925
     20-6
     216 3465 1530 3645 3105
     221 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 1620 90 90 3555 1620 3555 1710
     231 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 2340 90 90 3555 2340 3555 2430
     241 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 3015 90 90 3555 3015 3555 3105
     252 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     26        1 1 1.00 60.00 120.00
     27         3555 1755 3555 2205
     282 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     29        1 1 1.00 60.00 120.00
     30         3555 2475 3555 2925
     31-6
     326 2115 1530 2295 3105
     331 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 1620 90 90 2205 1620 2205 1710
     341 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 2340 90 90 2205 2340 2205 2430
     351 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 3015 90 90 2205 3015 2205 3105
     362 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     37        1 1 1.00 60.00 120.00
     38         2205 1755 2205 2205
     392 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     40        1 1 1.00 60.00 120.00
     41         2205 2475 2205 2925
     42-6
    10431 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 1620 90 90 4905 1620 4905 1710
     441 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 3015 90 90 4905 3015 4905 3105
    11451 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 945 90 90 4905 945 4905 1035
    12461 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 2340 90 90 4905 2340 4905 2430
    13 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 1620 90 90 1665 1620 1665 1710
    14 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 2340 90 90 1665 2340 1665 2430
    15 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 3060 90 90 1665 3060 1665 3150
    16 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 1620 90 90 3195 1620 3195 1710
    17 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 2340 90 90 3195 2340 3195 2430
    18 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 3060 90 90 3195 3060 3195 3150
    19 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 1620 90 90 6525 1620 6525 1710
    20 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 2340 90 90 6525 2340 6525 2430
    21 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 3060 90 90 4905 3060 4905 3150
    22 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 3060 90 90 6525 3060 6525 3150
     472 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     48        1 1 1.00 60.00 120.00
     49         2790 1620 2430 1620
     502 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     51        1 1 1.00 60.00 120.00
     52         4095 2340 3735 2340
     532 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     54        1 1 1.00 60.00 120.00
     55         6660 1620 6300 1620
     562 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
     57        1 1 1.00 60.00 120.00
     58         5490 945 5130 945
    23592 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    2460        1 1 1.00 60.00 120.00
     
    3066        1 1 1.00 60.00 120.00
    3167         4770 1080 4590 1260 4590 2070 4770 2250
    32 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    33         1 1 1.00 60.00 120.00
    34          1665 1755 1665 2205
    35 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    36         1 1 1.00 60.00 120.00
    37          1665 2475 1665 2925
    38 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    39         1 1 1.00 60.00 120.00
    40          3195 1755 3195 2205
    41 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    42         1 1 1.00 60.00 120.00
    43          3195 2475 3195 2925
    44 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    45         1 1 1.00 60.00 120.00
    46          6525 1755 6525 2205
    47 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    48         1 1 1.00 60.00 120.00
    49          6525 2475 6525 2925
    50 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    51         1 1 1.00 60.00 120.00
    52          1260 1620 1485 1620
    53 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    54         1 1 1.00 60.00 120.00
    55          1980 1440 1755 1440
    56 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    57         1 1 1.00 60.00 120.00
    58          2790 2340 3015 2340
    59 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    60         1 1 1.00 60.00 120.00
    61          3600 1620 3375 1620
    62 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    63         1 1 1.00 60.00 120.00
    64          4500 945 4725 945
    65 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    66         1 1 1.00 60.00 120.00
    67          5265 765 5040 765
    68 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    69         1 1 1.00 60.00 120.00
    70          6120 1620 6345 1620
    71 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    72         1 1 1.00 60.00 120.00
    73          6840 1440 6615 1440
    74 4 1 0 50 -1 0 12 0.0000 0 135 1170 1665 3375 Initial State\001
    75 4 1 0 50 -1 0 12 0.0000 0 135 1170 3195 3375 Found Handler\001
    76 4 1 0 50 -1 0 12 0.0000 0 165 1530 6570 3375 Handling Complete\001
    77 4 2 0 50 -1 0 12 0.0000 0 135 720 1485 2385 handlers\001
    78 4 1 0 50 -1 0 12 0.0000 0 135 900 4905 3375 Handler in\001
    79 4 1 0 50 -1 0 12 0.0000 0 165 810 4905 3600 Try block\001
    80 4 0 0 50 -1 0 12 0.0000 0 135 360 855 1665 head\001
    81 4 0 0 50 -1 0 12 0.0000 4 120 810 2025 1485 execution\001
    82 4 0 0 50 -1 0 12 0.0000 0 135 360 2385 2385 head\001
    83 4 0 0 50 -1 0 12 0.0000 4 120 810 3645 1665 execution\001
    84 4 0 0 50 -1 0 12 0.0000 0 135 360 4095 990 head\001
    85 4 0 0 50 -1 0 12 0.0000 4 120 810 5310 810 execution\001
    86 4 0 0 50 -1 0 12 0.0000 0 135 360 5715 1665 head\001
    87 4 0 0 50 -1 0 12 0.0000 4 120 810 6885 1485 execution\001
     684 0 0 50 -1 0 12 0.0000 4 135 1170 1980 3375 Initial State\001
     694 0 0 50 -1 0 12 0.0000 4 135 1170 3420 3375 Found Handler\001
     704 0 0 50 -1 0 12 0.0000 4 165 810 4770 3375 Try block\001
     714 0 0 50 -1 0 12 0.0000 4 135 900 4770 3555 in Handler\001
     724 0 0 50 -1 0 12 0.0000 4 165 1530 5940 3375 Handling Complete\001
  • doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex

    rb7fd9daf rf95634e  
    129129\begin{center}\textbf{Abstract}\end{center}
    130130
    131 The \CFA (Cforall) programming language is an evolutionary refinement of
    132 the C programming language, adding modern programming features without
    133 changing the programming paradigms of C.
    134 One of these modern programming features is more powerful error handling
    135 through the addition of an exception handling mechanism (EHM).
    136 
    137 This thesis covers the design and implementation of the \CFA EHM,
    138 along with a review of the other required \CFA features.
    139 The EHM includes common features of termination exception handling,
    140 which abandons and recovers from an operation,
    141 and similar support for resumption exception handling,
    142 which repairs and continues with an operation.
    143 The design of both has been adapted to utilize other tools \CFA provides,
    144 as well as fit with the assertion based interfaces of the language.
    145 
    146 The EHM has been implemented into the \CFA compiler and run-time environment.
    147 Although it has not yet been optimized, performance testing has shown it has
    148 comparable performance to other EHMs,
    149 which is sufficient for use in current \CFA programs.
     131This is the abstract.
    150132
    151133\cleardoublepage
     
    156138\begin{center}\textbf{Acknowledgements}\end{center}
    157139
    158 As is tradition and his due, I would like to begin by thanking my
    159 supervisor Peter Buhr. From accepting me in a first place,
    160 to helping me run performance tests, I would not be here without him.
    161 Also if there was an ``artist" field here he would be listed there as well,
    162 he helped me a lot with the diagrams.
    163 
    164 I would like to thank the readers
    165 Gregor Richards and Yizhou Zhang
    166 for their feedback and approval.
    167 The presentation of the thesis has definitely been improved with their
    168 feedback.
    169 
    170 I also thank the entire Cforall Team who built the rest of the
    171 \CFA compiler. From the existing features I used in my work, to the
    172 internal tooling that makes further development easier and the optimizations
    173 that make running tests pass by quickly.
    174 This includes: Aaron Moss, Rob Schluntz, Thierry Delisle, Michael Brooks,
    175 Mubeen Zulfieqar \& Fangren Yu.
    176 
    177 And thank-you Henry Xue, the co-op student who
    178 converted my macro implementation of exception declarations into
    179 the compiler features presented in this thesis.
    180 
    181 Finally I thank my family, who are still relieved I learned how to read.
    182 
     140I would like to thank all the little people who made this thesis possible.
    183141\cleardoublepage
    184142
  • doc/theses/andrew_beach_MMath/uw-ethesis.bib

    rb7fd9daf rf95634e  
    11% Bibliography of key references for "LaTeX for Thesis and Large Documents"
    22% For use with BibTeX
    3 % The online reference does not seem to be supported here.
    43
    5 @misc{Dice21,
    6     author      = {Dave Dice},
    7     year        = 2021,
    8     month       = aug,
    9     howpublished= {personal communication}
     4@book{goossens.book,
     5        author =        "Michel Goossens and Frank Mittelbach and
     6                         Alexander Samarin",
     7        title =         "The \LaTeX\ Companion",
     8        year =          "1994",
     9        publisher =     "Addison-Wesley",
     10        address =       "Reading, Massachusetts"
    1011}
    1112
    12 @misc{CforallExceptionBenchmarks,
    13     contributer = {pabuhr@plg},
    14     key         = {Cforall Exception Benchmarks},
    15     author      = {{\textsf{C}{$\mathbf{\forall}$} Exception Benchmarks}},
    16     howpublished= {\href{https://github.com/cforall/ExceptionBenchmarks_SPE20}{https://\-github.com/\-cforall/\-ExceptionBenchmarks\_SPE20}},
     13@book{knuth.book,
     14        author =        "Donald Knuth",
     15        title =         "The \TeX book",
     16        year =          "1986",
     17        publisher =     "Addison-Wesley",
     18        address =       "Reading, Massachusetts"
    1719}
    1820
    19 % Could not get `#the-for-statement` to work.
    20 @misc{PythonForLoop,
    21     author={Python Software Foundation},
    22     key={Python Compound Statements},
    23     howpublished={\href{https://docs.python.org/3/reference/compound_stmts.html}{https://\-docs.python.org/\-3/\-reference/\-compound\_stmts.html}},
    24     addendum={Accessed 2021-08-30},
     21@book{lamport.book,
     22        author =        "Leslie Lamport",
     23        title =         "\LaTeX\ --- A Document Preparation System",
     24        edition =       "Second",
     25        year =          "1994",
     26        publisher =     "Addison-Wesley",
     27        address =       "Reading, Massachusetts"
    2528}
    26 
    27 % Again, I would like this to have `#StopIteration`.
    28 @misc{PythonExceptions,
    29     author={Python Software Foundation},
    30     key={Python Exceptions},
    31     howpublished={\href{https://docs.python.org/3/library/exceptions.html}{https://\-docs.python.org/\-3/\-library/\-exceptions.html}},
    32     addendum={Accessed 2021-08-30},
    33 }
    34 
    35 @misc{CppHistory,
    36     author={C++ Community},
    37     key={Cpp Reference History},
    38     howpublished={\href{https://en.cppreference.com/w/cpp/language/history}{https://\-en.cppreference.com/\-w/\-cpp/\-language/\-history}},
    39     addendum={Accessed 2021-08-30},
    40 }
    41 
    42 @misc{CppExceptSpec,
    43     author={C++ Community},
    44     key={Cpp Reference Exception Specification},
    45     howpublished={\href{https://en.cppreference.com/w/cpp/language/except_spec}{https://\-en.cppreference.com/\-w/\-cpp/\-language/\-except\_spec}},
    46     addendum={Accessed 2021-09-08},
    47 }
    48 
    49 @misc{RustPanicMacro,
    50     author={The Rust Team},
    51     key={Rust Panic Macro},
    52     howpublished={\href{https://doc.rust-lang.org/std/macro.panic.html}{https://\-doc.rust-lang.org/\-std/\-macro.panic.html}},
    53     addendum={Accessed 2021-08-31},
    54 }
    55 
    56 @misc{RustPanicModule,
    57     author={The Rust Team},
    58     key={Rust Panic Module},
    59     howpublished={\href{https://doc.rust-lang.org/std/panic/index.html}{https://\-doc.rust-lang.org/\-std/\-panic/\-index.html}},
    60     addendum={Accessed 2021-08-31},
    61 }
    62 
    63 @manual{Go:2021,
    64     keywords={Go programming language},
    65     author={Robert Griesemer and Rob Pike and Ken Thompson},
    66     title={{Go} Programming Language},
    67     organization={Google},
    68     year=2021,
    69     note={\href{http://golang.org/ref/spec}{http://\-golang.org/\-ref/\-spec}},
    70     addendum={Accessed 2021-08-31},
    71 }
  • doc/theses/andrew_beach_MMath/uw-ethesis.tex

    rb7fd9daf rf95634e  
    210210\lstMakeShortInline@
    211211\lstset{language=CFA,style=cfacommon,basicstyle=\linespread{0.9}\tt}
     212% PAB causes problems with inline @=
     213%\lstset{moredelim=**[is][\protect\color{red}]{@}{@}}
    212214% Annotations from Peter:
    213215\newcommand{\PAB}[1]{{\color{blue}PAB: #1}}
  • doc/theses/andrew_beach_MMath/vtable-layout.fig

    rb7fd9daf rf95634e  
    88-2
    991200 2
     102 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
     11         1620 1665
    10122 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
    1113         3510 1890 3645 1755
     
    14162 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
    1517         3645 1305 3645 1755
    16 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
    17          2115 1935 2250 1935
    18 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
    19          2250 1170 2115 1170 2115 2475 2250 2475
    20 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
    21          2250 1350 2115 1350
    22184 0 0 50 -1 0 12 0.0000 4 165 630 2295 1305 type_id\001
    23194 0 0 50 -1 0 12 0.0000 4 165 1170 2295 1500 parent_field0\001
  • doc/theses/mubeen_zulfiqar_MMath/allocator.tex

    rb7fd9daf rf95634e  
    2424\end{itemize}
    2525
    26 The new features added to uHeapLmmm (incl. @malloc\_size@ routine)
     26The new features added to uHeapLmmm (incl. @malloc_size@ routine)
    2727\CFA alloc interface with examples.
    28 
    2928\begin{itemize}
    3029\item
     
    118117We added a few more features and routines to the allocator's C interface that can make the allocator more usable to the programmers. THese features will programmer more control on the dynamic memory allocation.
    119118
    120 \subsubsection void * aalloc( size\_t dim, size\_t elemSize )
     119\subsubsection void * aalloc( size_t dim, size_t elemSize )
    121120aalloc is an extension of malloc. It allows programmer to allocate a dynamic array of objects without calculating the total size of array explicitly. The only alternate of this routine in the other allocators is calloc but calloc also fills the dynamic memory with 0 which makes it slower for a programmer who only wants to dynamically allocate an array of objects without filling it with 0.
    122121\paragraph{Usage}
    123122aalloc takes two parameters.
    124 
    125123\begin{itemize}
    126124\item
     
    131129It returns address of dynamic object allocatoed on heap that can contain dim number of objects of the size elemSize. On failure, it returns NULL pointer.
    132130
    133 \subsubsection void * resize( void * oaddr, size\_t size )
     131\subsubsection void * resize( void * oaddr, size_t size )
    134132resize is an extension of relloc. It allows programmer to reuse a cuurently allocated dynamic object with a new size requirement. Its alternate in the other allocators is realloc but relloc also copy the data in old object to the new object which makes it slower for the programmer who only wants to reuse an old dynamic object for a new size requirement but does not want to preserve the data in the old object to the new object.
    135133\paragraph{Usage}
    136134resize takes two parameters.
    137 
    138135\begin{itemize}
    139136\item
     
    144141It returns an object that is of the size given but it does not preserve the data in the old object. On failure, it returns NULL pointer.
    145142
    146 \subsubsection void * resize( void * oaddr, size\_t nalign, size\_t size )
     143\subsubsection void * resize( void * oaddr, size_t nalign, size_t size )
    147144This resize is an extension of the above resize (FIX ME: cite above resize). In addition to resizing the size of of an old object, it can also realign the old object to a new alignment requirement.
    148145\paragraph{Usage}
    149146This resize takes three parameters. It takes an additional parameter of nalign as compared to the above resize (FIX ME: cite above resize).
    150 
    151147\begin{itemize}
    152148\item
     
    159155It returns an object with the size and alignment given in the parameters. On failure, it returns a NULL pointer.
    160156
    161 \subsubsection void * amemalign( size\_t alignment, size\_t dim, size\_t elemSize )
     157\subsubsection void * amemalign( size_t alignment, size_t dim, size_t elemSize )
    162158amemalign is a hybrid of memalign and aalloc. It allows programmer to allocate an aligned dynamic array of objects without calculating the total size of the array explicitly. It frees the programmer from calculating the total size of the array.
    163159\paragraph{Usage}
    164160amemalign takes three parameters.
    165 
    166161\begin{itemize}
    167162\item
     
    174169It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment. On failure, it returns NULL pointer.
    175170
    176 \subsubsection void * cmemalign( size\_t alignment, size\_t dim, size\_t elemSize )
     171\subsubsection void * cmemalign( size_t alignment, size_t dim, size_t elemSize )
    177172cmemalign is a hybrid of amemalign and calloc. It allows programmer to allocate an aligned dynamic array of objects that is 0 filled. The current way to do this in other allocators is to allocate an aligned object with memalign and then fill it with 0 explicitly. This routine provides both features of aligning and 0 filling, implicitly.
    178173\paragraph{Usage}
    179174cmemalign takes three parameters.
    180 
    181175\begin{itemize}
    182176\item
     
    189183It returns a dynamic array of objects that has the capacity to contain dim number of objects of the size of elemSize. The returned dynamic array is aligned to the given alignment and is 0 filled. On failure, it returns NULL pointer.
    190184
    191 \subsubsection size\_t malloc\_alignment( void * addr )
    192 malloc\_alignment returns the alignment of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required alignment.
    193 \paragraph{Usage}
    194 malloc\_alignment takes one parameters.
    195 
     185\subsubsection size_t malloc_alignment( void * addr )
     186malloc_alignment returns the alignment of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required alignment.
     187\paragraph{Usage}
     188malloc_alignment takes one parameters.
    196189\begin{itemize}
    197190\item
    198191addr: the address of the currently allocated dynamic object.
    199192\end{itemize}
    200 malloc\_alignment returns the alignment of the given dynamic object. On failure, it return the value of default alignment of the uHeapLmmm allocator.
    201 
    202 \subsubsection bool malloc\_zero\_fill( void * addr )
    203 malloc\_zero\_fill returns whether a currently allocated dynamic object was initially zero filled at the time of allocation. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verifying the zero filled property of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was zero filled at the time of allocation.
    204 \paragraph{Usage}
    205 malloc\_zero\_fill takes one parameters.
    206 
     193malloc_alignment returns the alignment of the given dynamic object. On failure, it return the value of default alignment of the uHeapLmmm allocator.
     194
     195\subsubsection bool malloc_zero_fill( void * addr )
     196malloc_zero_fill returns whether a currently allocated dynamic object was initially zero filled at the time of allocation. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verifying the zero filled property of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was zero filled at the time of allocation.
     197\paragraph{Usage}
     198malloc_zero_fill takes one parameters.
    207199\begin{itemize}
    208200\item
    209201addr: the address of the currently allocated dynamic object.
    210202\end{itemize}
    211 malloc\_zero\_fill returns true if the dynamic object was initially zero filled and return false otherwise. On failure, it returns false.
    212 
    213 \subsubsection size\_t malloc\_size( void * addr )
    214 malloc\_size returns the allocation size of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required size. Its current alternate in the other allocators is malloc\_usable\_size. But, malloc\_size is different from malloc\_usable\_size as malloc\_usabe\_size returns the total data capacity of dynamic object including the extra space at the end of the dynamic object. On the other hand, malloc\_size returns the size that was given to the allocator at the allocation of the dynamic object. This size is updated when an object is realloced, resized, or passed through a similar allocator routine.
    215 \paragraph{Usage}
    216 malloc\_size takes one parameters.
    217 
     203malloc_zero_fill returns true if the dynamic object was initially zero filled and return false otherwise. On failure, it returns false.
     204
     205\subsubsection size_t malloc_size( void * addr )
     206malloc_size returns the allocation size of a currently allocated dynamic object. It allows the programmer in memory management and personal bookkeeping. It helps the programmer in verofying the alignment of a dynamic object especially in a scenerio similar to prudcer-consumer where a producer allocates a dynamic object and the consumer needs to assure that the dynamic object was allocated with the required size. Its current alternate in the other allocators is malloc_usable_size. But, malloc_size is different from malloc_usable_size as malloc_usabe_size returns the total data capacity of dynamic object including the extra space at the end of the dynamic object. On the other hand, malloc_size returns the size that was given to the allocator at the allocation of the dynamic object. This size is updated when an object is realloced, resized, or passed through a similar allocator routine.
     207\paragraph{Usage}
     208malloc_size takes one parameters.
    218209\begin{itemize}
    219210\item
    220211addr: the address of the currently allocated dynamic object.
    221212\end{itemize}
    222 malloc\_size returns the allocation size of the given dynamic object. On failure, it return zero.
    223 
    224 \subsubsection void * realloc( void * oaddr, size\_t nalign, size\_t size )
     213malloc_size returns the allocation size of the given dynamic object. On failure, it return zero.
     214
     215\subsubsection void * realloc( void * oaddr, size_t nalign, size_t size )
    225216This realloc is an extension of the default realloc (FIX ME: cite default realloc). In addition to reallocating an old object and preserving the data in old object, it can also realign the old object to a new alignment requirement.
    226217\paragraph{Usage}
    227218This realloc takes three parameters. It takes an additional parameter of nalign as compared to the default realloc.
    228 
    229219\begin{itemize}
    230220\item
     
    247237It returns a dynamic object of the size of type T. On failure, it return NULL pointer.
    248238
    249 \subsubsection T * aalloc( size\_t dim )
     239\subsubsection T * aalloc( size_t dim )
    250240This aalloc is a simplified polymorphic form of above aalloc (FIX ME: cite aalloc). It takes one parameter as compared to the above aalloc that takes two parameters.
    251241\paragraph{Usage}
    252242aalloc takes one parameters.
    253 
    254243\begin{itemize}
    255244\item
     
    258247It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. On failure, it return NULL pointer.
    259248
    260 \subsubsection T * calloc( size\_t dim )
     249\subsubsection T * calloc( size_t dim )
    261250This calloc is a simplified polymorphic form of defualt calloc (FIX ME: cite calloc). It takes one parameter as compared to the default calloc that takes two parameters.
    262251\paragraph{Usage}
    263252This calloc takes one parameter.
    264 
    265253\begin{itemize}
    266254\item
     
    269257It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. On failure, it return NULL pointer.
    270258
    271 \subsubsection T * resize( T * ptr, size\_t size )
     259\subsubsection T * resize( T * ptr, size_t size )
    272260This resize is a simplified polymorphic form of above resize (FIX ME: cite resize with alignment). It takes two parameters as compared to the above resize that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as CFA provides gives allocator the liberty to get the alignment of the returned type.
    273261\paragraph{Usage}
    274262This resize takes two parameters.
    275 
    276263\begin{itemize}
    277264\item
     
    282269It returns a dynamic object of the size given in paramters. The returned object is aligned to the alignemtn of type T. On failure, it return NULL pointer.
    283270
    284 \subsubsection T * realloc( T * ptr, size\_t size )
     271\subsubsection T * realloc( T * ptr, size_t size )
    285272This realloc is a simplified polymorphic form of defualt realloc (FIX ME: cite realloc with align). It takes two parameters as compared to the above realloc that takes three parameters. It frees the programmer from explicitly mentioning the alignment of the allocation as CFA provides gives allocator the liberty to get the alignment of the returned type.
    286273\paragraph{Usage}
    287274This realloc takes two parameters.
    288 
    289275\begin{itemize}
    290276\item
     
    295281It returns a dynamic object of the size given in paramters that preserves the data in the given object. The returned object is aligned to the alignemtn of type T. On failure, it return NULL pointer.
    296282
    297 \subsubsection T * memalign( size\_t align )
     283\subsubsection T * memalign( size_t align )
    298284This memalign is a simplified polymorphic form of defualt memalign (FIX ME: cite memalign). It takes one parameters as compared to the default memalign that takes two parameters.
    299285\paragraph{Usage}
    300286memalign takes one parameters.
    301 
    302287\begin{itemize}
    303288\item
     
    306291It returns a dynamic object of the size of type T that is aligned to given parameter align. On failure, it return NULL pointer.
    307292
    308 \subsubsection T * amemalign( size\_t align, size\_t dim )
     293\subsubsection T * amemalign( size_t align, size_t dim )
    309294This amemalign is a simplified polymorphic form of above amemalign (FIX ME: cite amemalign). It takes two parameter as compared to the above amemalign that takes three parameters.
    310295\paragraph{Usage}
    311296amemalign takes two parameters.
    312 
    313297\begin{itemize}
    314298\item
     
    319303It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. The returned object is aligned to the given parameter align. On failure, it return NULL pointer.
    320304
    321 \subsubsection T * cmemalign( size\_t align, size\_t dim  )
     305\subsubsection T * cmemalign( size_t align, size_t dim  )
    322306This cmemalign is a simplified polymorphic form of above cmemalign (FIX ME: cite cmemalign). It takes two parameter as compared to the above cmemalign that takes three parameters.
    323307\paragraph{Usage}
    324308cmemalign takes two parameters.
    325 
    326309\begin{itemize}
    327310\item
     
    332315It returns a dynamic object that has the capacity to contain dim number of objects, each of the size of type T. The returned object is aligned to the given parameter align and is zero filled. On failure, it return NULL pointer.
    333316
    334 \subsubsection T * aligned\_alloc( size\_t align )
    335 This aligned\_alloc is a simplified polymorphic form of defualt aligned\_alloc (FIX ME: cite aligned\_alloc). It takes one parameter as compared to the default aligned\_alloc that takes two parameters.
    336 \paragraph{Usage}
    337 This aligned\_alloc takes one parameter.
    338 
     317\subsubsection T * aligned_alloc( size_t align )
     318This aligned_alloc is a simplified polymorphic form of defualt aligned_alloc (FIX ME: cite aligned_alloc). It takes one parameter as compared to the default aligned_alloc that takes two parameters.
     319\paragraph{Usage}
     320This aligned_alloc takes one parameter.
    339321\begin{itemize}
    340322\item
     
    343325It returns a dynamic object of the size of type T that is aligned to the given parameter. On failure, it return NULL pointer.
    344326
    345 \subsubsection int posix\_memalign( T ** ptr, size\_t align )
    346 This posix\_memalign is a simplified polymorphic form of defualt posix\_memalign (FIX ME: cite posix\_memalign). It takes two parameters as compared to the default posix\_memalign that takes three parameters.
    347 \paragraph{Usage}
    348 This posix\_memalign takes two parameter.
    349 
     327\subsubsection int posix_memalign( T ** ptr, size_t align )
     328This posix_memalign is a simplified polymorphic form of defualt posix_memalign (FIX ME: cite posix_memalign). It takes two parameters as compared to the default posix_memalign that takes three parameters.
     329\paragraph{Usage}
     330This posix_memalign takes two parameter.
    350331\begin{itemize}
    351332\item
     
    354335align: required alignment of the dynamic object.
    355336\end{itemize}
    356 
    357337It stores address of the dynamic object of the size of type T in given parameter ptr. This object is aligned to the given parameter. On failure, it return NULL pointer.
    358338
     
    369349It returns a dynamic object of the size that is calcutaed by rouding the size of type T. The returned object is also aligned to the page size. On failure, it return NULL pointer.
    370350
    371 \subsection Alloc Interface
     351\subsection{Alloc Interface}
    372352In addition to improve allocator interface both for CFA and our standalone allocator uHeapLmmm in C. We also added a new alloc interface in CFA that increases usability of dynamic memory allocation.
    373353This interface helps programmers in three major ways.
    374 
    375354\begin{itemize}
    376355\item
     
    392371This is the only parameter in the alloc routine that has a fixed-position and it is also the only parameter that does not use a backtick function. It has to be passed at the first position to alloc call in-case of an array allocation of objects of type T.
    393372It represents the required number of members in the array allocation as in CFA's aalloc (FIX ME: cite aalloc).
    394 This parameter should be of type size\_t.
     373This parameter should be of type size_t.
    395374
    396375Example: int a = alloc( 5 )
     
    398377
    399378\paragraph{Align}
    400 This parameter is position-free and uses a backtick routine align (`align). The parameter passed with `align should be of type size\_t. If the alignment parameter is not a power of two or is less than the default alignment of the allocator (that can be found out using routine libAlign in CFA) then the passed alignment parameter will be rejected and the default alignment will be used.
     379This parameter is position-free and uses a backtick routine align (`align). The parameter passed with `align should be of type size_t. If the alignment parameter is not a power of two or is less than the default alignment of the allocator (that can be found out using routine libAlign in CFA) then the passed alignment parameter will be rejected and the default alignment will be used.
    401380
    402381Example: int b = alloc( 5 , 64`align )
     
    406385This parameter is position-free and uses a backtick routine fill (`fill). In case of realloc, only the extra space after copying the data in the old object will be filled with given parameter.
    407386Three types of parameters can be passed using `fill.
    408 
    409387\begin{itemize}
    410388\item
  • doc/theses/mubeen_zulfiqar_MMath/background.tex

    rb7fd9daf rf95634e  
    2323====================
    2424
    25 \section{Background}
    26 
    27 % FIXME: cite wasik
    28 \cite{wasik.thesis}
    29 
    30 \subsection{Memory Allocation}
    31 With dynamic allocation being an important feature of C, there are many standalone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators.
    32 
    33 \paragraph{dlmalloc}
    34 dlmalloc (FIX ME: cite allocator) is a thread-safe allocator that is single threaded and single heap. dlmalloc maintains free-lists of different sizes to store freed dynamic memory. (FIX ME: cite wasik)
    35 
    36 \paragraph{hoard}
    37 Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thred heaps that have thread-local free-lists, and a gloabl shared heap. (FIX ME: cite wasik)
    38 
    39 \paragraph{jemalloc}
    40 jemalloc (FIX ME: cite allocator) is a thread-safe allocator that uses multiple arenas. Each thread is assigned an arena. Each arena has chunks that contain contagious memory regions of same size. An arena has multiple chunks that contain regions of multiple sizes.
    41 
    42 \paragraph{ptmalloc}
    43 ptmalloc (FIX ME: cite allocator) is a modification of dlmalloc. It is a thread-safe multi-threaded memory allocator that uses multiple heaps. ptmalloc heap has similar design to dlmalloc's heap.
    44 
    45 \paragraph{rpmalloc}
    46 rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-calss contains memory regions of the relevant size.
    47 
    48 \paragraph{tbb malloc}
    49 tbb malloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses private heap for each thread. Each private-heap has multiple bins of different sizes. Each bin contains free regions of the same size.
    50 
    51 \paragraph{tc malloc}
    52 tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty.
    53 
    54 \subsection{Benchmarks}
    55 There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators.
    56 
    57 \paragraph{threadtest}
    58 (FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency.
    59 
    60 \paragraph{shbench}
    61 (FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator.
    62 
    63 \paragraph{larson}
    64 (FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance.
     25\cite{Wasik08}
  • doc/theses/mubeen_zulfiqar_MMath/benchmarks.tex

    rb7fd9daf rf95634e  
    149149*** FIX ME: Insert a figure of above benchmark with description
    150150
    151 \paragraph{Relevant Knobs}
     151\paragrpah{Relevant Knobs}
    152152*** FIX ME: Insert Relevant Knobs
    153153
  • doc/theses/mubeen_zulfiqar_MMath/intro.tex

    rb7fd9daf rf95634e  
    4747\begin{itemize}
    4848\item
    49 aligned\_alloc
     49aligned_alloc
    5050\item
    51 malloc\_usable\_size
     51malloc_usable_size
    5252\item
    5353memalign
    5454\item
    55 posix\_memalign
     55posix_memalign
    5656\item
    5757pvalloc
     
    6161
    6262With the rise of concurrent applications, memory allocators should be able to fulfill dynamic memory requests from multiple threads in parallel without causing contention on shared resources. There needs to be a set of a standard benchmarks that can be used to evaluate an allocator's performance in different scenerios.
     63
     64\section{Background}
     65
     66\subsection{Memory Allocation}
     67With dynamic allocation being an important feature of C, there are many standalone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators.
     68
     69\paragraph{dlmalloc}
     70dlmalloc (FIX ME: cite allocator) is a thread-safe allocator that is single threaded and single heap. dlmalloc maintains free-lists of different sizes to store freed dynamic memory. (FIX ME: cite wasik)
     71
     72\paragraph{hoard}
     73Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thred heaps that have thread-local free-lists, and a gloabl shared heap. (FIX ME: cite wasik)
     74
     75\paragraph{jemalloc}
     76jemalloc (FIX ME: cite allocator) is a thread-safe allocator that uses multiple arenas. Each thread is assigned an arena. Each arena has chunks that contain contagious memory regions of same size. An arena has multiple chunks that contain regions of multiple sizes.
     77
     78\paragraph{ptmalloc}
     79ptmalloc (FIX ME: cite allocator) is a modification of dlmalloc. It is a thread-safe multi-threaded memory allocator that uses multiple heaps. ptmalloc heap has similar design to dlmalloc's heap.
     80
     81\paragraph{rpmalloc}
     82rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-calss contains memory regions of the relevant size.
     83
     84\paragraph{tbb malloc}
     85tbb malloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses private heap for each thread. Each private-heap has multiple bins of different sizes. Each bin contains free regions of the same size.
     86
     87\paragraph{tc malloc}
     88tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty.
     89
     90\subsection{Benchmarks}
     91There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators.
     92
     93\paragraph{threadtest}
     94(FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency.
     95
     96\paragraph{shbench}
     97(FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator.
     98
     99\paragraph{larson}
     100(FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance.
    63101
    64102\section{Research Objectives}
  • doc/theses/mubeen_zulfiqar_MMath/performance.tex

    rb7fd9daf rf95634e  
    4444tc               &             &  \\
    4545\end{tabularx}
    46 
    47 %(FIX ME: complete table)
     46(FIX ME: complete table)
    4847
    4948\section{Experiment Environment}
  • doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.bib

    rb7fd9daf rf95634e  
    2727        address =       "Reading, Massachusetts"
    2828}
    29 
    30 @article{wasik.thesis,
    31     author        = "Ayelet Wasik",
    32     title         = "Features of A Multi-Threaded Memory Alloator",
    33     publisher     = "University of Waterloo",
    34     year          = "2008"
    35 }
  • doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.tex

    rb7fd9daf rf95634e  
    8484\usepackage{graphicx}
    8585\usepackage{comment} % Removes large sections of the document.
    86 \usepackage{tabularx}
    8786
    8887% Hyperlinks make it very easy to navigate an electronic document.
     
    192191% Tip: Putting each sentence on a new line is a way to simplify later editing.
    193192%----------------------------------------------------------------------
    194 \begin{sloppypar}
    195 
    196193\input{intro}
    197194\input{background}
     
    200197\input{performance}
    201198\input{conclusion}
    202 
    203 \end{sloppypar}
    204199
    205200%----------------------------------------------------------------------
  • doc/theses/thierry_delisle_PhD/.gitignore

    rb7fd9daf rf95634e  
    1313comp_II/presentation.pdf
    1414
    15 seminars/build/
    16 seminars/img/*.fig.bak
    17 seminars/*.pdf
    18 
    1915thesis/build/
    2016thesis/fig/*.fig.bak
  • doc/theses/thierry_delisle_PhD/thesis/Makefile

    rb7fd9daf rf95634e  
    2020        practice \
    2121        io \
    22         eval_micro \
    23         eval_macro \
    2422}}
    2523
     
    3735        pivot_ring \
    3836        system \
    39         cycle \
    4037}
    4138
  • doc/theses/thierry_delisle_PhD/thesis/thesis.tex

    rb7fd9daf rf95634e  
    11%======================================================================
    2 % University of Waterloo Thesis Template for LaTeX
    3 % Last Updated November, 2020
    4 % by Stephen Carr, IST Client Services,
     2% University of Waterloo Thesis Template for LaTeX 
     3% Last Updated November, 2020 
     4% by Stephen Carr, IST Client Services, 
    55% University of Waterloo, 200 University Ave. W., Waterloo, Ontario, Canada
    66% FOR ASSISTANCE, please send mail to request@uwaterloo.ca
     
    1515% Some important notes on using this template and making it your own...
    1616
    17 % The University of Waterloo has required electronic thesis submission since October 2006.
     17% The University of Waterloo has required electronic thesis submission since October 2006. 
    1818% See the uWaterloo thesis regulations at
    1919% https://uwaterloo.ca/graduate-studies/thesis.
    2020% This thesis template is geared towards generating a PDF version optimized for viewing on an electronic display, including hyperlinks within the PDF.
    2121
    22 % DON'T FORGET TO ADD YOUR OWN NAME AND TITLE in the "hyperref" package configuration below.
     22% DON'T FORGET TO ADD YOUR OWN NAME AND TITLE in the "hyperref" package configuration below. 
    2323% THIS INFORMATION GETS EMBEDDED IN THE PDF FINAL PDF DOCUMENT.
    2424% You can view the information if you view properties of the PDF document.
    2525
    26 % Many faculties/departments also require one or more printed copies.
    27 % This template attempts to satisfy both types of output.
     26% Many faculties/departments also require one or more printed copies. 
     27% This template attempts to satisfy both types of output. 
    2828% See additional notes below.
    2929% It is based on the standard "book" document class which provides all necessary sectioning structures and allows multi-part theses.
     
    3232
    3333% 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 
     34 
    3535% E.g. to process a thesis called "mythesis.tex" based on this template, run:
    3636
    3737% pdflatex mythesis     -- first pass of the pdflatex processor
    3838% bibtex mythesis       -- generates bibliography from .bib data file(s)
    39 % makeindex         -- should be run only if an index is used
     39% makeindex         -- should be run only if an index is used 
    4040% pdflatex mythesis     -- fixes numbering in cross-references, bibliographic references, glossaries, index, etc.
    4141% pdflatex mythesis     -- it takes a couple of passes to completely process all cross-references
    4242
    4343% 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.
     44% Then click the PDFLaTeX button two more times. 
    4545% If you have an index as well,you'll need to run MakeIndex from the Tools menu as well, before running pdflatex
    4646% the last two times.
     
    5151% Tip: Photographs should be cropped and compressed so as not to be too large.
    5252
    53 % To create a PDF output that is optimized for double-sided printing:
     53% To create a PDF output that is optimized for double-sided printing: 
    5454% 1) comment-out the \documentclass statement in the preamble below, and un-comment the second \documentclass line.
    5555% 2) change the value assigned below to the boolean variable "PrintVersion" from " false" to "true".
     
    6767% If you have to, it's easier to make changes to nomenclature once here than in a million places throughout your thesis!
    6868\newcommand{\package}[1]{\textbf{#1}} % package names in bold text
    69 \newcommand{\cmmd}[1]{\textbackslash\texttt{#1}} % command name in tt font
     69\newcommand{\cmmd}[1]{\textbackslash\texttt{#1}} % command name in tt font 
    7070\newcommand{\href}[1]{#1} % does nothing, but defines the command so the print-optimized version will ignore \href tags (redefined by hyperref pkg).
    7171%\newcommand{\texorpdfstring}[2]{#1} % does nothing, but defines the command
     
    235235\part{Evaluation}
    236236\label{Evaluation}
    237 % \chapter{Theoretical and Existance Proofs}
    238 \input{text/eval_micro.tex}
    239 \input{text/eval_macro.tex}
     237\chapter{Theoretical and Existance Proofs}
     238\chapter{Micro-Benchmarks}
     239\chapter{Larger-Scale applications}
    240240\part{Conclusion \& Annexes}
    241241
  • doc/user/user.tex

    rb7fd9daf rf95634e  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sun Oct 10 12:45:00 2021
    14 %% Update Count     : 5095
     13%% Last Modified On : Mon May 31 09:03:34 2021
     14%% Update Count     : 5071
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    44444444\CFA provides a fine-grained solution where a \Index{recursive lock} is acquired and released indirectly via a manipulator ©acquire© or instantiating an \Index{RAII} type specific for the kind of stream: ©osacquire©\index{ostream@©ostream©!osacquire@©osacquire©} for output streams and ©isacquire©\index{isacquire@©isacquire©}\index{istream@©istream©!isacquire@©isacquire©} for input streams.
    44454445
    4446 The common usage is the short form of the mutex statement\index{ostream@©ostream©!mutex@©mutex©} to lock a stream during a single cascaded I/O expression, \eg:
    4447 \begin{cfa}
    4448 $\emph{thread\(_1\)}$ : ®mutex()® sout | "abc " | "def ";
    4449 $\emph{thread\(_2\)}$ : ®mutex()® sout | "uvw " | "xyz ";
     4446The common usage is manipulator ©acquire©\index{ostream@©ostream©!acquire@©acquire©} to lock a stream during a single cascaded I/O expression, with the manipulator appearing as the first item in a cascade list, \eg:
     4447\begin{cfa}
     4448$\emph{thread\(_1\)}$ : sout | ®acquire® | "abc " | "def ";   // manipulator
     4449$\emph{thread\(_2\)}$ : sout | ®acquire® | "uvw " | "xyz ";
    44504450\end{cfa}
    44514451Now, the order of the thread execution is still non-deterministic, but the output is constrained to two possible lines in either order.
     
    44664466In summary, the stream lock is acquired by the ©acquire© manipulator and implicitly released at the end of the cascaded I/O expression ensuring all operations in the expression occur atomically.
    44674467
    4468 To lock a stream across multiple I/O operations, he long form of the mutex statement is used, \eg:
    4469 \begin{cfa}
    4470 ®mutex( sout )® {
     4468To lock a stream across multiple I/O operations, an object of type ©osacquire© or ©isacquire© is declared to implicitly acquire/release the stream lock providing mutual exclusion for the object's duration, \eg:
     4469\begin{cfa}
     4470{       // acquire sout for block duration
     4471        ®osacquire® acq = { sout };                             $\C{// named stream locker}$
    44714472        sout | 1;
    4472         ®mutex() sout® | 2 | 3;                         $\C{// unnecessary, but ok because of recursive lock}$
     4473        sout | ®acquire® | 2 | 3;                               $\C{// unnecessary, but ok to acquire and release again}$
    44734474        sout | 4;
    4474 } // implicitly release sout lock
    4475 \end{cfa}
    4476 Note, the unnecessary ©mutex© in the middle of the mutex statement, works because the recursive stream-lock can be acquired/released multiple times by the owner thread.
     4475}       // implicitly release the lock when "acq" is deallocated
     4476\end{cfa}
     4477Note, the unnecessary ©acquire© manipulator works because the recursive stream-lock can be acquired/released multiple times by the owner thread.
    44774478Hence, calls to functions that also acquire a stream lock for their output do not result in \Index{deadlock}.
    44784479
    44794480The previous values written by threads 1 and 2 can be read in concurrently:
    44804481\begin{cfa}
    4481 ®mutex( sin )® {
     4482{       // acquire sin lock for block duration
     4483        ®isacquire acq = { sin };®                              $\C{// named stream locker}$
    44824484        int x, y, z, w;
    44834485        sin | x;
    4484         ®mutex() sin® | y | z;                          $\C{// unnecessary, but ok because of recursive lock}$
     4486        sin | ®acquire® | y | z;                                $\C{// unnecessary, but ok to acquire and release again}$
    44854487        sin | w;
    4486 } // implicitly release sin lock
     4488}       // implicitly release the lock when "acq" is deallocated
    44874489\end{cfa}
    44884490Again, the order of the reading threads is non-deterministic.
     
    44914493\Textbf{WARNING:} The general problem of \Index{nested locking} can occur if routines are called in an I/O sequence that block, \eg:
    44924494\begin{cfa}
    4493 ®mutex() sout® | "data:" | rtn( mon );  $\C{// mutex call on monitor}$
     4495sout | ®acquire® | "data:" | rtn( mon );        $\C{// mutex call on monitor}$
    44944496\end{cfa}
    44954497If the thread executing the I/O expression blocks in the monitor with the ©sout© lock, other threads writing to ©sout© also block until the thread holding the lock is unblocked and releases it.
     
    44984500\begin{cfa}
    44994501int ®data® = rtn( mon );
    4500 mutex() sout | "data:" | ®data®;
     4502sout | acquire | "data:" | ®data®;
    45014503\end{cfa}
    45024504
     
    45044506\section{String Stream}
    45054507
    4506 The stream types ©ostrstream© and ©istrstream© provide all the stream formatting capabilities to/from a C string rather than a stream file.
    4507 \VRef[Figure]{f:StringStreamProcessing} shows writing (output) to and reading (input) from a C string.
    4508 The only string stream operations different from a file stream are:
    4509 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
    4510 \item
    4511 constructors to create a stream that writes to a write buffer (©ostrstream©) of ©size©, or reads from a read buffer (©istrstream©) containing a C string terminated with ©'\0'©.
    4512 \begin{cfa}
    4513 void ?{}( ostrstream &, char buf[], size_t size );
    4514 void ?{}( istrstream & is, char buf[] );
    4515 \end{cfa}
    4516 \item
    4517 \Indexc{write} (©ostrstream© only) writes all the buffered characters to the specified stream (©stdout© default).
    4518 \begin{cfa}
    4519 ostrstream & write( ostrstream & os, FILE * stream = stdout );
    4520 \end{cfa}
    4521 There is no ©read© for ©istrstream©.
    4522 \end{itemize}
    4523 
     4508All the stream formatting capabilities are available to format text to/from a C string rather than to a stream file.
     4509\VRef[Figure]{f:StringStreamProcessing} shows writing (output) and reading (input) from a C string.
    45244510\begin{figure}
    45254511\begin{cfa}
     
    45344520        double x = 12345678.9, y = 98765.4321e-11;
    45354521
    4536         osstr | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)) | "abc";
    4537         write( osstr ); $\C{// write string to stdout}$
    4538         printf( "%s", buf ); $\C{// same lines of output}$
    4539         sout | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)) | "abc";
    4540 
    4541         char buf2[] = "12 14 15 3.5 7e4 abc"; $\C{// input buffer}$
     4522        osstr | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y)); $\C{// same lines of output}$
     4523        write( osstr );
     4524        printf( "%s", buf );
     4525        sout | i | hex(j) | wd(10, k) | sci(x) | unit(eng(y));
     4526
     4527        char buf2[] = "12 14 15 3.5 7e4"; $\C{// input buffer}$
    45424528        ®istrstream isstr = { buf2 };®
    4543         char s[10];
    4544         isstr | i | j | k | x | y | s;
    4545         sout  | i | j | k | x | y | s;
    4546 }
    4547 
    4548 3 0x5          7 1.234568e+07 987.654n abc
    4549 3 0x5          7 1.234568e+07 987.654n abc
    4550 3 0x5          7 1.234568e+07 987.654n abc
    4551 12 14 15 3.5 70000. abc
     4529        isstr | i | j | k | x | y;
     4530        sout | i | j | k | x | y;
     4531}
    45524532\end{cfa}
    45534533\caption{String Stream Processing}
    45544534\label{f:StringStreamProcessing}
    45554535\end{figure}
     4536
     4537\VRef[Figure]{f:StringStreamFunctions} shows the string stream operations.
     4538\begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
     4539\item
     4540\Indexc{write} (©ostrstream© only) writes all the buffered characters to the specified stream (©stdout© default).
     4541\end{itemize}
     4542The constructor functions:
     4543\begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
     4544\item
     4545create a bound stream to a write buffer (©ostrstream©) of ©size© or a read buffer (©istrstream©) containing a C string terminated with ©'\0'©.
     4546\end{itemize}
     4547
     4548\begin{figure}
     4549\begin{cfa}
     4550// *********************************** ostrstream ***********************************
     4551
     4552ostrstream & write( ostrstream & os, FILE * stream = stdout );
     4553
     4554void ?{}( ostrstream &, char buf[], size_t size );
     4555
     4556// *********************************** istrstream ***********************************
     4557
     4558void ?{}( istrstream & is, char buf[] );
     4559\end{cfa}
     4560\caption{String Stream Functions}
     4561\label{f:StringStreamFunctions}
     4562\end{figure}
     4563
    45564564
    45574565\begin{comment}
  • libcfa/prelude/bootloader.cf

    rb7fd9daf rf95634e  
    33char ** cfa_args_argv;
    44char ** cfa_args_envp;
    5 __attribute__((weak)) extern int cfa_main_returned;
     5int cfa_main_returned = 0;
    66
    77int main(int argc, char* argv[], char* envp[]) {
     
    1010        cfa_args_envp = envp;
    1111        int ret = invoke_main(argc, argv, envp);
    12         if(&cfa_main_returned) cfa_main_returned = 1;
     12        cfa_main_returned = 1;
    1313        return ret;
    1414}
  • libcfa/prelude/builtins.c

    rb7fd9daf rf95634e  
    1010// Created On       : Fri Jul 21 16:21:03 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Aug 14 08:45:54 2021
    13 // Update Count     : 133
     12// Last Modified On : Wed Jul 21 13:31:34 2021
     13// Update Count     : 129
    1414//
    1515
     
    107107#endif // __SIZEOF_INT128__
    108108
    109 // for-control index constraints
    110 // forall( T | { void ?{}( T &, zero_t ); void ?{}( T &, one_t ); T ?+=?( T &, T ); T ?-=?( T &, T ); int ?<?( T, T ); } )
    111 // static inline T __for_control_index_constraints__( T t ) { return t; }
    112 
    113109// exponentiation operator implementation
    114110
  • libcfa/src/Makefile.am

    rb7fd9daf rf95634e  
    4848        math.hfa \
    4949        time_t.hfa \
    50         bits/algorithm.hfa \
    5150        bits/align.hfa \
    5251        bits/containers.hfa \
     
    7877        memory.hfa \
    7978        parseargs.hfa \
    80         parseconfig.hfa \
    8179        rational.hfa \
    8280        stdlib.hfa \
     
    8785        containers/pair.hfa \
    8886        containers/result.hfa \
    89         containers/string.hfa \
    90         containers/string_res.hfa \
    9187        containers/vector.hfa \
    9288        device/cpu.hfa
     
    9490libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \
    9591        assert.cfa \
     92        bits/algorithm.hfa \
    9693        bits/debug.cfa \
    9794        exception.c \
     
    109106        concurrency/invoke.h \
    110107        concurrency/future.hfa \
    111         concurrency/kernel/fwd.hfa \
    112         concurrency/mutex_stmt.hfa
     108        concurrency/kernel/fwd.hfa
    113109
    114110inst_thread_headers_src = \
     
    196192        $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@}
    197193
    198 concurrency/io/call.cfa: $(srcdir)/concurrency/io/call.cfa.in
    199         ${AM_V_GEN}python3 $< > $@
    200 
    201194#----------------------------------------------------------------------------------------------------------------
    202195libcfa_la_SOURCES = ${libsrc}
  • libcfa/src/concurrency/clib/cfathread.cfa

    rb7fd9daf rf95634e  
    1313// Update Count     :
    1414//
    15 
    16 // #define EPOLL_FOR_SOCKETS
    1715
    1816#include "fstream.hfa"
     
    2523#include "cfathread.h"
    2624
    27 extern "C" {
    28                 #include <string.h>
    29                 #include <errno.h>
    30 }
    31 
    3225extern void ?{}(processor &, const char[], cluster &, thread$ *);
    3326extern "C" {
    3427      extern void __cfactx_invoke_thread(void (*main)(void *), void * this);
    35         extern int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
    3628}
    3729
    3830extern Time __kernel_get_time();
    39 extern unsigned register_proc_id( void );
    4031
    4132//================================================================================
    42 // Epoll support for sockets
    43 
    44 #if defined(EPOLL_FOR_SOCKETS)
    45         extern "C" {
    46                 #include <sys/epoll.h>
    47                 #include <sys/resource.h>
    48         }
    49 
    50         static pthread_t master_poller;
    51         static int master_epollfd = 0;
    52         static size_t poller_cnt = 0;
    53         static int * poller_fds = 0p;
    54         static struct leaf_poller * pollers = 0p;
    55 
    56         struct __attribute__((aligned)) fd_info_t {
    57                 int pollid;
    58                 size_t rearms;
    59         };
    60         rlim_t fd_limit = 0;
    61         static fd_info_t * volatile * fd_map = 0p;
    62 
    63         void * master_epoll( __attribute__((unused)) void * args ) {
    64                 unsigned id = register_proc_id();
    65 
    66                 enum { MAX_EVENTS = 5 };
    67                 struct epoll_event events[MAX_EVENTS];
    68                 for() {
    69                         int ret = epoll_wait(master_epollfd, events, MAX_EVENTS, -1);
    70                         if ( ret < 0 ) {
    71                                 abort | "Master epoll error: " | strerror(errno);
    72                         }
    73 
    74                         for(i; ret) {
    75                                 thread$ * thrd = (thread$ *)events[i].data.u64;
    76                                 unpark( thrd );
    77                         }
    78                 }
    79 
    80                 return 0p;
    81         }
    82 
    83         static inline int epoll_rearm(int epollfd, int fd, uint32_t event) {
    84                 struct epoll_event eevent;
    85                 eevent.events = event | EPOLLET | EPOLLONESHOT;
    86                 eevent.data.u64 = (uint64_t)active_thread();
    87 
    88                 if(0 != epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &eevent))
    89                 {
    90                         if(errno == ENOENT) return -1;
    91                         abort | acquire | "epoll" | epollfd | "ctl rearm" | fd | "error: " | errno | strerror(errno);
    92                 }
    93 
    94                 park();
    95                 return 0;
    96         }
    97 
    98         thread leaf_poller {
    99                 int epollfd;
    100         };
    101 
    102         void ?{}(leaf_poller & this, int fd) { this.epollfd = fd; }
    103 
    104         void main(leaf_poller & this) {
    105                 enum { MAX_EVENTS = 1024 };
    106                 struct epoll_event events[MAX_EVENTS];
    107                 const int max_retries = 5;
    108                 int retries = max_retries;
    109 
    110                 struct epoll_event event;
    111                 event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
    112                 event.data.u64 = (uint64_t)&(thread&)this;
    113 
    114                 if(0 != epoll_ctl(master_epollfd, EPOLL_CTL_ADD, this.epollfd, &event))
    115                 {
    116                         abort | "master epoll ctl add leaf: " | errno | strerror(errno);
    117                 }
    118 
    119                 park();
    120 
    121                 for() {
    122                         yield();
    123                         int ret = epoll_wait(this.epollfd, events, MAX_EVENTS, 0);
    124                         if ( ret < 0 ) {
    125                                 abort | "Leaf epoll error: " | errno | strerror(errno);
    126                         }
    127 
    128                         if(ret) {
    129                                 for(i; ret) {
    130                                         thread$ * thrd = (thread$ *)events[i].data.u64;
    131                                         unpark( thrd, UNPARK_REMOTE );
    132                                 }
    133                         }
    134                         else if(0 >= --retries) {
    135                                 epoll_rearm(master_epollfd, this.epollfd, EPOLLIN);
    136                         }
    137                 }
    138         }
    139 
    140         void setup_epoll( void ) __attribute__(( constructor ));
    141         void setup_epoll( void ) {
    142                 if(master_epollfd) abort | "Master epoll already setup";
    143 
    144                 master_epollfd = epoll_create1(0);
    145                 if(master_epollfd == -1) {
    146                         abort | "failed to create master epoll: " | errno | strerror(errno);
    147                 }
    148 
    149                 struct rlimit rlim;
    150                 if(int ret = getrlimit(RLIMIT_NOFILE, &rlim); 0 != ret) {
    151                         abort | "failed to get nofile limit: " | errno | strerror(errno);
    152                 }
    153 
    154                 fd_limit = rlim.rlim_cur;
    155                 fd_map = alloc(fd_limit);
    156                 for(i;fd_limit) {
    157                         fd_map[i] = 0p;
    158                 }
    159 
    160                 poller_cnt = 2;
    161                 poller_fds = alloc(poller_cnt);
    162                 pollers    = alloc(poller_cnt);
    163                 for(i; poller_cnt) {
    164                         poller_fds[i] = epoll_create1(0);
    165                         if(poller_fds[i] == -1) {
    166                                 abort | "failed to create leaf epoll [" | i | "]: " | errno | strerror(errno);
    167                         }
    168 
    169                         (pollers[i]){ poller_fds[i] };
    170                 }
    171 
    172                 pthread_attr_t attr;
    173                 if (int ret = pthread_attr_init(&attr); 0 != ret) {
    174                         abort | "failed to create master epoll thread attr: " | ret | strerror(ret);
    175                 }
    176 
    177                 if (int ret = pthread_create(&master_poller, &attr, master_epoll, 0p); 0 != ret) {
    178                         abort | "failed to create master epoll thread: " | ret | strerror(ret);
    179                 }
    180         }
    181 
    182         static inline int epoll_wait(int fd, uint32_t event) {
    183                 if(fd_map[fd] >= 1p) {
    184                         fd_map[fd]->rearms++;
    185                         epoll_rearm(poller_fds[fd_map[fd]->pollid], fd, event);
    186                         return 0;
    187                 }
    188 
    189                 for() {
    190                         fd_info_t * expected = 0p;
    191                         fd_info_t * sentinel = 1p;
    192                         if(__atomic_compare_exchange_n( &(fd_map[fd]), &expected, sentinel, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED)) {
    193                                 struct epoll_event eevent;
    194                                 eevent.events = event | EPOLLET | EPOLLONESHOT;
    195                                 eevent.data.u64 = (uint64_t)active_thread();
    196 
    197                                 int id = thread_rand() % poller_cnt;
    198                                 if(0 != epoll_ctl(poller_fds[id], EPOLL_CTL_ADD, fd, &eevent))
    199                                 {
    200                                         abort | "epoll ctl add" | poller_fds[id] | fd | fd_map[fd] | expected | "error: " | errno | strerror(errno);
    201                                 }
    202 
    203                                 fd_info_t * ninfo = alloc();
    204                                 ninfo->pollid = id;
    205                                 ninfo->rearms = 0;
    206                                 __atomic_store_n( &fd_map[fd], ninfo, __ATOMIC_SEQ_CST);
    207 
    208                                 park();
    209                                 return 0;
    210                         }
    211 
    212                         if(expected >= 0) {
    213                                 fd_map[fd]->rearms++;
    214                                 epoll_rearm(poller_fds[fd_map[fd]->pollid], fd, event);
    215                                 return 0;
    216                         }
    217 
    218                         Pause();
    219                 }
    220         }
    221 #endif
    222 
    223 //================================================================================
    224 // Thread run by the C Interface
     33// Thread run y the C Interface
    22534
    22635struct cfathread_object {
     
    436245        // Mutex
    437246        struct cfathread_mutex {
    438                 linear_backoff_then_block_lock impl;
     247                fast_lock impl;
    439248        };
    440249        int cfathread_mutex_init(cfathread_mutex_t *restrict mut, const cfathread_mutexattr_t *restrict) __attribute__((nonnull (1))) { *mut = new(); return 0; }
     
    451260        // Condition
    452261        struct cfathread_condition {
    453                 condition_variable(linear_backoff_then_block_lock) impl;
     262                condition_variable(fast_lock) impl;
    454263        };
    455264        int cfathread_cond_init(cfathread_cond_t *restrict cond, const cfathread_condattr_t *restrict) __attribute__((nonnull (1))) { *cond = new(); return 0; }
     
    479288        // IO operations
    480289        int cfathread_socket(int domain, int type, int protocol) {
    481                 return socket(domain, type
    482                 #if defined(EPOLL_FOR_SOCKETS)
    483                         | SOCK_NONBLOCK
    484                 #endif
    485                 , protocol);
     290                return socket(domain, type, protocol);
    486291        }
    487292        int cfathread_bind(int socket, const struct sockaddr *address, socklen_t address_len) {
     
    494299
    495300        int cfathread_accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len) {
    496                 #if defined(EPOLL_FOR_SOCKETS)
    497                         int ret;
    498                         for() {
    499                                 yield();
    500                                 ret = accept4(socket, address, address_len, SOCK_NONBLOCK);
    501                                 if(ret >= 0) break;
    502                                 if(errno != EAGAIN && errno != EWOULDBLOCK) break;
    503 
    504                                 epoll_wait(socket, EPOLLIN);
    505                         }
    506                         return ret;
    507                 #else
    508                         return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY);
    509                 #endif
     301                return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY);
    510302        }
    511303
    512304        int cfathread_connect(int socket, const struct sockaddr *address, socklen_t address_len) {
    513                 #if defined(EPOLL_FOR_SOCKETS)
    514                         int ret;
    515                         for() {
    516                                 ret = connect(socket, address, address_len);
    517                                 if(ret >= 0) break;
    518                                 if(errno != EAGAIN && errno != EWOULDBLOCK) break;
    519 
    520                                 epoll_wait(socket, EPOLLIN);
    521                         }
    522                         return ret;
    523                 #else
    524                         return cfa_connect(socket, address, address_len, CFA_IO_LAZY);
    525                 #endif
     305                return cfa_connect(socket, address, address_len, CFA_IO_LAZY);
    526306        }
    527307
     
    535315
    536316        ssize_t cfathread_sendmsg(int socket, const struct msghdr *message, int flags) {
    537                 #if defined(EPOLL_FOR_SOCKETS)
    538                         ssize_t ret;
    539                         __STATS__( false, io.ops.sockwrite++; )
    540                         for() {
    541                                 ret = sendmsg(socket, message, flags);
    542                                 if(ret >= 0) break;
    543                                 if(errno != EAGAIN && errno != EWOULDBLOCK) break;
    544 
    545                                 __STATS__( false, io.ops.epllwrite++; )
    546                                 epoll_wait(socket, EPOLLOUT);
    547                         }
    548                 #else
    549                         ssize_t ret = cfa_sendmsg(socket, message, flags, CFA_IO_LAZY);
    550                 #endif
    551                 return ret;
     317                return cfa_sendmsg(socket, message, flags, CFA_IO_LAZY);
    552318        }
    553319
    554320        ssize_t cfathread_write(int fildes, const void *buf, size_t nbyte) {
    555321                // Use send rather then write for socket since it's faster
    556                 #if defined(EPOLL_FOR_SOCKETS)
    557                         ssize_t ret;
    558                         // __STATS__( false, io.ops.sockwrite++; )
    559                         for() {
    560                                 ret = send(fildes, buf, nbyte, 0);
    561                                 if(ret >= 0) break;
    562                                 if(errno != EAGAIN && errno != EWOULDBLOCK) break;
    563 
    564                                 // __STATS__( false, io.ops.epllwrite++; )
    565                                 epoll_wait(fildes, EPOLLOUT);
    566                         }
    567                 #else
    568                         ssize_t ret = cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY);
    569                 #endif
    570                 return ret;
     322                return cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY);
    571323        }
    572324
     
    584336                msg.msg_controllen = 0;
    585337
    586                 #if defined(EPOLL_FOR_SOCKETS)
    587                         ssize_t ret;
    588                         yield();
    589                         for() {
    590                                 ret = recvmsg(socket, &msg, flags);
    591                                 if(ret >= 0) break;
    592                                 if(errno != EAGAIN && errno != EWOULDBLOCK) break;
    593 
    594                                 epoll_wait(socket, EPOLLIN);
    595                         }
    596                 #else
    597                         ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY);
    598                 #endif
     338                ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY);
    599339
    600340                if(address_len) *address_len = msg.msg_namelen;
     
    604344        ssize_t cfathread_read(int fildes, void *buf, size_t nbyte) {
    605345                // Use recv rather then read for socket since it's faster
    606                 #if defined(EPOLL_FOR_SOCKETS)
    607                         ssize_t ret;
    608                         __STATS__( false, io.ops.sockread++; )
    609                         yield();
    610                         for() {
    611                                 ret = recv(fildes, buf, nbyte, 0);
    612                                 if(ret >= 0) break;
    613                                 if(errno != EAGAIN && errno != EWOULDBLOCK) break;
    614 
    615                                 __STATS__( false, io.ops.epllread++; )
    616                                 epoll_wait(fildes, EPOLLIN);
    617                         }
    618                 #else
    619                         ssize_t ret = cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY);
    620                 #endif
    621                 return ret;
    622         }
    623 
    624 }
     346                return cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY);
     347        }
     348
     349}
  • libcfa/src/concurrency/invoke.h

    rb7fd9daf rf95634e  
    170170                bool corctx_flag;
    171171
     172                int last_cpu;
     173
    172174                //SKULLDUGGERY errno is not save in the thread data structure because returnToKernel appears to be the only function to require saving and restoring it
    173175
     
    175177                struct cluster * curr_cluster;
    176178
    177                 // preferred ready-queue or CPU
     179                // preferred ready-queue
    178180                unsigned preferred;
    179181
  • libcfa/src/concurrency/io.cfa

    rb7fd9daf rf95634e  
    9090        static inline unsigned __flush( struct $io_context & );
    9191        static inline __u32 __release_sqes( struct $io_context & );
    92         extern void __kernel_unpark( thread$ * thrd, unpark_hint );
     92        extern void __kernel_unpark( thread$ * thrd );
    9393
    9494        bool __cfa_io_drain( processor * proc ) {
     
    118118                        __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future );
    119119
    120                         __kernel_unpark( fulfil( *future, cqe.res, false ), UNPARK_LOCAL );
     120                        __kernel_unpark( fulfil( *future, cqe.res, false ) );
    121121                }
    122122
     
    183183                ctx.proc->io.pending = false;
    184184
     185                ready_schedule_lock();
    185186                __cfa_io_drain( proc );
     187                ready_schedule_unlock();
    186188                // for(i; 2) {
    187189                //      unsigned idx = proc->rdq.id + i;
     
    309311                // Make the sqes visible to the submitter
    310312                __atomic_store_n(sq.kring.tail, tail + have, __ATOMIC_RELEASE);
    311                 sq.to_submit += have;
     313                sq.to_submit++;
    312314
    313315                ctx->proc->io.pending = true;
    314316                ctx->proc->io.dirty   = true;
    315317                if(sq.to_submit > 30 || !lazy) {
    316                         ready_schedule_lock();
    317318                        __cfa_io_flush( ctx->proc );
    318                         ready_schedule_unlock();
    319319                }
    320320        }
  • libcfa/src/concurrency/io/types.hfa

    rb7fd9daf rf95634e  
    188188                return wait(this.self);
    189189        }
    190 
    191         void reset( io_future_t & this ) {
    192                 return reset(this.self);
    193         }
    194190}
  • libcfa/src/concurrency/kernel.cfa

    rb7fd9daf rf95634e  
    2222#include <errno.h>
    2323#include <stdio.h>
    24 #include <string.h>
    2524#include <signal.h>
    2625#include <unistd.h>
     
    3231#include "kernel_private.hfa"
    3332#include "preemption.hfa"
    34 #include "strstream.hfa"
    35 #include "device/cpu.hfa"
    3633
    3734//Private includes
     
    196193
    197194                        if( !readyThread ) {
    198                                 ready_schedule_lock();
    199195                                __cfa_io_flush( this );
    200                                 ready_schedule_unlock();
    201 
    202196                                readyThread = __next_thread_slow( this->cltr );
    203197                        }
     
    237231                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    238232
    239                                 {
    240                                         eventfd_t val;
    241                                         ssize_t ret = read( this->idle, &val, sizeof(val) );
    242                                         if(ret < 0) {
    243                                                 switch((int)errno) {
    244                                                 case EAGAIN:
    245                                                 #if EAGAIN != EWOULDBLOCK
    246                                                         case EWOULDBLOCK:
    247                                                 #endif
    248                                                 case EINTR:
    249                                                         // No need to do anything special here, just assume it's a legitimate wake-up
    250                                                         break;
    251                                                 default:
    252                                                         abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    253                                                 }
    254                                         }
    255                                 }
     233                                __disable_interrupts_hard();
     234                                eventfd_t val;
     235                                eventfd_read( this->idle, &val );
     236                                __enable_interrupts_hard();
    256237
    257238                                #if !defined(__CFA_NO_STATISTICS__)
     
    280261
    281262                        if(this->io.pending && !this->io.dirty) {
    282                                 ready_schedule_lock();
    283263                                __cfa_io_flush( this );
    284                                 ready_schedule_unlock();
    285264                        }
    286265
     
    322301
    323302                                // Don't block if we are done
    324                                 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) {
    325                                         ready_schedule_unlock();
    326                                         break MAIN_LOOP;
    327                                 }
     303                                if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
    328304
    329305                                __STATS( __tls_stats()->ready.sleep.halts++; )
     
    349325                                }
    350326
    351                                 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
     327                                        __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
    352328                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    353329
    354                                 {
    355                                         eventfd_t val;
    356                                         ssize_t ret = read( this->idle, &val, sizeof(val) );
    357                                         if(ret < 0) {
    358                                                 switch((int)errno) {
    359                                                 case EAGAIN:
    360                                                 #if EAGAIN != EWOULDBLOCK
    361                                                         case EWOULDBLOCK:
    362                                                 #endif
    363                                                 case EINTR:
    364                                                         // No need to do anything special here, just assume it's a legitimate wake-up
    365                                                         break;
    366                                                 default:
    367                                                         abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    368                                                 }
    369                                         }
    370                                 }
     330                                // __disable_interrupts_hard();
     331                                eventfd_t val;
     332                                eventfd_read( this->idle, &val );
     333                                // __enable_interrupts_hard();
    371334
    372335                                        __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); )
     
    430393        /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next );
    431394        __builtin_prefetch( thrd_dst->context.SP );
     395
     396        int curr = __kernel_getcpu();
     397        if(thrd_dst->last_cpu != curr) {
     398                int64_t l = thrd_dst->last_cpu;
     399                int64_t c = curr;
     400                int64_t v = (l << 32) | c;
     401                __push_stat( __tls_stats(), v, false, "Processor", this );
     402        }
     403
     404        thrd_dst->last_cpu = curr;
    432405
    433406        __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name);
     
    484457                if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) {
    485458                        // The thread was preempted, reschedule it and reset the flag
    486                         schedule_thread$( thrd_dst, UNPARK_LOCAL );
     459                        schedule_thread$( thrd_dst );
    487460                        break RUNNING;
    488461                }
     
    568541// Scheduler routines
    569542// KERNEL ONLY
    570 static void __schedule_thread( thread$ * thrd, unpark_hint hint ) {
     543static void __schedule_thread( thread$ * thrd ) {
    571544        /* paranoid */ verify( ! __preemption_enabled() );
    572545        /* paranoid */ verify( ready_schedule_islocked());
     
    588561        // Dereference the thread now because once we push it, there is not guaranteed it's still valid.
    589562        struct cluster * cl = thrd->curr_cluster;
    590         __STATS(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
     563        __STATS(bool outside = thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
    591564
    592565        // push the thread to the cluster ready-queue
    593         push( cl, thrd, hint );
     566        push( cl, thrd, local );
    594567
    595568        // variable thrd is no longer safe to use
     
    616589}
    617590
    618 void schedule_thread$( thread$ * thrd, unpark_hint hint ) {
     591void schedule_thread$( thread$ * thrd ) {
    619592        ready_schedule_lock();
    620                 __schedule_thread( thrd, hint );
     593                __schedule_thread( thrd );
    621594        ready_schedule_unlock();
    622595}
     
    669642}
    670643
    671 void __kernel_unpark( thread$ * thrd, unpark_hint hint ) {
     644void __kernel_unpark( thread$ * thrd ) {
    672645        /* paranoid */ verify( ! __preemption_enabled() );
    673646        /* paranoid */ verify( ready_schedule_islocked());
     
    677650        if(__must_unpark(thrd)) {
    678651                // Wake lost the race,
    679                 __schedule_thread( thrd, hint );
     652                __schedule_thread( thrd );
    680653        }
    681654
     
    684657}
    685658
    686 void unpark( thread$ * thrd, unpark_hint hint ) {
     659void unpark( thread$ * thrd ) {
    687660        if( !thrd ) return;
    688661
     
    690663                disable_interrupts();
    691664                        // Wake lost the race,
    692                         schedule_thread$( thrd, hint );
     665                        schedule_thread$( thrd );
    693666                enable_interrupts(false);
    694667        }
     
    947920                        /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count);
    948921                        /* paranoid */ verify( it->local_data->this_stats );
    949                         // __print_stats( it->local_data->this_stats, cltr->print_stats, "Processor", it->name, (void*)it );
    950922                        __tally_stats( cltr->stats, it->local_data->this_stats );
    951923                        it = &(*it)`next;
     
    957929                // this doesn't solve all problems but does solve many
    958930                // so it's probably good enough
    959                 disable_interrupts();
    960931                uint_fast32_t last_size = ready_mutate_lock();
    961932
     
    965936                // Unlock the RWlock
    966937                ready_mutate_unlock( last_size );
    967                 enable_interrupts();
    968938        }
    969939
  • libcfa/src/concurrency/kernel.hfa

    rb7fd9daf rf95634e  
    151151struct __attribute__((aligned(128))) __timestamp_t {
    152152        volatile unsigned long long tv;
    153         volatile unsigned long long ma;
    154 };
    155 
    156 // Aligned timestamps which are used by the relaxed ready queue
    157 struct __attribute__((aligned(128))) __help_cnts_t {
    158         volatile unsigned long long src;
    159         volatile unsigned long long dst;
    160         volatile unsigned long long tri;
    161 };
    162 
    163 static inline void  ?{}(__timestamp_t & this) { this.tv = 0; this.ma = 0; }
     153};
     154
     155static inline void  ?{}(__timestamp_t & this) { this.tv = 0; }
    164156static inline void ^?{}(__timestamp_t & this) {}
    165157
     
    177169                // Array of times
    178170                __timestamp_t * volatile tscs;
    179 
    180                 // Array of stats
    181                 __help_cnts_t * volatile help;
    182171
    183172                // Number of lanes (empty or not)
  • libcfa/src/concurrency/kernel/fwd.hfa

    rb7fd9daf rf95634e  
    119119
    120120        extern "Cforall" {
    121                 enum unpark_hint { UNPARK_LOCAL, UNPARK_REMOTE };
    122 
    123121                extern void park( void );
    124                 extern void unpark( struct thread$ *, unpark_hint );
    125                 static inline void unpark( struct thread$ * thrd ) { unpark(thrd, UNPARK_LOCAL); }
     122                extern void unpark( struct thread$ * this );
    126123                static inline struct thread$ * active_thread () {
    127124                        struct thread$ * t = publicTLS_get( this_thread );
  • libcfa/src/concurrency/kernel/startup.cfa

    rb7fd9daf rf95634e  
    100100// Other Forward Declarations
    101101extern void __wake_proc(processor *);
    102 extern int cfa_main_returned;                                                   // from interpose.cfa
    103102
    104103//-----------------------------------------------------------------------------
     
    201200        __cfadbg_print_safe(runtime_core, "Kernel : Main cluster ready\n");
    202201
    203         // Construct the processor context of the main processor
    204         void ?{}(processorCtx_t & this, processor * proc) {
    205                 (this.__cor){ "Processor" };
    206                 this.__cor.starter = 0p;
    207                 this.proc = proc;
    208         }
    209 
    210         void ?{}(processor & this) with( this ) {
    211                 ( this.terminated ){};
    212                 ( this.runner ){};
    213                 init( this, "Main Processor", *mainCluster, 0p );
    214                 kernel_thread = pthread_self();
    215 
    216                 runner{ &this };
    217                 __cfadbg_print_safe(runtime_core, "Kernel : constructed main processor context %p\n", &runner);
    218         }
    219 
    220         // Initialize the main processor and the main processor ctx
    221         // (the coroutine that contains the processing control flow)
    222         mainProcessor = (processor *)&storage_mainProcessor;
    223         (*mainProcessor){};
    224 
    225         register_tls( mainProcessor );
    226 
    227202        // Start by initializing the main thread
    228203        // SKULLDUGGERY: the mainThread steals the process main thread
     
    235210        __cfadbg_print_safe(runtime_core, "Kernel : Main thread ready\n");
    236211
     212
     213
     214        // Construct the processor context of the main processor
     215        void ?{}(processorCtx_t & this, processor * proc) {
     216                (this.__cor){ "Processor" };
     217                this.__cor.starter = 0p;
     218                this.proc = proc;
     219        }
     220
     221        void ?{}(processor & this) with( this ) {
     222                ( this.terminated ){};
     223                ( this.runner ){};
     224                init( this, "Main Processor", *mainCluster, 0p );
     225                kernel_thread = pthread_self();
     226
     227                runner{ &this };
     228                __cfadbg_print_safe(runtime_core, "Kernel : constructed main processor context %p\n", &runner);
     229        }
     230
     231        // Initialize the main processor and the main processor ctx
     232        // (the coroutine that contains the processing control flow)
     233        mainProcessor = (processor *)&storage_mainProcessor;
     234        (*mainProcessor){};
     235
     236        register_tls( mainProcessor );
     237        mainThread->last_cpu = __kernel_getcpu();
     238
    237239        //initialize the global state variables
    238240        __cfaabi_tls.this_processor = mainProcessor;
     
    250252        // Add the main thread to the ready queue
    251253        // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread
    252         schedule_thread$(mainThread, UNPARK_LOCAL);
     254        schedule_thread$(mainThread);
    253255
    254256        // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX
     
    269271
    270272static void __kernel_shutdown(void) {
    271         if(!cfa_main_returned) return;
    272273        /* paranoid */ verify( __preemption_enabled() );
    273274        disable_interrupts();
     
    485486        link.next = 0p;
    486487        link.ts   = -1llu;
    487         preferred = ready_queue_new_preferred();
     488        preferred = -1u;
    488489        last_proc = 0p;
    489490        #if defined( __CFA_WITH_VERIFY__ )
  • libcfa/src/concurrency/kernel_private.hfa

    rb7fd9daf rf95634e  
    4646}
    4747
    48 void schedule_thread$( thread$ *, unpark_hint hint ) __attribute__((nonnull (1)));
     48void schedule_thread$( thread$ * ) __attribute__((nonnull (1)));
    4949
    5050extern bool __preemption_enabled();
     
    300300// push thread onto a ready queue for a cluster
    301301// returns true if the list was previously empty, false otherwise
    302 __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint);
     302__attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool local);
    303303
    304304//-----------------------------------------------------------------------
     
    321321
    322322//-----------------------------------------------------------------------
    323 // get preferred ready for new thread
    324 unsigned ready_queue_new_preferred();
    325 
    326 //-----------------------------------------------------------------------
    327323// Increase the width of the ready queue (number of lanes) by 4
    328324void ready_queue_grow  (struct cluster * cltr);
  • libcfa/src/concurrency/locks.hfa

    rb7fd9daf rf95634e  
    324324        }
    325325
     326        // linear backoff bounded by spin_count
     327        spin = spin_start;
     328        int spin_counter = 0;
     329        int yield_counter = 0;
     330        for ( ;; ) {
     331                if(try_lock_contention(this)) return true;
     332                if(spin_counter < spin_count) {
     333                        for (int i = 0; i < spin; i++) Pause();
     334                        if (spin < spin_end) spin += spin;
     335                        else spin_counter++;
     336                } else if (yield_counter < yield_count) {
     337                        // after linear backoff yield yield_count times
     338                        yield_counter++;
     339                        yield();
     340                } else { break; }
     341        }
     342
     343        // block until signalled
     344        while (block(this)) if(try_lock_contention(this)) return true;
     345
     346        // this should never be reached as block(this) always returns true
     347        return false;
     348}
     349
     350static inline bool lock_improved(linear_backoff_then_block_lock & this) with(this) {
     351        // if owner just return
     352        if (active_thread() == owner) return true;
     353        size_t compare_val = 0;
     354        int spin = spin_start;
     355        // linear backoff
     356        for( ;; ) {
     357                compare_val = 0;
     358                if (internal_try_lock(this, compare_val)) return true;
     359                if (2 == compare_val) break;
     360                for (int i = 0; i < spin; i++) Pause();
     361                if (spin >= spin_end) break;
     362                spin += spin;
     363        }
     364
     365        // linear backoff bounded by spin_count
     366        spin = spin_start;
     367        int spin_counter = 0;
     368        int yield_counter = 0;
     369        for ( ;; ) {
     370                compare_val = 0;
     371                if(internal_try_lock(this, compare_val)) return true;
     372                if (2 == compare_val) break;
     373                if(spin_counter < spin_count) {
     374                        for (int i = 0; i < spin; i++) Pause();
     375                        if (spin < spin_end) spin += spin;
     376                        else spin_counter++;
     377                } else if (yield_counter < yield_count) {
     378                        // after linear backoff yield yield_count times
     379                        yield_counter++;
     380                        yield();
     381                } else { break; }
     382        }
     383
    326384        if(2 != compare_val && try_lock_contention(this)) return true;
    327385        // block until signalled
     
    344402static inline void on_notify(linear_backoff_then_block_lock & this, struct thread$ * t ) { unpark(t); }
    345403static inline size_t on_wait(linear_backoff_then_block_lock & this) { unlock(this); return 0; }
    346 static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock(this); }
     404static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock_improved(this); }
    347405
    348406//-----------------------------------------------------------------------------
  • libcfa/src/concurrency/monitor.cfa

    rb7fd9daf rf95634e  
    990990}
    991991
    992 //-----------------------------------------------------------------------------
    993 // Enter routine for mutex stmt
    994 // Can't be accepted since a mutex stmt is effectively an anonymous routine
    995 // Thus we do not need a monitor group
    996 void lock( monitor$ * this ) {
    997         thread$ * thrd = active_thread();
    998 
    999         // Lock the monitor spinlock
    1000         lock( this->lock __cfaabi_dbg_ctx2 );
    1001 
    1002         __cfaabi_dbg_print_safe( "Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner);
    1003 
    1004         if( unlikely(0 != (0x1 & (uintptr_t)this->owner)) ) {
    1005                 abort( "Attempt by thread \"%.256s\" (%p) to access joined monitor %p.", thrd->self_cor.name, thrd, this );
    1006         }
    1007         else if( !this->owner ) {
    1008                 // No one has the monitor, just take it
    1009                 __set_owner( this, thrd );
    1010 
    1011                 __cfaabi_dbg_print_safe( "Kernel :  mon is free \n" );
    1012         }
    1013         else if( this->owner == thrd) {
    1014                 // We already have the monitor, just note how many times we took it
    1015                 this->recursion += 1;
    1016 
    1017                 __cfaabi_dbg_print_safe( "Kernel :  mon already owned \n" );
    1018         }
    1019         else {
    1020                 __cfaabi_dbg_print_safe( "Kernel :  blocking \n" );
    1021 
    1022                 // Some one else has the monitor, wait in line for it
    1023                 /* paranoid */ verify( thrd->link.next == 0p );
    1024                 append( this->entry_queue, thrd );
    1025                 /* paranoid */ verify( thrd->link.next == 1p );
    1026 
    1027                 unlock( this->lock );
    1028                 park();
    1029 
    1030                 __cfaabi_dbg_print_safe( "Kernel : %10p Entered  mon %p\n", thrd, this);
    1031 
    1032                 /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    1033                 return;
    1034         }
    1035 
    1036         __cfaabi_dbg_print_safe( "Kernel : %10p Entered  mon %p\n", thrd, this);
    1037 
    1038         /* paranoid */ verifyf( active_thread() == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", active_thread(), this->owner, this->recursion, this );
    1039         /* paranoid */ verify( this->lock.lock );
    1040 
    1041         // Release the lock and leave
    1042         unlock( this->lock );
    1043         return;
    1044 }
    1045 
    1046 // Leave routine for mutex stmt
    1047 // Is just a wrapper around __leave for the is_lock trait to see
    1048 void unlock( monitor$ * this ) { __leave( this ); }
    1049 
    1050992// Local Variables: //
    1051993// mode: c //
  • libcfa/src/concurrency/monitor.hfa

    rb7fd9daf rf95634e  
    6565        free( th );
    6666}
    67 
    68 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } )
    69 void adelete( T arr[] ) {
    70         if ( arr ) {                                                                            // ignore null
    71                 size_t dim = malloc_size( arr ) / sizeof( T );
    72                 for ( int i = dim - 1; i >= 0; i -= 1 ) {               // reverse allocation order, must be unsigned
    73                         ^(arr[i]){};                                                            // run destructor
    74                 } // for
    75                 free( arr );
    76         } // if
    77 } // adelete
    7867
    7968//-----------------------------------------------------------------------------
     
    160149void __waitfor_internal( const __waitfor_mask_t & mask, int duration );
    161150
    162 // lock and unlock routines for mutex statements to use
    163 void lock( monitor$ * this );
    164 void unlock( monitor$ * this );
    165 
    166151// Local Variables: //
    167152// mode: c //
  • libcfa/src/concurrency/ready_queue.cfa

    rb7fd9daf rf95634e  
    100100        #define __kernel_rseq_unregister rseq_unregister_current_thread
    101101#elif defined(CFA_HAVE_LINUX_RSEQ_H)
    102         static void __kernel_raw_rseq_register  (void);
    103         static void __kernel_raw_rseq_unregister(void);
     102        void __kernel_raw_rseq_register  (void);
     103        void __kernel_raw_rseq_unregister(void);
    104104
    105105        #define __kernel_rseq_register __kernel_raw_rseq_register
     
    246246// Cforall Ready Queue used for scheduling
    247247//=======================================================================
    248 unsigned long long moving_average(unsigned long long nval, unsigned long long oval) {
    249         const unsigned long long tw = 16;
    250         const unsigned long long nw = 4;
    251         const unsigned long long ow = tw - nw;
    252         return ((nw * nval) + (ow * oval)) / tw;
    253 }
    254 
    255248void ?{}(__ready_queue_t & this) with (this) {
    256249        #if defined(USE_CPU_WORK_STEALING)
     
    258251                lanes.data = alloc( lanes.count );
    259252                lanes.tscs = alloc( lanes.count );
    260                 lanes.help = alloc( cpu_info.hthrd_count );
    261253
    262254                for( idx; (size_t)lanes.count ) {
    263255                        (lanes.data[idx]){};
    264256                        lanes.tscs[idx].tv = rdtscl();
    265                         lanes.tscs[idx].ma = rdtscl();
    266                 }
    267                 for( idx; (size_t)cpu_info.hthrd_count ) {
    268                         lanes.help[idx].src = 0;
    269                         lanes.help[idx].dst = 0;
    270                         lanes.help[idx].tri = 0;
    271257                }
    272258        #else
    273259                lanes.data  = 0p;
    274260                lanes.tscs  = 0p;
    275                 lanes.help  = 0p;
    276261                lanes.count = 0;
    277262        #endif
     
    285270        free(lanes.data);
    286271        free(lanes.tscs);
    287         free(lanes.help);
    288272}
    289273
    290274//-----------------------------------------------------------------------
    291275#if defined(USE_CPU_WORK_STEALING)
    292         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     276        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {
    293277                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
    294278
    295279                processor * const proc = kernelTLS().this_processor;
    296                 const bool external = (!proc) || (cltr != proc->cltr);
    297 
    298                 // Figure out the current cpu and make sure it is valid
     280                const bool external = !push_local || (!proc) || (cltr != proc->cltr);
     281
    299282                const int cpu = __kernel_getcpu();
    300283                /* paranoid */ verify(cpu >= 0);
     
    302285                /* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count);
    303286
    304                 // Figure out where thread was last time and make sure it's
    305                 /* paranoid */ verify(thrd->preferred >= 0);
    306                 /* paranoid */ verify(thrd->preferred < cpu_info.hthrd_count);
    307                 /* paranoid */ verify(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count);
    308                 const int prf = thrd->preferred * READYQ_SHARD_FACTOR;
    309 
    310                 const cpu_map_entry_t & map;
    311                 choose(hint) {
    312                         case UNPARK_LOCAL : &map = &cpu_info.llc_map[cpu];
    313                         case UNPARK_REMOTE: &map = &cpu_info.llc_map[prf];
    314                 }
     287                const cpu_map_entry_t & map = cpu_info.llc_map[cpu];
    315288                /* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count);
    316289                /* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count);
     
    323296                        if(unlikely(external)) { r = __tls_rand(); }
    324297                        else { r = proc->rdq.its++; }
    325                         choose(hint) {
    326                                 case UNPARK_LOCAL : i = start + (r % READYQ_SHARD_FACTOR);
    327                                 case UNPARK_REMOTE: i = prf   + (r % READYQ_SHARD_FACTOR);
    328                         }
     298                        i = start + (r % READYQ_SHARD_FACTOR);
    329299                        // If we can't lock it retry
    330300                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     
    362332                processor * const proc = kernelTLS().this_processor;
    363333                const int start = map.self * READYQ_SHARD_FACTOR;
    364                 const unsigned long long ctsc = rdtscl();
    365334
    366335                // Did we already have a help target
    367336                if(proc->rdq.target == -1u) {
    368                         unsigned long long max = 0;
     337                        // if We don't have a
     338                        unsigned long long min = ts(lanes.data[start]);
    369339                        for(i; READYQ_SHARD_FACTOR) {
    370                                 unsigned long long tsc = moving_average(ctsc - ts(lanes.data[start + i]), lanes.tscs[start + i].ma);
    371                                 if(tsc > max) max = tsc;
    372                         }
    373                          proc->rdq.cutoff = (max + 2 * max) / 2;
     340                                unsigned long long tsc = ts(lanes.data[start + i]);
     341                                if(tsc < min) min = tsc;
     342                        }
     343                        proc->rdq.cutoff = min;
     344
    374345                        /* paranoid */ verify(lanes.count < 65536); // The following code assumes max 65536 cores.
    375346                        /* paranoid */ verify(map.count < 65536); // The following code assumes max 65536 cores.
    376347
    377                         if(0 == (__tls_rand() % 100)) {
     348                        if(0 == (__tls_rand() % 10_000)) {
    378349                                proc->rdq.target = __tls_rand() % lanes.count;
    379350                        } else {
     
    387358                }
    388359                else {
    389                         unsigned long long max = 0;
    390                         for(i; READYQ_SHARD_FACTOR) {
    391                                 unsigned long long tsc = moving_average(ctsc - ts(lanes.data[start + i]), lanes.tscs[start + i].ma);
    392                                 if(tsc > max) max = tsc;
    393                         }
    394                         const unsigned long long cutoff = (max + 2 * max) / 2;
     360                        const unsigned long long bias = 0; //2_500_000_000;
     361                        const unsigned long long cutoff = proc->rdq.cutoff > bias ? proc->rdq.cutoff - bias : proc->rdq.cutoff;
    395362                        {
    396363                                unsigned target = proc->rdq.target;
    397364                                proc->rdq.target = -1u;
    398                                 lanes.help[target / READYQ_SHARD_FACTOR].tri++;
    399                                 if(moving_average(ctsc - lanes.tscs[target].tv, lanes.tscs[target].ma) > cutoff) {
     365                                if(lanes.tscs[target].tv < cutoff && ts(lanes.data[target]) < cutoff) {
    400366                                        thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
    401367                                        proc->rdq.last = target;
    402368                                        if(t) return t;
    403                                         else proc->rdq.target = -1u;
    404369                                }
    405                                 else proc->rdq.target = -1u;
    406370                        }
    407371
     
    464428        }
    465429
    466         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     430        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {
    467431                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
    468432
    469                 const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
     433                const bool external = !push_local || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
    470434                /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count );
    471435
     
    551515#endif
    552516#if defined(USE_WORK_STEALING)
    553         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     517        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {
    554518                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
    555519
    556520                // #define USE_PREFERRED
    557521                #if !defined(USE_PREFERRED)
    558                 const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
     522                const bool external = !push_local || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
    559523                /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count );
    560524                #else
    561525                        unsigned preferred = thrd->preferred;
    562                         const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;
     526                        const bool external = push_local || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;
    563527                        /* paranoid */ verifyf(external || preferred < lanes.count, "Invalid preferred queue %u for %u lanes", preferred, lanes.count );
    564528
     
    681645        // Actually pop the list
    682646        struct thread$ * thrd;
    683         unsigned long long tsc_before = ts(lane);
    684647        unsigned long long tsv;
    685648        [thrd, tsv] = pop(lane);
     
    695658        __STATS( stats.success++; )
    696659
    697         #if defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
    698                 unsigned long long now = rdtscl();
     660        #if defined(USE_WORK_STEALING)
    699661                lanes.tscs[w].tv = tsv;
    700                 lanes.tscs[w].ma = moving_average(now > tsc_before ? now - tsc_before : 0, lanes.tscs[w].ma);
    701662        #endif
    702663
    703         #if defined(USE_CPU_WORK_STEALING)
    704                 thrd->preferred = w / READYQ_SHARD_FACTOR;
    705         #else
    706                 thrd->preferred = w;
    707         #endif
     664        thrd->preferred = w;
    708665
    709666        // return the popped thread
     
    731688
    732689//-----------------------------------------------------------------------
    733 // get preferred ready for new thread
    734 unsigned ready_queue_new_preferred() {
    735         unsigned pref = 0;
    736         if(struct thread$ * thrd = publicTLS_get( this_thread )) {
    737                 pref = thrd->preferred;
    738         }
    739         else {
    740                 #if defined(USE_CPU_WORK_STEALING)
    741                         pref = __kernel_getcpu();
    742                 #endif
    743         }
    744 
    745         #if defined(USE_CPU_WORK_STEALING)
    746                 /* paranoid */ verify(pref >= 0);
    747                 /* paranoid */ verify(pref < cpu_info.hthrd_count);
    748         #endif
    749 
    750         return pref;
    751 }
    752 
    753 //-----------------------------------------------------------------------
    754690// Check that all the intrusive queues in the data structure are still consistent
    755691static void check( __ready_queue_t & q ) with (q) {
     
    979915        extern void __enable_interrupts_hard();
    980916
    981         static void __kernel_raw_rseq_register  (void) {
     917        void __kernel_raw_rseq_register  (void) {
    982918                /* paranoid */ verify( __cfaabi_rseq.cpu_id == RSEQ_CPU_ID_UNINITIALIZED );
    983919
     
    997933        }
    998934
    999         static void __kernel_raw_rseq_unregister(void) {
     935        void __kernel_raw_rseq_unregister(void) {
    1000936                /* paranoid */ verify( __cfaabi_rseq.cpu_id >= 0 );
    1001937
  • libcfa/src/concurrency/ready_subqueue.hfa

    rb7fd9daf rf95634e  
    9898
    9999        // Get the relevant nodes locally
     100        unsigned long long ts = this.anchor.ts;
    100101        thread$ * node = this.anchor.next;
    101102        this.anchor.next = node->link.next;
     
    115116        /* paranoid */ verify( node->link.ts   != 0  );
    116117        /* paranoid */ verify( this.anchor.ts  != 0  );
    117         return [node, this.anchor.ts];
     118        return [node, ts];
    118119}
    119120
  • libcfa/src/concurrency/stats.cfa

    rb7fd9daf rf95634e  
    4848                        stats->io.calls.completed   = 0;
    4949                        stats->io.calls.errors.busy = 0;
    50                         stats->io.ops.sockread      = 0;
    51                         stats->io.ops.epllread      = 0;
    52                         stats->io.ops.sockwrite     = 0;
    53                         stats->io.ops.epllwrite     = 0;
    5450                #endif
    5551
     
    108104                        tally_one( &cltr->io.calls.completed  , &proc->io.calls.completed   );
    109105                        tally_one( &cltr->io.calls.errors.busy, &proc->io.calls.errors.busy );
    110                         tally_one( &cltr->io.ops.sockread     , &proc->io.ops.sockread      );
    111                         tally_one( &cltr->io.ops.epllread     , &proc->io.ops.epllread      );
    112                         tally_one( &cltr->io.ops.sockwrite    , &proc->io.ops.sockwrite     );
    113                         tally_one( &cltr->io.ops.epllwrite    , &proc->io.ops.epllwrite     );
    114106                #endif
    115107        }
     
    187179                                     | " - cmp " | eng3(io.calls.drain) | "/" | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)"
    188180                                     | " - " | eng3(io.calls.errors.busy) | " EBUSY";
    189                                 sstr | "- ops blk: "
    190                                      |   " sk rd: " | eng3(io.ops.sockread)  | "epll: " | eng3(io.ops.epllread)
    191                                      |   " sk wr: " | eng3(io.ops.sockwrite) | "epll: " | eng3(io.ops.epllwrite);
    192181                                sstr | nl;
    193182                        }
  • libcfa/src/concurrency/stats.hfa

    rb7fd9daf rf95634e  
    102102                                volatile uint64_t sleeps;
    103103                        } poller;
    104                         struct {
    105                                 volatile uint64_t sockread;
    106                                 volatile uint64_t epllread;
    107                                 volatile uint64_t sockwrite;
    108                                 volatile uint64_t epllwrite;
    109                         } ops;
    110104                };
    111105        #endif
  • libcfa/src/concurrency/thread.cfa

    rb7fd9daf rf95634e  
    2525#include "invoke.h"
    2626
    27 uint64_t thread_rand();
    28 
    2927//-----------------------------------------------------------------------------
    3028// Thread ctors and dtors
     
    3634        preempted = __NO_PREEMPTION;
    3735        corctx_flag = false;
     36        disable_interrupts();
     37        last_cpu = __kernel_getcpu();
     38        enable_interrupts();
    3839        curr_cor = &self_cor;
    3940        self_mon.owner = &this;
     
    4344        link.next = 0p;
    4445        link.ts   = -1llu;
    45         preferred = ready_queue_new_preferred();
     46        preferred = -1u;
    4647        last_proc = 0p;
    4748        #if defined( __CFA_WITH_VERIFY__ )
     
    140141        /* paranoid */ verify( this_thrd->context.SP );
    141142
    142         schedule_thread$( this_thrd, UNPARK_LOCAL );
     143        schedule_thread$( this_thrd );
    143144        enable_interrupts();
    144145}
  • libcfa/src/device/cpu.cfa

    rb7fd9daf rf95634e  
    144144// Count number of cpus in the system
    145145static int count_cpus(void) {
    146         const char * fpath = "/sys/devices/system/cpu/online";
     146        const char * fpath = "/sys/devices/system/cpu/present";
    147147        int fd = open(fpath, 0, O_RDONLY);
    148148        /* paranoid */ verifyf(fd >= 0, "Could not open file %s", fpath);
     
    422422        }
    423423}
    424 
    425 cpu_info_t cpu_info;
  • libcfa/src/device/cpu.hfa

    rb7fd9daf rf95634e  
    3030};
    3131
    32 extern cpu_info_t cpu_info;
     32cpu_info_t cpu_info;
  • libcfa/src/fstream.cfa

    rb7fd9daf rf95634e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 10 11:23:05 2021
    13 // Update Count     : 512
     12// Last Modified On : Thu Jul 29 22:34:10 2021
     13// Update Count     : 454
    1414//
    1515
     
    2828#define IO_MSG "I/O error: "
    2929
    30 // private
    31 void ?{}( ofstream & os, void * file ) with( os ) {
    32         file$ = file;
    33         sepDefault$ = true;
    34         sepOnOff$ = false;
    35         nlOnOff$ = true;
    36         prt$ = false;
    37         sawNL$ = false;
     30void ?{}( ofstream & os, void * file ) {
     31        os.file$ = file;
     32        os.sepDefault$ = true;
     33        os.sepOnOff$ = false;
     34        os.nlOnOff$ = true;
     35        os.prt$ = false;
     36        os.sawNL$ = false;
     37        os.acquired$ = false;
    3838        sepSetCur$( os, sepGet( os ) );
    3939        sepSet( os, " " );
     
    4141} // ?{}
    4242
    43 inline bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
    44 inline void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; }
    45 inline void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
    46 inline const char * sepGetCur$( ofstream & os ) { return os.sepCur$; }
    47 inline void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
    48 inline bool getNL$( ofstream & os ) { return os.sawNL$; }
    49 inline void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; }
    50 inline bool getANL$( ofstream & os ) { return os.nlOnOff$; }
    51 inline bool getPrt$( ofstream & os ) { return os.prt$; }
    52 inline void setPrt$( ofstream & os, bool state ) { os.prt$ = state; }
    53 
    54 inline void lock( ofstream & os ) with( os ) {  lock( os.lock$ ); }
    55 inline void unlock( ofstream & os ) { unlock( os.lock$ ); }
     43// private
     44bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
     45void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; }
     46void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
     47const char * sepGetCur$( ofstream & os ) { return os.sepCur$; }
     48void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
     49bool getNL$( ofstream & os ) { return os.sawNL$; }
     50void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; }
     51bool getANL$( ofstream & os ) { return os.nlOnOff$; }
     52bool getPrt$( ofstream & os ) { return os.prt$; }
     53void setPrt$( ofstream & os, bool state ) { os.prt$ = state; }
    5654
    5755// public
    5856void ?{}( ofstream & os ) { os.file$ = 0p; }
    59 void ?{}( ofstream & os, const char name[], const char mode[] ) { open( os, name, mode ); }
    60 void ?{}( ofstream & os, const char name[] ) { open( os, name, "w" ); }
    61 void ^?{}( ofstream & os ) { close( os ); }
     57
     58void ?{}( ofstream & os, const char name[], const char mode[] ) {
     59        open( os, name, mode );
     60} // ?{}
     61
     62void ?{}( ofstream & os, const char name[] ) {
     63        open( os, name, "w" );
     64} // ?{}
     65
     66void ^?{}( ofstream & os ) {
     67        close( os );
     68} // ^?{}
    6269
    6370void sepOn( ofstream & os ) { os.sepOnOff$ = ! getNL$( os ); }
     
    100107        if ( &os == &exit ) exit( EXIT_FAILURE );
    101108        if ( &os == &abort ) abort();
     109        if ( os.acquired$ ) { os.acquired$ = false; release( os ); }
    102110} // ends
    103111
    104 bool fail( ofstream & os ) { return os.file$ == 0 || ferror( (FILE *)(os.file$) ); }
    105 void clear( ofstream & os ) { clearerr( (FILE *)(os.file$) ); }
    106 int flush( ofstream & os ) { return fflush( (FILE *)(os.file$) ); }
     112bool fail( ofstream & os ) {
     113        return os.file$ == 0 || ferror( (FILE *)(os.file$) );
     114} // fail
     115
     116void clear( ofstream & os ) {
     117        clearerr( (FILE *)(os.file$) );
     118} // clear
     119
     120int flush( ofstream & os ) {
     121        return fflush( (FILE *)(os.file$) );
     122} // flush
    107123
    108124void open( ofstream & os, const char name[], const char mode[] ) {
    109         FILE * file;
    110     for ( cnt; 10 ) {
    111                 errno = 0;
    112                 file = fopen( name, mode );
    113           if ( file != 0p || errno != EINTR ) break;            // timer interrupt ?
    114           if ( cnt == 9 ) abort( "ofstream open EINTR spinning exceeded" );
    115     } // for
     125        FILE * file = fopen( name, mode );
     126        #ifdef __CFA_DEBUG__
    116127        if ( file == 0p ) {
    117128                throw (Open_Failure){ os };
    118129                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    119130        } // if
    120         (os){ file };                                                                           // initialize
     131        #endif // __CFA_DEBUG__
     132        (os){ file };
    121133} // open
    122134
    123 void open( ofstream & os, const char name[] ) { open( os, name, "w" ); }
    124 
    125 void close( ofstream & os ) with( os ) {
    126   if ( (FILE *)(file$) == 0p ) return;
    127   if ( (FILE *)(file$) == (FILE *)stdout || (FILE *)(file$) == (FILE *)stderr ) return;
    128 
    129         int ret;
    130     for ( cnt; 10 ) {
    131                 errno = 0;
    132                 ret = fclose( (FILE *)(file$) );
    133           if ( ret != EOF || errno != EINTR ) break;            // timer interrupt ?
    134           if ( cnt == 9 ) abort( "ofstream open EINTR spinning exceeded" );
    135     } // for
    136         if ( ret == EOF ) {
     135void open( ofstream & os, const char name[] ) {
     136        open( os, name, "w" );
     137} // open
     138
     139void close( ofstream & os ) {
     140  if ( (FILE *)(os.file$) == 0p ) return;
     141  if ( (FILE *)(os.file$) == (FILE *)stdout || (FILE *)(os.file$) == (FILE *)stderr ) return;
     142
     143        if ( fclose( (FILE *)(os.file$) ) == EOF ) {
    137144                throw (Close_Failure){ os };
    138145                // abort | IO_MSG "close output" | nl | strerror( errno );
    139146        } // if
    140         file$ = 0p;                                                                                     // safety after close
     147        os.file$ = 0p;
    141148} // close
    142149
     
    157164        va_list args;
    158165        va_start( args, format );
    159                
    160         int len;
    161     for ( cnt; 10 ) {
    162                 errno = 0;
    163                 len = vfprintf( (FILE *)(os.file$), format, args );
    164           if ( len != EOF || errno != EINTR ) break;            // timer interrupt ?
    165           if ( cnt == 9 ) abort( "ofstream fmt EINTR spinning exceeded" );
    166     } // for
     166        int len = vfprintf( (FILE *)(os.file$), format, args );
    167167        if ( len == EOF ) {
    168168                if ( ferror( (FILE *)(os.file$) ) ) {
     
    177177} // fmt
    178178
     179inline void acquire( ofstream & os ) {
     180        lock( os.lock$ );
     181        if ( ! os.acquired$ ) os.acquired$ = true;
     182        else unlock( os.lock$ );
     183} // acquire
     184
     185inline void release( ofstream & os ) {
     186        unlock( os.lock$ );
     187} // release
     188
     189void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); }
     190void ^?{}( osacquire & acq ) { release( acq.os ); }
     191
    179192static ofstream soutFile = { (FILE *)stdout };
    180193ofstream & sout = soutFile, & stdout = soutFile;
     
    194207        flush( os );
    195208        return os;
     209        // (ofstream &)(os | '\n');
     210        // setPrt$( os, false );                                                        // turn off
     211        // setNL$( os, true );
     212        // flush( os );
     213        // return sepOff( os );                                                 // prepare for next line
    196214} // nl
    197215
     
    201219
    202220// private
    203 void ?{}( ifstream & is, void * file ) with( is ) {
    204         file$ = file;
    205         nlOnOff$ = false;
    206 } // ?{}
    207 
    208 bool getANL$( ifstream & os ) { return os.nlOnOff$; }
    209 
    210 inline void lock( ifstream & os ) with( os ) { lock( os.lock$ ); }
    211 inline void unlock( ifstream & os ) { unlock( os.lock$ ); }
     221void ?{}( ifstream & is, void * file ) {
     222        is.file$ = file;
     223        is.nlOnOff$ = false;
     224        is.acquired$ = false;
     225} // ?{}
    212226
    213227// public
    214228void ?{}( ifstream & is ) { is.file$ = 0p; }
    215 void ?{}( ifstream & is, const char name[], const char mode[] ) { open( is, name, mode ); }
    216 void ?{}( ifstream & is, const char name[] ) { open( is, name, "r" ); }
    217 void ^?{}( ifstream & is ) { close( is ); }
    218 
    219 bool fail( ifstream & is ) { return is.file$ == 0p || ferror( (FILE *)(is.file$) ); }
    220 void clear( ifstream & is ) { clearerr( (FILE *)(is.file$) ); }
     229
     230void ?{}( ifstream & is, const char name[], const char mode[] ) {
     231        open( is, name, mode );
     232} // ?{}
     233
     234void ?{}( ifstream & is, const char name[] ) {
     235        open( is, name, "r" );
     236} // ?{}
     237
     238void ^?{}( ifstream & is ) {
     239        close( is );
     240} // ^?{}
    221241
    222242void nlOn( ifstream & os ) { os.nlOnOff$ = true; }
    223243void nlOff( ifstream & os ) { os.nlOnOff$ = false; }
    224 
    225 void ends( ifstream & is ) {}
    226 
    227 bool eof( ifstream & is ) { return feof( (FILE *)(is.file$) ) != 0; }
     244bool getANL( ifstream & os ) { return os.nlOnOff$; }
     245
     246bool fail( ifstream & is ) {
     247        return is.file$ == 0p || ferror( (FILE *)(is.file$) );
     248} // fail
     249
     250void clear( ifstream & is ) {
     251        clearerr( (FILE *)(is.file$) );
     252} // clear
     253
     254void ends( ifstream & is ) {
     255        if ( is.acquired$ ) { is.acquired$ = false; release( is ); }
     256} // ends
     257
     258bool eof( ifstream & is ) {
     259        return feof( (FILE *)(is.file$) );
     260} // eof
    228261
    229262void open( ifstream & is, const char name[], const char mode[] ) {
    230         FILE * file;
    231     for ( cnt; 10 ) {
    232                 errno = 0;
    233                 file = fopen( name, mode );
    234           if ( file != 0p || errno != EINTR ) break;            // timer interrupt ?
    235           if ( cnt == 9 ) abort( "ifstream open EINTR spinning exceeded" );
    236     } // for
     263        FILE * file = fopen( name, mode );
     264        #ifdef __CFA_DEBUG__
    237265        if ( file == 0p ) {
    238266                throw (Open_Failure){ is };
    239267                // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
    240268        } // if
    241         (is){ file };                                                                           // initialize
     269        #endif // __CFA_DEBUG__
     270        is.file$ = file;
    242271} // open
    243272
    244 void open( ifstream & is, const char name[] ) { open( is, name, "r" ); }
    245 
    246 void close( ifstream & is ) with( is ) {
    247   if ( (FILE *)(file$) == 0p ) return;
    248   if ( (FILE *)(file$) == (FILE *)stdin ) return;
    249 
    250         int ret;
    251     for ( cnt; 10 ) {
    252                 errno = 0;
    253                 ret = fclose( (FILE *)(file$) );
    254           if ( ret != EOF || errno != EINTR ) break;            // timer interrupt ?
    255           if ( cnt == 9 ) abort( "ifstream close EINTR spinning exceeded" );
    256     } // for
    257         if ( ret == EOF ) {
     273void open( ifstream & is, const char name[] ) {
     274        open( is, name, "r" );
     275} // open
     276
     277void close( ifstream & is ) {
     278  if ( (FILE *)(is.file$) == 0p ) return;
     279  if ( (FILE *)(is.file$) == (FILE *)stdin ) return;
     280
     281        if ( fclose( (FILE *)(is.file$) ) == EOF ) {
    258282                throw (Close_Failure){ is };
    259283                // abort | IO_MSG "close input" | nl | strerror( errno );
    260284        } // if
    261         file$ = 0p;                                                                                     // safety after close
     285        is.file$ = 0p;
    262286} // close
    263287
     
    288312int fmt( ifstream & is, const char format[], ... ) {
    289313        va_list args;
     314
    290315        va_start( args, format );
    291 
    292         int len;
    293     for () {                                                                                    // no check for EINTR limit waiting for keyboard input
    294                 errno = 0;
    295                 len = vfscanf( (FILE *)(is.file$), format, args );
    296           if ( len != EOF || errno != EINTR ) break;            // timer interrupt ?
    297     } // for
     316        int len = vfscanf( (FILE *)(is.file$), format, args );
    298317        if ( len == EOF ) {
    299318                if ( ferror( (FILE *)(is.file$) ) ) {
     
    305324} // fmt
    306325
     326inline void acquire( ifstream & is ) {
     327        lock( is.lock$ );
     328        if ( ! is.acquired$ ) is.acquired$ = true;
     329        else unlock( is.lock$ );
     330} // acquire
     331
     332inline void release( ifstream & is ) {
     333        unlock( is.lock$ );
     334} // release
     335
     336void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); }
     337void ^?{}( isacquire & acq ) { release( acq.is ); }
     338
    307339static ifstream sinFile = { (FILE *)stdin };
    308340ifstream & sin = sinFile, & stdin = sinFile;
     
    315347
    316348// exception I/O constructors
    317 void ?{}( Open_Failure & ex, ofstream & ostream ) with(ex) {
    318         virtual_table = &Open_Failure_vt;
    319         ostream = &ostream;
    320         tag = 1;
    321 } // ?{}
    322 
    323 void ?{}( Open_Failure & ex, ifstream & istream ) with(ex) {
    324         virtual_table = &Open_Failure_vt;
    325         istream = &istream;
    326         tag = 0;
     349void ?{}( Open_Failure & this, ofstream & ostream ) {
     350        this.virtual_table = &Open_Failure_vt;
     351        this.ostream = &ostream;
     352        this.tag = 1;
     353} // ?{}
     354
     355void ?{}( Open_Failure & this, ifstream & istream ) {
     356        this.virtual_table = &Open_Failure_vt;
     357        this.istream = &istream;
     358        this.tag = 0;
    327359} // ?{}
    328360
     
    331363
    332364// exception I/O constructors
    333 void ?{}( Close_Failure & ex, ofstream & ostream ) with(ex) {
    334         virtual_table = &Close_Failure_vt;
    335         ostream = &ostream;
    336         tag = 1;
    337 } // ?{}
    338 
    339 void ?{}( Close_Failure & ex, ifstream & istream ) with(ex) {
    340         virtual_table = &Close_Failure_vt;
    341         istream = &istream;
    342         tag = 0;
     365void ?{}( Close_Failure & this, ofstream & ostream ) {
     366        this.virtual_table = &Close_Failure_vt;
     367        this.ostream = &ostream;
     368        this.tag = 1;
     369} // ?{}
     370
     371void ?{}( Close_Failure & this, ifstream & istream ) {
     372        this.virtual_table = &Close_Failure_vt;
     373        this.istream = &istream;
     374        this.tag = 0;
    343375} // ?{}
    344376
     
    347379
    348380// exception I/O constructors
    349 void ?{}( Write_Failure & ex, ofstream & ostream ) with(ex) {
    350         virtual_table = &Write_Failure_vt;
    351         ostream = &ostream;
    352         tag = 1;
    353 } // ?{}
    354 
    355 void ?{}( Write_Failure & ex, ifstream & istream ) with(ex) {
    356         virtual_table = &Write_Failure_vt;
    357         istream = &istream;
    358         tag = 0;
     381void ?{}( Write_Failure & this, ofstream & ostream ) {
     382        this.virtual_table = &Write_Failure_vt;
     383        this.ostream = &ostream;
     384        this.tag = 1;
     385} // ?{}
     386
     387void ?{}( Write_Failure & this, ifstream & istream ) {
     388        this.virtual_table = &Write_Failure_vt;
     389        this.istream = &istream;
     390        this.tag = 0;
    359391} // ?{}
    360392
     
    363395
    364396// exception I/O constructors
    365 void ?{}( Read_Failure & ex, ofstream & ostream ) with(ex) {
    366         virtual_table = &Read_Failure_vt;
    367         ostream = &ostream;
    368         tag = 1;
    369 } // ?{}
    370 
    371 void ?{}( Read_Failure & ex, ifstream & istream ) with(ex) {
    372         virtual_table = &Read_Failure_vt;
    373         istream = &istream;
    374         tag = 0;
     397void ?{}( Read_Failure & this, ofstream & ostream ) {
     398        this.virtual_table = &Read_Failure_vt;
     399        this.ostream = &ostream;
     400        this.tag = 1;
     401} // ?{}
     402
     403void ?{}( Read_Failure & this, ifstream & istream ) {
     404        this.virtual_table = &Read_Failure_vt;
     405        this.istream = &istream;
     406        this.tag = 0;
    375407} // ?{}
    376408
  • libcfa/src/fstream.hfa

    rb7fd9daf rf95634e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 10 09:37:32 2021
    13 // Update Count     : 243
     12// Last Modified On : Wed Jul 28 07:35:50 2021
     13// Update Count     : 234
    1414//
    1515
     
    3636        char tupleSeparator$[ofstream_sepSize];
    3737        multiple_acquisition_lock lock$;
     38        bool acquired$;
    3839}; // ofstream
    3940
     
    5253void setPrt$( ofstream &, bool );
    5354
    54 void lock( ofstream & );
    55 void unlock( ofstream & );
    56 
    5755// public
    5856void sepOn( ofstream & );
     
    7775void open( ofstream &, const char name[] );
    7876void close( ofstream & );
    79 
    8077ofstream & write( ofstream &, const char data[], size_t size );
     78
     79void acquire( ofstream & );
     80void release( ofstream & );
     81
     82struct osacquire {
     83        ofstream & os;
     84};
     85void ?{}( osacquire & acq, ofstream & );
     86void ^?{}( osacquire & acq );
    8187
    8288void ?{}( ofstream & );
     
    101107        bool nlOnOff$;
    102108        multiple_acquisition_lock lock$;
     109        bool acquired$;
    103110}; // ifstream
    104111
    105112// Satisfies istream
    106 
    107 // private
    108 bool getANL$( ifstream & );
    109 
    110 void lock( ifstream & );
    111 void unlock( ifstream & );
    112113
    113114// public
    114115void nlOn( ifstream & );
    115116void nlOff( ifstream & );
     117bool getANL( ifstream & );
    116118void ends( ifstream & );
    117119int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     
    123125void open( ifstream & is, const char name[] );
    124126void close( ifstream & is );
    125 
    126127ifstream & read( ifstream & is, char data[], size_t size );
    127128ifstream & ungetc( ifstream & is, char c );
     129
     130void acquire( ifstream & is );
     131void release( ifstream & is );
     132
     133struct isacquire {
     134        ifstream & is;
     135};
     136void ?{}( isacquire & acq, ifstream & is );
     137void ^?{}( isacquire & acq );
    128138
    129139void ?{}( ifstream & is );
  • libcfa/src/heap.cfa

    rb7fd9daf rf95634e  
    102102} // prtUnfreed
    103103
    104 extern int cfa_main_returned;                                                   // from interpose.cfa
     104extern int cfa_main_returned;                                                   // from bootloader.cf
    105105extern "C" {
    106106        void heapAppStart() {                                                           // called by __cfaabi_appready_startup
  • libcfa/src/interpose.cfa

    rb7fd9daf rf95634e  
    9494} __cabi_libc;
    9595
    96 int cfa_main_returned;
    97 
    9896extern "C" {
    9997        void __cfaabi_interpose_startup( void ) {
    10098                const char *version = 0p;
    101                 cfa_main_returned = 0;
    10299
    103100                preload_libgcc();
  • libcfa/src/iostream.cfa

    rb7fd9daf rf95634e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 10 09:28:17 2021
    13 // Update Count     : 1345
     12// Last Modified On : Sat May 15 09:39:21 2021
     13// Update Count     : 1342
    1414//
    1515
     
    398398                return os;
    399399        } // nlOff
     400} // distribution
     401
     402forall( ostype & | ostream( ostype ) ) {
     403        ostype & acquire( ostype & os ) {
     404                acquire( os );                                                                  // call void returning
     405                return os;
     406        } // acquire
    400407} // distribution
    401408
     
    822829                        fmt( is, "%c", &temp );                                         // must pass pointer through varg to fmt
    823830                        // do not overwrite parameter with newline unless appropriate
    824                         if ( temp != '\n' || getANL$( is ) ) { c = temp; break; }
     831                        if ( temp != '\n' || getANL( is ) ) { c = temp; break; }
    825832                        if ( eof( is ) ) break;
    826833                } // for
     
    10281035                return is;
    10291036        } // nlOff
     1037} // distribution
     1038
     1039forall( istype & | istream( istype ) ) {
     1040        istype & acquire( istype & is ) {
     1041                acquire( is );                                                                  // call void returning
     1042                return is;
     1043        } // acquire
    10301044} // distribution
    10311045
  • libcfa/src/iostream.hfa

    rb7fd9daf rf95634e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 10 10:02:07 2021
    13 // Update Count     : 407
     12// Last Modified On : Wed Apr 28 20:37:56 2021
     13// Update Count     : 401
    1414//
    1515
     
    5858        void close( ostype & );
    5959        ostype & write( ostype &, const char [], size_t );
     60        void acquire( ostype & );                                                       // concurrent access
    6061}; // ostream
    6162
     
    141142        ostype & nlOn( ostype & );
    142143        ostype & nlOff( ostype & );
     144} // distribution
     145
     146forall( ostype & | ostream( ostype ) ) {
     147        ostype & acquire( ostype & );
    143148} // distribution
    144149
     
    291296
    292297trait basic_istream( istype & ) {
    293         // private
    294         bool getANL$( istype & );                                                       // get scan newline (on/off)
    295         // public
     298        bool getANL( istype & );                                                        // get scan newline (on/off)
    296299        void nlOn( istype & );                                                          // read newline
    297300        void nlOff( istype & );                                                         // scan newline
     301
    298302        void ends( istype & os );                                                       // end of output statement
    299303        int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     
    308312        void close( istype & is );
    309313        istype & read( istype &, char [], size_t );
     314        void acquire( istype & );                                                       // concurrent access
    310315}; // istream
    311316
     
    374379} // distribution
    375380
     381forall( istype & | istream( istype ) ) {
     382        istype & acquire( istype & );
     383} // distribution
     384
    376385// *********************************** manipulators ***********************************
    377386
  • libcfa/src/memory.cfa

    rb7fd9daf rf95634e  
    155155
    156156forall(T &)
    157 T * release(unique_ptr(T) & this) {
    158         T * data = this.data;
    159         this.data = 0p;
    160         return data;
    161 }
    162 
    163 forall(T &)
    164157int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that) {
    165158        return this.data == that.data;
  • libcfa/src/memory.hfa

    rb7fd9daf rf95634e  
    9494
    9595forall(T &)
    96 T * release(unique_ptr(T) & this);
    97 
    98 forall(T &)
    9996int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that);
    10097forall(T &)
  • libcfa/src/strstream.cfa

    rb7fd9daf rf95634e  
    1010// Created On       : Thu Apr 22 22:24:35 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 10 16:13:20 2021
    13 // Update Count     : 101
     12// Last Modified On : Tue Apr 27 20:59:53 2021
     13// Update Count     : 78
    1414//
    1515
    1616#include "strstream.hfa"
    17 #include "fstream.hfa"                                                                  // abort
    1817
    1918#include <stdio.h>                                                                              // vsnprintf
     
    3130
    3231// private
    33 inline bool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
    34 inline void sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; }
    35 inline void sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
    36 inline const char * sepGetCur$( ostrstream & os ) { return os.sepCur$; }
    37 inline void sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
    38 inline bool getNL$( ostrstream & os ) { return os.sawNL$; }
    39 inline void setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; }
    40 inline bool getANL$( ostrstream & os ) { return os.nlOnOff$; }
    41 inline bool getPrt$( ostrstream & os ) { return os.prt$; }
    42 inline void setPrt$( ostrstream & os, bool state ) { os.prt$ = state; }
     32bool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
     33void sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; }
     34void sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
     35const char * sepGetCur$( ostrstream & os ) { return os.sepCur$; }
     36void sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
     37bool getNL$( ostrstream & os ) { return os.sawNL$; }
     38void setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; }
     39bool getANL$( ostrstream & os ) { return os.nlOnOff$; }
     40bool getPrt$( ostrstream & os ) { return os.prt$; }
     41void setPrt$( ostrstream & os, bool state ) { os.prt$ = state; }
    4342
    4443// public
     
    129128// *********************************** istrstream ***********************************
    130129
    131 // private
    132 bool getANL$( istrstream & is ) { return is.nlOnOff$; }
    133130
    134131// public
     
    139136} // ?{}
    140137
     138bool getANL( istrstream & is ) { return is.nlOnOff$; }
    141139void nlOn( istrstream & is ) { is.nlOnOff$ = true; }
    142140void nlOff( istrstream & is ) { is.nlOnOff$ = false; }
    143141
    144 void ends( istrstream & is ) {}
    145 bool eof( istrstream & is ) { return false; }
     142void ends( istrstream & is ) {
     143} // ends
    146144
    147 int fmt( istrstream & is, const char format[], ... ) with(is) {
    148         va_list args;
    149         va_start( args, format );
    150         // THIS DOES NOT WORK BECAUSE VSSCANF RETURNS NUMBER OF VALUES READ VERSUS BUFFER POSITION SCANNED.
    151         int len = vsscanf( buf$ + cursor$, format, args );
    152         va_end( args );
    153         if ( len == EOF ) {
    154                 abort | IO_MSG "invalid read";
    155         } // if
    156         // SKULLDUGGERY: This hack skips over characters read by vsscanf by moving to the next whitespace but it does not
    157         // handle C reads with wdi manipulators that leave the cursor at a non-whitespace character.
    158         for ( ; buf$[cursor$] != ' ' && buf$[cursor$] != '\t' && buf$[cursor$] != '\0'; cursor$ += 1 ) {
    159                 //printf( "X \'%c\'\n", buf$[cursor$] );
    160         } // for
    161         if ( buf$[cursor$] != '\0' ) cursor$ += 1;      // advance to whitespace
    162         return len;
    163 } // fmt
     145int eof( istrstream & is ) {
     146        return 0;
     147} // eof
    164148
    165149istrstream &ungetc( istrstream & is, char c ) {
     
    170154} // ungetc
    171155
     156int fmt( istrstream & is, const char format[], ... ) {
     157        va_list args;
     158        va_start( args, format );
     159        // This does not work because vsscanf does not return buffer position.
     160        int len = vsscanf( is.buf$ + is.cursor$, format, args );
     161        va_end( args );
     162        if ( len == EOF ) {
     163                int j;
     164                printf( "X %d%n\n", len, &j );
     165        } // if
     166        is.cursor$ += len;
     167        return len;
     168} // fmt
     169
    172170// Local Variables: //
    173171// tab-width: 4 //
  • libcfa/src/strstream.hfa

    rb7fd9daf rf95634e  
    1010// Created On       : Thu Apr 22 22:20:59 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 10 10:14:22 2021
    13 // Update Count     : 47
     12// Last Modified On : Tue Apr 27 20:58:50 2021
     13// Update Count     : 41
    1414//
    1515
     
    8585// Satisfies basic_istream
    8686
    87 // private
    88 bool getANL$( istrstream & );
    89 
    9087// public
     88bool getANL( istrstream & );
    9189void nlOn( istrstream & );
    9290void nlOff( istrstream & );
    9391void ends( istrstream & );
     92int fmt( istrstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     93istrstream & ungetc( istrstream & is, char c );
     94int eof( istrstream & is );
    9495
    95 int fmt( istrstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
    96 istrstream & ungetc( istrstream &, char );
    97 bool eof( istrstream & );
    98 
    99 void ?{}( istrstream &, char buf[] );
     96void ?{}( istrstream & is, char buf[] );
    10097
    10198// Local Variables: //
  • src/AST/Decl.hpp

    rb7fd9daf rf95634e  
    131131        // declared type, derived from parameter declarations
    132132        ptr<FunctionType> type;
    133         /// Null for the forward declaration of a function.
    134133        ptr<CompoundStmt> stmts;
    135134        std::vector< ptr<Expr> > withExprs;
  • src/AST/Pass.hpp

    rb7fd9daf rf95634e  
    348348
    349349        /// When this node is finished being visited, restore the value of a variable
    350         /// You may assign to the return value to set the new value in the same statement.
    351350        template< typename T >
    352         T& GuardValue( T& val ) {
     351        void GuardValue( T& val ) {
    353352                at_cleanup( [ val ]( void * newVal ) {
    354353                        * static_cast< T * >( newVal ) = val;
    355354                }, static_cast< void * >( & val ) );
    356                 return val;
    357355        }
    358356
     
    396394};
    397395
    398 /// Used to get a pointer to the wrapping TranslationUnit.
    399 struct WithConstTranslationUnit {
    400         const TranslationUnit * translationUnit = nullptr;
    401 
    402         const TranslationUnit & transUnit() const {
    403                 assertf( translationUnit, "WithConstTranslationUnit not set-up." );
    404                 return *translationUnit;
    405         }
    406 };
    407 
    408396}
    409397
  • src/AST/Pass.impl.hpp

    rb7fd9daf rf95634e  
    420420template< typename core_t >
    421421inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & visitor ) {
    422         if ( auto ptr = __pass::translation_unit::get_cptr( visitor.core, 0 ) ) {
    423                 ValueGuard<const TranslationUnit *> guard( *ptr );
    424                 *ptr = &unit;
    425                 return ast::accept_all( unit.decls, visitor );
    426         } else {
    427                 return ast::accept_all( unit.decls, visitor );
    428         }
     422        return ast::accept_all( unit.decls, visitor );
    429423}
    430424
  • src/AST/Pass.proto.hpp

    rb7fd9daf rf95634e  
    426426        } // namespace forall
    427427
    428         // For passes that need access to the global context. Sreaches `translationUnit`
    429         namespace translation_unit {
    430                 template<typename core_t>
    431                 static inline auto get_cptr( core_t & core, int )
    432                                 -> decltype( &core.translationUnit ) {
    433                         return &core.translationUnit;
    434                 }
    435 
    436                 template<typename core_t>
    437                 static inline const TranslationUnit ** get_cptr( core_t &, long ) {
    438                         return nullptr;
    439                 }
    440         }
    441 
    442428        template<typename core_t>
    443429        static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) {
  • src/AST/Stmt.hpp

    rb7fd9daf rf95634e  
    175175class CaseStmt final : public Stmt {
    176176public:
    177         /// Null for the default label.
    178177        ptr<Expr> cond;
    179178        std::vector<ptr<Stmt>> stmts;
  • src/AST/TranslationUnit.hpp

    rb7fd9daf rf95634e  
    2626        std::list< ptr< Decl > > decls;
    2727
    28         struct Global {
     28        struct Globals {
    2929                std::map< UniqueId, Decl * > idMap;
    3030
    31                 ptr<Type> sizeType;
     31                const Type * sizeType;
    3232                const FunctionDecl * dereference;
    3333                const StructDecl * dtorStruct;
  • src/AST/porting.md

    rb7fd9daf rf95634e  
    9898        * `Initializer` => `ast::Init`
    9999    * `Statement` => `ast::Stmt`
    100     * `ReferenceToType` => `ast::BaseInstType`
    101100        * any field names should follow a similar renaming
    102101  * because they don't really belong to `Type` (and for consistency with `Linkage::Spec`):
  • src/CodeGen/FixMain.cc

    rb7fd9daf rf95634e  
    2222#include <string>                  // for operator<<
    2323
    24 #include "AST/Decl.hpp"
    25 #include "AST/Type.hpp"
    26 #include "Common/PassVisitor.h"
    2724#include "Common/SemanticError.h"  // for SemanticError
    2825#include "CodeGen/GenType.h"       // for GenType
     
    3229
    3330namespace CodeGen {
    34 
    35 namespace {
    36 
    37 struct FindMainCore {
    38         FunctionDecl * main_signature = nullptr;
    39 
    40         void previsit( FunctionDecl * decl ) {
    41                 if ( FixMain::isMain( decl ) ) {
    42                         if ( main_signature ) {
    43                                 SemanticError( decl, "Multiple definition of main routine\n" );
    44                         }
    45                         main_signature = decl;
    46                 }
    47         }
    48 };
    49 
    50 }
    51 
    5231        bool FixMain::replace_main = false;
     32        std::unique_ptr<FunctionDecl> FixMain::main_signature = nullptr;
    5333
    5434        template<typename container>
     
    5737        }
    5838
    59         void FixMain::fix( std::list< Declaration * > & translationUnit,
    60                         std::ostream &os, const char* bootloader_filename ) {
    61                 PassVisitor< FindMainCore > main_finder;
    62                 acceptAll( translationUnit, main_finder );
    63                 FunctionDecl * main_signature = main_finder.pass.main_signature;
     39        void FixMain::registerMain(FunctionDecl* functionDecl)
     40        {
     41                if(main_signature) {
     42                        SemanticError(functionDecl, "Multiple definition of main routine\n");
     43                }
     44                main_signature.reset( functionDecl->clone() );
     45        }
    6446
     47        void FixMain::fix(std::ostream &os, const char* bootloader_filename) {
    6548                if( main_signature ) {
    6649                        os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return ";
    67                         main_signature->mangleName = SymTab::Mangler::mangle(main_signature);
     50                        main_signature->mangleName = SymTab::Mangler::mangle(main_signature.get());
    6851
    6952                        os << main_signature->get_scopedMangleName() << "(";
     
    8265                }
    8366        }
    84 
    85 namespace {
    86 
    87 ObjectDecl * signedIntObj() {
    88         return new ObjectDecl(
    89                 "", Type::StorageClasses(), LinkageSpec::Cforall, 0,
    90                 new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr );
    91 }
    92 
    93 ObjectDecl * charStarObj() {
    94         return new ObjectDecl(
    95                 "", Type::StorageClasses(), LinkageSpec::Cforall, 0,
    96                 new PointerType( Type::Qualifiers(),
    97                         new PointerType( Type::Qualifiers(),
    98                                 new BasicType( Type::Qualifiers(), BasicType::Char ) ) ),
    99                 nullptr );
    100 }
    101 
    102 std::string create_mangled_main_function_name( FunctionType * function_type ) {
    103         std::unique_ptr<FunctionDecl> decl( new FunctionDecl(
    104                 "main", Type::StorageClasses(), LinkageSpec::Cforall,
    105                 function_type, nullptr ) );
    106         return SymTab::Mangler::mangle( decl.get() );
    107 }
    108 
    109 std::string mangled_0_argument_main() {
    110         FunctionType* main_type = new FunctionType( Type::Qualifiers(), true );
    111         main_type->get_returnVals().push_back( signedIntObj() );
    112         return create_mangled_main_function_name( main_type );
    113 }
    114 
    115 std::string mangled_2_argument_main() {
    116         FunctionType* main_type = new FunctionType( Type::Qualifiers(), false );
    117         main_type->get_returnVals().push_back( signedIntObj() );
    118         main_type->get_parameters().push_back( signedIntObj() );
    119         main_type->get_parameters().push_back( charStarObj() );
    120         return create_mangled_main_function_name( main_type );
    121 }
    122 
    123 bool is_main( const std::string & mangled_name ) {
    124         // This breaks if you move it out of the function.
    125         static const std::string mangled_mains[] = {
    126                 mangled_0_argument_main(),
    127                 mangled_2_argument_main(),
    128                 //mangled_3_argument_main(),
    129         };
    130 
    131         for ( auto main_name : mangled_mains ) {
    132                 if ( main_name == mangled_name ) return true;
    133         }
    134         return false;
    135 }
    136 
    137 } // namespace
    138 
    139 bool FixMain::isMain( FunctionDecl * decl ) {
    140         if ( std::string("main") != decl->name ) {
    141                 return false;
    142         }
    143         return is_main( SymTab::Mangler::mangle( decl, true, true ) );
    144 }
    145 
    146 bool FixMain::isMain( const ast::FunctionDecl * decl ) {
    147         if ( std::string("main") != decl->name ) {
    148                 return false;
    149         }
    150         return is_main( Mangle::mangle( decl, Mangle::Type ) );
    151 }
    152 
    15367};
  • src/CodeGen/FixMain.h

    rb7fd9daf rf95634e  
    99// Author           : Thierry Delisle
    1010// Created On       : Thr Jan 12 14:11:09 2017
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Oct 29 16:20:00 2021
    13 // Update Count     : 8
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Sun Feb 16 03:24:32 2020
     13// Update Count     : 5
    1414//
    1515
     
    1818#include <iosfwd>
    1919#include <memory>
    20 #include <list>
    2120
    2221#include "SynTree/LinkageSpec.h"
    2322
    24 class Declaration;
    2523class FunctionDecl;
    26 namespace ast {
    27         class FunctionDecl;
    28 }
    2924
    3025namespace CodeGen {
     26        class FixMain {
     27          public :
     28                static inline LinkageSpec::Spec mainLinkage() {
     29                        return replace_main ? LinkageSpec::Cforall : LinkageSpec::C;
     30                }
     31               
     32                static inline void setReplaceMain(bool val) {
     33                        replace_main = val;
     34                }
    3135
    32 class FixMain {
    33 public :
    34         static inline LinkageSpec::Spec mainLinkage() {
    35                 return replace_main ? LinkageSpec::Cforall : LinkageSpec::C;
    36         }
     36                static void registerMain(FunctionDecl* val);
    3737
    38         static inline void setReplaceMain(bool val) {
    39                 replace_main = val;
    40         }
     38                static void fix(std::ostream &os, const char* bootloader_filename);
    4139
    42         static bool isMain(FunctionDecl* decl);
    43         static bool isMain(const ast::FunctionDecl * decl);
    44 
    45         static void fix( std::list< Declaration * > & decls,
    46                         std::ostream &os, const char* bootloader_filename );
    47 
    48 private:
    49         static bool replace_main;
    50 };
    51 
     40          private:
     41                static bool replace_main;
     42                static std::unique_ptr<FunctionDecl> main_signature;
     43        };
    5244} // namespace CodeGen
  • src/CodeGen/FixNames.cc

    rb7fd9daf rf95634e  
    99// Author           : Richard C. Bilson
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Oct 29 15:49:00 2021
    13 // Update Count     : 23
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Fri Dec 13 23:39:14 2019
     13// Update Count     : 21
    1414//
    1515
     
    1919#include <string>                  // for string, operator!=, operator==
    2020
    21 #include "AST/Chain.hpp"
    22 #include "AST/Expr.hpp"
    23 #include "AST/Pass.hpp"
    2421#include "Common/PassVisitor.h"
    2522#include "Common/SemanticError.h"  // for SemanticError
     
    4946        };
    5047
     48        std::string mangle_main() {
     49                FunctionType* main_type;
     50                std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,
     51                                                                                                                                   main_type = new FunctionType( Type::Qualifiers(), true ), nullptr )
     52                                };
     53                main_type->get_returnVals().push_back(
     54                        new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
     55                );
     56
     57                auto && name = SymTab::Mangler::mangle( mainDecl.get() );
     58                // std::cerr << name << std::endl;
     59                return std::move(name);
     60        }
     61        std::string mangle_main_args() {
     62                FunctionType* main_type;
     63                std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,
     64                                                                                                                                   main_type = new FunctionType( Type::Qualifiers(), false ), nullptr )
     65                                };
     66                main_type->get_returnVals().push_back(
     67                        new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
     68                );
     69
     70                main_type->get_parameters().push_back(
     71                        new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
     72                );
     73
     74                main_type->get_parameters().push_back(
     75                        new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0,
     76                        new PointerType( Type::Qualifiers(), new PointerType( Type::Qualifiers(), new BasicType( Type::Qualifiers(), BasicType::Char ) ) ),
     77                        nullptr )
     78                );
     79
     80                auto&& name = SymTab::Mangler::mangle( mainDecl.get() );
     81                // std::cerr << name << std::endl;
     82                return std::move(name);
     83        }
     84
     85        bool is_main(const std::string& name) {
     86                static std::string mains[] = {
     87                        mangle_main(),
     88                        mangle_main_args()
     89                };
     90
     91                for(const auto& m : mains) {
     92                        if( name == m ) return true;
     93                }
     94                return false;
     95        }
     96
    5197        void fixNames( std::list< Declaration* > & translationUnit ) {
    5298                PassVisitor<FixNames> fixer;
     
    72118                fixDWT( functionDecl );
    73119
    74                 if ( FixMain::isMain( functionDecl ) ) {
     120                if(is_main( SymTab::Mangler::mangle(functionDecl, true, true) )) {
    75121                        int nargs = functionDecl->get_functionType()->get_parameters().size();
    76122                        if( !(nargs == 0 || nargs == 2 || nargs == 3) ) {
     
    78124                        }
    79125                        functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) );
     126                        CodeGen::FixMain::registerMain( functionDecl );
    80127                }
    81128        }
     
    85132                GuardAction( [this](){ scopeLevel--; } );
    86133        }
    87 
    88 /// Does work with the main function and scopeLevels.
    89 class FixNames_new : public ast::WithGuards {
    90         int scopeLevel = 1;
    91 
    92         bool shouldSetScopeLevel( const ast::DeclWithType * dwt ) {
    93                 return !dwt->name.empty() && dwt->linkage.is_mangled
    94                         && dwt->scopeLevel != scopeLevel;
    95         }
    96 public:
    97         const ast::ObjectDecl *postvisit( const ast::ObjectDecl *objectDecl ) {
    98                 if ( shouldSetScopeLevel( objectDecl ) ) {
    99                         return ast::mutate_field( objectDecl, &ast::ObjectDecl::scopeLevel, scopeLevel );
    100                 }
    101                 return objectDecl;
    102         }
    103 
    104         const ast::FunctionDecl *postvisit( const ast::FunctionDecl *functionDecl ) {
    105                 // This store is used to ensure a maximum of one call to mutate.
    106                 ast::FunctionDecl * mutDecl = nullptr;
    107 
    108                 if ( shouldSetScopeLevel( functionDecl ) ) {
    109                         mutDecl = ast::mutate( functionDecl );
    110                         mutDecl->scopeLevel = scopeLevel;
    111                 }
    112 
    113                 if ( FixMain::isMain( functionDecl ) ) {
    114                         if ( !mutDecl ) { mutDecl = ast::mutate( functionDecl ); }
    115 
    116                         int nargs = mutDecl->params.size();
    117                         if ( 0 != nargs && 2 != nargs && 3 != nargs ) {
    118                                 SemanticError( functionDecl, "Main expected to have 0, 2 or 3 arguments\n" );
    119                         }
    120                         ast::chain_mutate( mutDecl->stmts )->kids.push_back(
    121                                 new ast::ReturnStmt(
    122                                         mutDecl->location,
    123                                         ast::ConstantExpr::from_int( mutDecl->location, 0 )
    124                                 )
    125                         );
    126                 }
    127                 return mutDecl ? mutDecl : functionDecl;
    128         }
    129 
    130         void previsit( const ast::CompoundStmt * ) {
    131                 GuardValue( scopeLevel ) += 1;
    132         }
    133 };
    134 
    135 void fixNames( ast::TranslationUnit & translationUnit ) {
    136         ast::Pass<FixNames_new>::run( translationUnit );
    137 }
    138 
    139134} // namespace CodeGen
    140135
  • src/CodeGen/FixNames.h

    rb7fd9daf rf95634e  
    99// Author           : Richard C. Bilson
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Oct 26 13:47:00 2021
    13 // Update Count     : 4
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Fri Jul 21 22:17:33 2017
     13// Update Count     : 3
    1414//
    1515
     
    1919
    2020class Declaration;
    21 namespace ast {
    22         struct TranslationUnit;
    23 }
    2421
    2522namespace CodeGen {
    2623        /// mangles object and function names
    2724        void fixNames( std::list< Declaration* > & translationUnit );
    28         void fixNames( ast::TranslationUnit & translationUnit );
    2925} // namespace CodeGen
    3026
  • src/CodeTools/DeclStats.cc

    rb7fd9daf rf95634e  
    156156                /// number of counting bins for linkages
    157157                static const unsigned n_named_specs = 8;
    158                 /// Mapping function from linkage to bin.
    159                 static unsigned linkage_index( LinkageSpec::Spec spec ) {
    160                         switch ( spec ) {
    161                         case LinkageSpec::Intrinsic:  return 0;
    162                         case LinkageSpec::C:          return 1;
    163                         case LinkageSpec::Cforall:    return 2;
    164                         case LinkageSpec::AutoGen:    return 3;
    165                         case LinkageSpec::Compiler:   return 4;
    166                         case LinkageSpec::BuiltinCFA: return 5;
    167                         case LinkageSpec::BuiltinC:   return 6;
    168                         default:                      return 7;
    169                         }
    170                 }
     158                /// map from total number of specs to bins
     159                static const unsigned ind_for_linkage[16];
    171160
    172161                Stats for_linkage[n_named_specs];            ///< Stores separate stats per linkage
     
    377366                        const std::string& mangleName = decl->get_mangleName().empty() ? decl->name : decl->get_mangleName();
    378367                        if ( seen_names.insert( mangleName ).second ) {
    379                                 Stats& stats = for_linkage[ linkage_index( decl->linkage ) ];
     368                                Stats& stats = for_linkage[ ind_for_linkage[ decl->linkage ] ];
    380369
    381370                                ++stats.n_decls;
     
    538527        };
    539528
     529        const unsigned DeclStats::ind_for_linkage[]
     530                = { 7, 7, 2, 1,   7, 7, 7, 3,   4, 7, 6, 5,   7, 7, 7, 0 };
     531
    540532        void printDeclStats( std::list< Declaration * > &translationUnit ) {
    541533                PassVisitor<DeclStats> stats;
  • src/Common/module.mk

    rb7fd9daf rf95634e  
    2222      Common/CompilerError.h \
    2323      Common/Debug.h \
    24       Common/DeclStats.hpp \
    25       Common/DeclStats.cpp \
    2624      Common/ErrorObjects.h \
    2725      Common/Eval.cc \
     
    3533      Common/PassVisitor.proto.h \
    3634      Common/PersistentMap.h \
    37       Common/ResolvProtoDump.hpp \
    38       Common/ResolvProtoDump.cpp \
    3935      Common/ScopedMap.h \
    4036      Common/SemanticError.cc \
  • src/Concurrency/Keywords.cc

    rb7fd9daf rf95634e  
    9393                ObjectDecl * addField( StructDecl * );
    9494                void addRoutines( ObjectDecl *, FunctionDecl * );
    95                 void addLockUnlockRoutines( StructDecl * );
    9695
    9796                virtual bool is_target( StructDecl * decl ) = 0;
     
    323322                StructDecl* dtor_guard_decl = nullptr;
    324323                StructDecl* thread_guard_decl = nullptr;
    325                 StructDecl* lock_guard_decl = nullptr;
    326324
    327325                static std::unique_ptr< Type > generic_func;
     
    465463        }
    466464
     465
    467466        void ConcurrentSueKeyword::handle( StructDecl * decl ) {
    468467                if( ! decl->body ) return;
     
    480479                FunctionDecl * func = forwardDeclare( decl );
    481480                ObjectDecl * field = addField( decl );
    482 
    483                 // add get_.* routine
    484481                addRoutines( field, func );
    485                 // add lock/unlock routines to monitors for use by mutex stmt
    486                 addLockUnlockRoutines( decl );
    487482        }
    488483
     
    617612        }
    618613
    619         // This function adds the get_.* routine body for coroutines, monitors etc
    620         //              after their corresponding struct has been made
    621614        void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) {
    622615                CompoundStmt * statement = new CompoundStmt();
     
    641634
    642635                declsToAddAfter.push_back( get_decl );
    643         }
    644 
    645         // Generates lock/unlock routines for monitors to be used by mutex stmts
    646         void ConcurrentSueKeyword::addLockUnlockRoutines( StructDecl * decl ) {
    647                 // this routine will be called for all ConcurrentSueKeyword children so only continue if we are a monitor
    648                 if ( !decl->is_monitor() ) return;
    649 
    650                 FunctionType * lock_fn_type = new FunctionType( noQualifiers, false );
    651                 FunctionType * unlock_fn_type = new FunctionType( noQualifiers, false );
    652 
    653                 // create this ptr parameter for both routines
    654                 ObjectDecl * this_decl = new ObjectDecl(
    655                         "this",
    656                         noStorageClasses,
    657                         LinkageSpec::Cforall,
    658                         nullptr,
    659                         new ReferenceType(
    660                                 noQualifiers,
    661                                 new StructInstType(
    662                                         noQualifiers,
    663                                         decl
    664                                 )
    665                         ),
    666                         nullptr
    667                 );
    668 
    669                 lock_fn_type->get_parameters().push_back( this_decl->clone() );
    670                 unlock_fn_type->get_parameters().push_back( this_decl->clone() );
    671                 fixupGenerics(lock_fn_type, decl);
    672                 fixupGenerics(unlock_fn_type, decl);
    673 
    674                 delete this_decl;
    675 
    676 
    677                 //////////////////////////////////////////////////////////////////////
    678                 // The following generates this lock routine for all monitors
    679                 /*
    680                         void lock (monitor_t & this) {
    681                                 lock(get_monitor(this));
    682                         }       
    683                 */
    684                 FunctionDecl * lock_decl = new FunctionDecl(
    685                         "lock",
    686                         Type::Static,
    687                         LinkageSpec::Cforall,
    688                         lock_fn_type,
    689                         nullptr,
    690                         { },
    691                         Type::Inline
    692                 );
    693 
    694                 UntypedExpr * get_monitor_lock =  new UntypedExpr (
    695                         new NameExpr( "get_monitor" ),
    696                         { new VariableExpr( lock_fn_type->get_parameters().front() ) }
    697                 );
    698 
    699                 CompoundStmt * lock_statement = new CompoundStmt();
    700                 lock_statement->push_back(
    701                         new ExprStmt(
    702                                 new UntypedExpr (
    703                                         new NameExpr( "lock" ),
    704                                         {
    705                                                 get_monitor_lock
    706                                         }
    707                                 )
    708                         )
    709                 );
    710                 lock_decl->set_statements( lock_statement );
    711 
    712                 //////////////////////////////////////////////////////////////////
    713                 // The following generates this routine for all monitors
    714                 /*
    715                         void unlock (monitor_t & this) {
    716                                 unlock(get_monitor(this));
    717                         }       
    718                 */
    719                 FunctionDecl * unlock_decl = new FunctionDecl(
    720                         "unlock",
    721                         Type::Static,
    722                         LinkageSpec::Cforall,
    723                         unlock_fn_type,
    724                         nullptr,
    725                         { },
    726                         Type::Inline
    727                 );
    728 
    729                 CompoundStmt * unlock_statement = new CompoundStmt();
    730 
    731                 UntypedExpr * get_monitor_unlock =  new UntypedExpr (
    732                         new NameExpr( "get_monitor" ),
    733                         { new VariableExpr( unlock_fn_type->get_parameters().front() ) }
    734                 );
    735 
    736                 unlock_statement->push_back(
    737                         new ExprStmt(
    738                                 new UntypedExpr(
    739                                         new NameExpr( "unlock" ),
    740                                         {
    741                                                 get_monitor_unlock
    742                                         }
    743                                 )
    744                         )
    745                 );
    746                 unlock_decl->set_statements( unlock_statement );
    747                
    748                 // pushes routines to declsToAddAfter to add at a later time
    749                 declsToAddAfter.push_back( lock_decl );
    750                 declsToAddAfter.push_back( unlock_decl );
    751636        }
    752637
     
    1052937                        assert( !thread_guard_decl );
    1053938                        thread_guard_decl = decl;
    1054                 }
    1055                 else if ( decl->name == "__mutex_stmt_lock_guard" && decl->body ) {
    1056                         assert( !lock_guard_decl );
    1057                         lock_guard_decl = decl;
    1058939                }
    1059940        }
     
    12001081                                new PointerType(
    12011082                                        noQualifiers,
    1202                                         //new TypeofType( noQualifiers, args.front()->clone() )
    1203                                         new TypeofType( noQualifiers, new UntypedExpr(
    1204                                                         new NameExpr( "__get_type" ),
    1205                                                         { args.front()->clone() }
    1206                                                 )
     1083                                        new StructInstType(
     1084                                                noQualifiers,
     1085                                                monitor_decl
    12071086                                        )
    12081087                                ),
     
    12141093                                map_range < std::list<Initializer*> > ( args, [](Expression * var ){
    12151094                                        return new SingleInit( new UntypedExpr(
    1216                                                         new NameExpr( "__get_ptr" ),
    1217                                                         { var }
     1095                                                new NameExpr( "get_monitor" ),
     1096                                                { var }
    12181097                                        ) );
    1219                                         //return new SingleInit( new AddressExpr( var ) );
    12201098                                })
    12211099                        )
    12221100                );
    1223 
    1224                 StructInstType * lock_guard_struct = new StructInstType( noQualifiers, lock_guard_decl );
    1225                 TypeExpr * lock_type_expr = new TypeExpr(
    1226                         new TypeofType( noQualifiers, new UntypedExpr(
    1227                                 new NameExpr( "__get_type" ),
    1228                                 { args.front()->clone() }
    1229                                 )
    1230                         )
    1231                 );
    1232 
    1233                 lock_guard_struct->parameters.push_back( lock_type_expr ) ;
    12341101
    12351102                // in reverse order :
     
    12411108                                LinkageSpec::Cforall,
    12421109                                nullptr,
    1243                                 lock_guard_struct,
     1110                                new StructInstType(
     1111                                        noQualifiers,
     1112                                        guard_decl
     1113                                ),
    12441114                                new ListInit(
    12451115                                        {
  • src/ControlStruct/ExceptTranslate.cc

    rb7fd9daf rf95634e  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // ExceptTranslate.cc -- Conversion of exception control flow structures.
     7// ExceptVisitor.cc --
    88//
    99// Author           : Andrew Beach
  • src/ControlStruct/ExceptTranslate.h

    rb7fd9daf rf95634e  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // ExceptTranslate.h -- Conversion of exception control flow structures.
     7// ExceptTranslate.h --
    88//
    99// Author           : Andrew Beach
    1010// Created On       : Tus Jun 06 10:13:00 2017
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Nov  8 11:43:00 2020
    13 // Update Count     : 6
     12// Last Modified On : Tus May 19 11:47:00 2020
     13// Update Count     : 5
    1414//
    1515
     
    1919
    2020class Declaration;
    21 namespace ast {
    22         class TranslationUnit;
    23 }
    2421
    2522namespace ControlStruct {
    2623        void translateThrows( std::list< Declaration *> & translationUnit );
    27         void translateThrows( ast::TranslationUnit & transUnit );
    2824        /* Replaces all throw & throwResume statements with function calls.
    2925         * These still need to be resolved, so call this before the reslover.
  • src/ControlStruct/LabelGenerator.cc

    rb7fd9daf rf95634e  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Nov  8 10:18:00 2021
    13 // Update Count     : 17
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Mon Mar 11 22:23:20 2019
     13// Update Count     : 15
    1414//
    1515
     
    1919
    2020#include "LabelGenerator.h"
    21 
    22 #include "AST/Attribute.hpp"
    23 #include "AST/Label.hpp"
    24 #include "AST/Stmt.hpp"
    2521#include "SynTree/Attribute.h"  // for Attribute
    2622#include "SynTree/Label.h"      // for Label, operator<<
     
    2824
    2925namespace ControlStruct {
    30 
    31 int LabelGenerator::current = 0;
    32 LabelGenerator * LabelGenerator::labelGenerator = nullptr;
     26        LabelGenerator * LabelGenerator::labelGenerator = 0;
    3327
    3428        LabelGenerator * LabelGenerator::getGenerator() {
     
    4943                return l;
    5044        }
    51 
    52 ast::Label LabelGenerator::newLabel(
    53                 const std::string & suffix, const ast::Stmt * stmt ) {
    54         assert( stmt );
    55 
    56         std::ostringstream os;
    57         os << "__L" << current++ << "__" << suffix;
    58         if ( stmt && !stmt->labels.empty() ) {
    59                 os << "_" << stmt->labels.front() << "__";
    60         }
    61         ast::Label ret_label( stmt->location, os.str() );
    62         ret_label.attributes.push_back( new ast::Attribute( "unused" ) );
    63         return ret_label;
    64 }
    65 
    6645} // namespace ControlStruct
    6746
  • src/ControlStruct/LabelGenerator.h

    rb7fd9daf rf95634e  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Nov  8 10:16:00 2021
    13 // Update Count     : 8
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Sat Jul 22 09:20:14 2017
     13// Update Count     : 6
    1414//
    1515
     
    2121
    2222class Statement;
    23 namespace ast {
    24         class Stmt;
    25         class Label;
    26 }
    2723
    2824namespace ControlStruct {
    29 
    30 class LabelGenerator {
    31         static int current;
    32         static LabelGenerator *labelGenerator;
    33 protected:
    34         LabelGenerator() {}
    35 public:
    36         static LabelGenerator *getGenerator();
    37         static Label newLabel(std::string suffix, Statement * stmt = nullptr);
    38         static ast::Label newLabel( const std::string&, const ast::Stmt * );
    39         static void reset() { current = 0; }
    40         static void rewind() { current--; }
    41 };
    42 
     25        class LabelGenerator {
     26          public:
     27                static LabelGenerator *getGenerator();
     28                Label newLabel(std::string suffix, Statement * stmt = nullptr);
     29                void reset() { current = 0; }
     30                void rewind() { current--; }
     31          protected:
     32                LabelGenerator(): current(0) {}
     33          private:
     34                int current;
     35                static LabelGenerator *labelGenerator;
     36        };
    4337} // namespace ControlStruct
    4438
  • src/ControlStruct/module.mk

    rb7fd9daf rf95634e  
    1818        ControlStruct/ExceptDecl.cc \
    1919        ControlStruct/ExceptDecl.h \
    20         ControlStruct/FixLabels.cpp \
    21         ControlStruct/FixLabels.hpp \
    2220        ControlStruct/ForExprMutator.cc \
    2321        ControlStruct/ForExprMutator.h \
     
    2826        ControlStruct/MLEMutator.cc \
    2927        ControlStruct/MLEMutator.h \
    30         ControlStruct/MultiLevelExit.cpp \
    31         ControlStruct/MultiLevelExit.hpp \
    3228        ControlStruct/Mutate.cc \
    3329        ControlStruct/Mutate.h
    3430
    35 SRC += $(SRC_CONTROLSTRUCT) \
    36         ControlStruct/ExceptTranslateNew.cpp \
    37         ControlStruct/ExceptTranslate.cc \
    38         ControlStruct/ExceptTranslate.h
    39 
     31SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h
    4032SRCDEMANGLE += $(SRC_CONTROLSTRUCT)
    4133
  • src/InitTweak/GenInit.cc

    rb7fd9daf rf95634e  
    99// Author           : Rob Schluntz
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Mon Oct 25 13:53:00 2021
    13 // Update Count     : 186
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Fri Dec 13 23:15:10 2019
     13// Update Count     : 184
    1414//
    1515#include "GenInit.h"
     
    2424#include "AST/Decl.hpp"
    2525#include "AST/Init.hpp"
    26 #include "AST/Pass.hpp"
    2726#include "AST/Node.hpp"
    2827#include "AST/Stmt.hpp"
     
    295294        }
    296295
    297 namespace {
    298 
    299 #       warning Remove the _New suffix after the conversion is complete.
    300         struct HoistArrayDimension_NoResolve_New final :
    301                         public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting,
    302                         public ast::WithGuards, public ast::WithConstTranslationUnit,
    303                         public ast::WithVisitorRef<HoistArrayDimension_NoResolve_New> {
    304                 void previsit( const ast::ObjectDecl * decl );
    305                 const ast::DeclWithType * postvisit( const ast::ObjectDecl * decl );
    306                 // Do not look for objects inside there declarations (and type).
    307                 void previsit( const ast::AggregateDecl * ) { visit_children = false; }
    308                 void previsit( const ast::NamedTypeDecl * ) { visit_children = false; }
    309                 void previsit( const ast::FunctionType * ) { visit_children = false; }
    310 
    311                 const ast::Type * hoist( const ast::Type * type );
    312 
    313                 ast::Storage::Classes storageClasses;
    314         };
    315 
    316         void HoistArrayDimension_NoResolve_New::previsit(
    317                         const ast::ObjectDecl * decl ) {
    318                 GuardValue( storageClasses ) = decl->storage;
    319         }
    320 
    321         const ast::DeclWithType * HoistArrayDimension_NoResolve_New::postvisit(
    322                         const ast::ObjectDecl * objectDecl ) {
    323                 return mutate_field( objectDecl, &ast::ObjectDecl::type,
    324                                 hoist( objectDecl->type ) );
    325         }
    326 
    327         const ast::Type * HoistArrayDimension_NoResolve_New::hoist(
    328                         const ast::Type * type ) {
    329                 static UniqueName dimensionName( "_array_dim" );
    330 
    331                 if ( !isInFunction() || storageClasses.is_static ) {
    332                         return type;
    333                 }
    334 
    335                 if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
    336                         if ( nullptr == arrayType->dimension ) {
    337                                 return type;
    338                         }
    339 
    340                         if ( !Tuples::maybeImpure( arrayType->dimension ) ) {
    341                                 return type;
    342                         }
    343 
    344                         ast::ptr<ast::Type> dimType = transUnit().global.sizeType;
    345                         assert( dimType );
    346                         add_qualifiers( dimType, ast::CV::Qualifiers( ast::CV::Const ) );
    347 
    348                         ast::ObjectDecl * arrayDimension = new ast::ObjectDecl(
    349                                 arrayType->dimension->location,
    350                                 dimensionName.newName(),
    351                                 dimType,
    352                                 new ast::SingleInit(
    353                                         arrayType->dimension->location,
    354                                         arrayType->dimension
    355                                 )
    356                         );
    357 
    358                         ast::ArrayType * mutType = ast::mutate( arrayType );
    359                         mutType->dimension = new ast::VariableExpr(
    360                                         arrayDimension->location, arrayDimension );
    361                         declsToAddBefore.push_back( arrayDimension );
    362 
    363                         mutType->base = hoist( mutType->base );
    364                         return mutType;
    365                 }
    366                 return type;
    367         }
    368 
    369         struct ReturnFixer_New final :
    370                         public ast::WithStmtsToAdd<>, ast::WithGuards {
    371                 void previsit( const ast::FunctionDecl * decl );
    372                 const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt );
    373         private:
    374                 const ast::FunctionDecl * funcDecl = nullptr;
    375         };
    376 
    377         void ReturnFixer_New::previsit( const ast::FunctionDecl * decl ) {
    378                 GuardValue( funcDecl ) = decl;
    379         }
    380 
    381         const ast::ReturnStmt * ReturnFixer_New::previsit(
    382                         const ast::ReturnStmt * stmt ) {
    383                 auto & returns = funcDecl->returns;
    384                 assert( returns.size() < 2 );
    385                 // Hands off if the function returns a reference.
    386                 // Don't allocate a temporary if the address is returned.
    387                 if ( stmt->expr && 1 == returns.size() ) {
    388                         ast::ptr<ast::DeclWithType> retDecl = returns.front();
    389                         if ( isConstructable( retDecl->get_type() ) ) {
    390                                 // Explicitly construct the return value using the return
    391                                 // expression and the retVal object.
    392                                 assertf( "" != retDecl->name,
    393                                         "Function %s has unnamed return value.\n",
    394                                         funcDecl->name.c_str() );
    395 
    396                                 auto retVal = retDecl.strict_as<ast::ObjectDecl>();
    397                                 if ( auto varExpr = stmt->expr.as<ast::VariableExpr>() ) {
    398                                         // Check if the return statement is already set up.
    399                                         if ( varExpr->var == retVal ) return stmt;
    400                                 }
    401                                 ast::ptr<ast::Stmt> ctorStmt = genCtorDtor(
    402                                         retVal->location, "?{}", retVal, stmt->expr );
    403                                 assertf( ctorStmt,
    404                                         "ReturnFixer: genCtorDtor returned nllptr: %s / %s",
    405                                         toString( retVal ).c_str(),
    406                                         toString( stmt->expr ).c_str() );
    407                                         stmtsToAddBefore.push_back( ctorStmt );
    408 
    409                                 // Return the retVal object.
    410                                 ast::ReturnStmt * mutStmt = ast::mutate( stmt );
    411                                 mutStmt->expr = new ast::VariableExpr(
    412                                         stmt->location, retDecl );
    413                                 return mutStmt;
    414                         }
    415                 }
    416                 return stmt;
    417         }
    418 
    419 } // namespace
    420 
    421         void genInit( ast::TranslationUnit & transUnit ) {
    422                 ast::Pass<HoistArrayDimension_NoResolve_New>::run( transUnit );
    423                 ast::Pass<ReturnFixer_New>::run( transUnit );
    424         }
    425 
    426296        void CtorDtor::generateCtorDtor( std::list< Declaration * > & translationUnit ) {
    427297                PassVisitor<CtorDtor> ctordtor;
  • src/InitTweak/GenInit.h

    rb7fd9daf rf95634e  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Fri Oct 22 16:08:00 2021
    13 // Update Count     : 6
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Sat Jul 22 09:31:19 2017
     13// Update Count     : 4
    1414//
    1515
     
    2727        /// Adds return value temporaries and wraps Initializers in ConstructorInit nodes
    2828        void genInit( std::list< Declaration * > & translationUnit );
    29         void genInit( ast::TranslationUnit & translationUnit );
    3029
    3130        /// Converts return statements into copy constructor calls on the hidden return variable
  • src/MakeLibCfa.h

    rb7fd9daf rf95634e  
    1919
    2020class Declaration;
    21 namespace ast {
    22         struct TranslationUnit;
    23 }
    2421
    2522namespace LibCfa {
    2623        void makeLibCfa( std::list< Declaration* > &prelude );
    27         void makeLibCfa( ast::TranslationUnit & translationUnit );
    2824} // namespace LibCfa
    2925
  • src/Makefile.am

    rb7fd9daf rf95634e  
    2323      CompilationState.h \
    2424      MakeLibCfa.cc \
    25           MakeLibCfaNew.cpp \
    2625        MakeLibCfa.h
    2726
  • src/Parser/parser.yy

    rb7fd9daf rf95634e  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Oct 15 09:20:17 2021
    13 // Update Count     : 5163
     12// Last Modified On : Tue Jul 20 22:03:04 2021
     13// Update Count     : 5031
    1414//
    1515
     
    3131// from ANSI90 to ANSI11 C are marked with the comment "C99/C11".
    3232
    33 // This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions. All of the
     33// This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions All of the
    3434// syntactic extensions for GCC C are marked with the comment "GCC". The second extensions are for Cforall (CFA), which
    3535// fixes several of C's outstanding problems and extends C with many modern language concepts. All of the syntactic
     
    6969        // 2. String encodings are transformed into canonical form (one encoding at start) so the encoding can be found
    7070        //    without searching the string, e.g.: "abc" L"def" L"ghi" => L"abc" "def" "ghi". Multiple encodings must match,
    71         //    e.g., u"a" U"b" L"c" is disallowed.
     71        //    i.e., u"a" U"b" L"c" is disallowed.
    7272
    7373        if ( from[0] != '"' ) {                                                         // encoding ?
     
    185185                type = new ExpressionNode( new CastExpr( maybeMoveBuild<Expression>(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) );
    186186        } // if
    187 //      type = new ExpressionNode( build_func( new ExpressionNode( build_varref( new string( "__for_control_index_constraints__" ) ) ), type ) );
    188187        return new ForCtrl(
    189188                distAttr( DeclarationNode::newTypeof( type, true ), DeclarationNode::newName( index )->addInitializer( new InitializerNode( start ) ) ),
     
    310309%token ATassign                                                                                 // @=
    311310
    312 %type<tok> identifier                                   identifier_at                           identifier_or_type_name         attr_name
     311%type<tok> identifier
     312%type<tok> identifier_or_type_name  attr_name
    313313%type<tok> quasi_keyword
    314314%type<constant> string_literal
     
    326326%type<en> conditional_expression                constant_expression                     assignment_expression           assignment_expression_opt
    327327%type<en> comma_expression                              comma_expression_opt
    328 %type<en> argument_expression_list_opt  argument_expression_list        argument_expression                     default_initializer_opt
     328%type<en> argument_expression_list_opt  argument_expression                     default_initializer_opt
    329329%type<ifctl> if_control_expression
    330330%type<fctl> for_control_expression              for_control_expression_list
     
    558558        IDENTIFIER
    559559        | quasi_keyword
    560         ;
    561 
    562 identifier_at:
    563         identifier
    564560        | '@'                                                                                           // CFA
    565561                { Token tok = { new string( DeclarationNode::anonymous.newName() ), yylval.tok.loc }; $$ = tok; }
     
    696692        // empty
    697693                { $$ = nullptr; }
    698         | argument_expression_list
    699         ;
    700 
    701 argument_expression_list:
    702         argument_expression
     694        | argument_expression
    703695        | argument_expression_list_opt ',' argument_expression
    704696                { $$ = (ExpressionNode *)($1->set_last( $3 )); }
     
    738730        | FLOATINGconstant fraction_constants_opt
    739731                { $$ = new ExpressionNode( build_field_name_fraction_constants( build_field_name_FLOATINGconstant( *$1 ), $2 ) ); }
    740         | identifier_at fraction_constants_opt                          // CFA, allow anonymous fields
     732        | identifier fraction_constants_opt
    741733                {
    742734                        $$ = new ExpressionNode( build_field_name_fraction_constants( build_varref( $1 ), $2 ) );
     
    10911083        comma_expression_opt ';'
    10921084                { $$ = new StatementNode( build_expr( $1 ) ); }
    1093         | MUTEX '(' ')' comma_expression ';'
    1094                 { $$ = new StatementNode( build_mutex( nullptr, new StatementNode( build_expr( $4 ) ) ) ); }
    1095                 // { SemanticError( yylloc, "Mutex expression is currently unimplemented." ); $$ = nullptr; }
    10961085        ;
    10971086
     
    11921181
    11931182iteration_statement:
    1194         WHILE '(' ')' statement                                                         // CFA => while ( 1 )
     1183        WHILE '(' push if_control_expression ')' statement pop
     1184                { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); }
     1185        | WHILE '(' ')' statement                                                       // CFA => while ( 1 )
    11951186                { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
    1196         | WHILE '(' if_control_expression ')' statement         %prec THEN
    1197                 { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ) ) ); }
    1198         | WHILE '(' if_control_expression ')' statement ELSE statement // CFA
    1199                 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
     1187        | DO statement WHILE '(' comma_expression ')' ';'
     1188                { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
    12001189        | DO statement WHILE '(' ')' ';'                                        // CFA => do while( 1 )
    12011190                { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
    1202         | DO statement WHILE '(' comma_expression ')' ';'       %prec THEN
    1203                 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
    1204         | DO statement WHILE '(' comma_expression ')' ELSE statement // CFA
    1205                 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
     1191        | FOR '(' push for_control_expression_list ')' statement pop
     1192                { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); }
    12061193        | FOR '(' ')' statement                                                         // CFA => for ( ;; )
    12071194                { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); }
    1208         | FOR '(' for_control_expression_list ')' statement     %prec THEN
    1209                 { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); }
    1210         | FOR '(' for_control_expression_list ')' statement ELSE statement // CFA
    1211                 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
    12121195        ;
    12131196
     
    13551338with_statement:
    13561339        WITH '(' tuple_expression_list ')' statement
    1357                 { $$ = new StatementNode( build_with( $3, $5 ) ); }
     1340                {
     1341                        $$ = new StatementNode( build_with( $3, $5 ) );
     1342                }
    13581343        ;
    13591344
    13601345// If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex".
    13611346mutex_statement:
    1362         MUTEX '(' argument_expression_list ')' statement
     1347        MUTEX '(' argument_expression_list_opt ')' statement
    13631348                { $$ = new StatementNode( build_mutex( $3, $5 ) ); }
    13641349        ;
     
    24602445        | simple_assignment_operator initializer        { $$ = $1 == OperKinds::Assign ? $2 : $2->set_maybeConstructed( false ); }
    24612446        | '=' VOID                                                                      { $$ = new InitializerNode( true ); }
    2462         | '{' initializer_list_opt comma_opt '}'        { $$ = new InitializerNode( $2, true ); }
    24632447        ;
    24642448
     
    24742458        | designation initializer                                       { $$ = $2->set_designators( $1 ); }
    24752459        | initializer_list_opt ',' initializer          { $$ = (InitializerNode *)( $1->set_last( $3 ) ); }
    2476         | initializer_list_opt ',' designation initializer { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
     2460        | initializer_list_opt ',' designation initializer
     2461                { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
    24772462        ;
    24782463
     
    24892474designation:
    24902475        designator_list ':'                                                                     // C99, CFA uses ":" instead of "="
    2491         | identifier_at ':'                                                                     // GCC, field name
     2476        | identifier ':'                                                                        // GCC, field name
    24922477                { $$ = new ExpressionNode( build_varref( $1 ) ); }
    24932478        ;
     
    25012486
    25022487designator:
    2503         '.' identifier_at                                                                       // C99, field name
     2488        '.' identifier                                                                          // C99, field name
    25042489                { $$ = new ExpressionNode( build_varref( $2 ) ); }
    25052490        | '[' push assignment_expression pop ']'                        // C99, single array element
     
    29332918
    29342919paren_identifier:
    2935         identifier_at
     2920        identifier
    29362921                { $$ = DeclarationNode::newName( $1 ); }
    29372922        | '(' paren_identifier ')'                                                      // redundant parenthesis
  • src/ResolvExpr/module.mk

    rb7fd9daf rf95634e  
    6161      ResolvExpr/WidenMode.h
    6262
    63 SRC += $(SRC_RESOLVEXPR) \
    64         ResolvExpr/AlternativePrinter.cc \
    65         ResolvExpr/AlternativePrinter.h \
    66         ResolvExpr/CandidatePrinter.cpp \
    67         ResolvExpr/CandidatePrinter.hpp
    6863
     64SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h
    6965SRCDEMANGLE += $(SRC_RESOLVEXPR)
  • src/Tuples/TupleExpansionNew.cpp

    rb7fd9daf rf95634e  
    88//
    99// Author           : Henry Xue
    10 // Created On       : Mon Aug 23 15:36:09 2021
     10// Created On       : Wed Aug 18 12:54:02 2021
    1111// Last Modified By : Henry Xue
    12 // Last Modified On : Mon Aug 23 15:36:09 2021
     12// Last Modified On : Wed Aug 18 12:54:02 2021
    1313// Update Count     : 1
    1414//
     15
     16// Currently not working due to unresolved issues with UniqueExpr
    1517
    1618#include "Tuples.h"
     
    1820namespace Tuples {
    1921namespace {
    20         struct MemberTupleExpander final : public ast::WithShortCircuiting, public ast::WithVisitorRef< MemberTupleExpander > {
    21                 void previsit( const ast::UntypedMemberExpr * ) { visit_children = false; }
    22         const ast::Expr * postvisit( const ast::UntypedMemberExpr * memberExpr );
    23         };
    2422        struct UniqueExprExpander final : public ast::WithDeclsToAdd<> {
    2523                const ast::Expr * postvisit( const ast::UniqueExpr * unqExpr );
    2624                std::map< int, ast::ptr<ast::Expr> > decls; // not vector, because order added may not be increasing order
    2725        };
    28 } // namespace
    29 
    30 void expandMemberTuples( ast::TranslationUnit & translationUnit ) {
    31         ast::Pass< MemberTupleExpander >::run( translationUnit );
    32 }
    33 
    34 namespace {
    35         namespace {
    36                 /// given a expression representing the member and an expression representing the aggregate,
    37                 /// reconstructs a flattened UntypedMemberExpr with the right precedence
    38                 const ast::Expr * reconstructMemberExpr( const ast::Expr * member, const ast::Expr * aggr, const CodeLocation & loc ) {
    39                         if ( auto memberExpr = dynamic_cast< const ast::UntypedMemberExpr * >( member ) ) {
    40                                 // construct a new UntypedMemberExpr with the correct structure , and recursively
    41                                 // expand that member expression.
    42                                 ast::Pass< MemberTupleExpander > expander;
    43                                 auto inner = new ast::UntypedMemberExpr( loc, memberExpr->aggregate, aggr );
    44                                 auto newMemberExpr = new ast::UntypedMemberExpr( loc, memberExpr->member, inner );
    45                                 //delete memberExpr;
    46                                 return newMemberExpr->accept( expander );
    47                         } else {
    48                                 // not a member expression, so there is nothing to do but attach and return
    49                                 return new ast::UntypedMemberExpr( loc, member, aggr );
    50                         }
    51                 }
    52         }
    53 
    54         const ast::Expr * MemberTupleExpander::postvisit( const ast::UntypedMemberExpr * memberExpr ) {
    55                 const CodeLocation loc = memberExpr->location;
    56         if ( auto tupleExpr = memberExpr->member.as< ast::UntypedTupleExpr >() ) {
    57                         auto mutExpr = mutate( tupleExpr );
    58                         ast::ptr< ast::Expr > aggr = memberExpr->aggregate->accept( *visitor );
    59                         // aggregate expressions which might be impure must be wrapped in unique expressions
    60                         if ( Tuples::maybeImpureIgnoreUnique( memberExpr->aggregate ) ) aggr = new ast::UniqueExpr( loc, aggr );
    61                         for ( auto & expr : mutExpr->exprs ) {
    62                                 expr = reconstructMemberExpr( expr, aggr, loc );
    63                         }
    64                         //delete aggr;
    65                         return mutExpr;
    66                 } else {
    67                         // there may be a tuple expr buried in the aggregate
    68                         return new ast::UntypedMemberExpr( loc, memberExpr->member, memberExpr->aggregate->accept( *visitor ) );
    69                 }
    70         }
    7126} // namespace
    7227
  • src/Tuples/Tuples.h

    rb7fd9daf rf95634e  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Henry Xue
    12 // Last Modified On : Mon Aug 23 15:36:09 2021
    13 // Update Count     : 19
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Jun 18 09:36:00 2019
     13// Update Count     : 18
    1414//
    1515
     
    3939        /// expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate
    4040        void expandMemberTuples( std::list< Declaration * > & translationUnit );
    41         void expandMemberTuples( ast::TranslationUnit & translationUnit );
    4241
    4342        /// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
  • src/Tuples/module.mk

    rb7fd9daf rf95634e  
    1010## Author           : Richard C. Bilson
    1111## Created On       : Mon Jun  1 17:49:17 2015
    12 ## Last Modified By : Henry Xue
    13 ## Last Modified On : Mon Aug 23 15:36:09 2021
    14 ## Update Count     : 2
     12## Last Modified By : Peter A. Buhr
     13## Last Modified On : Mon Jun  1 17:54:33 2015
     14## Update Count     : 1
    1515###############################################################################
    1616
  • src/main.cc

    rb7fd9daf rf95634e  
    99// Author           : Peter Buhr and Rob Schluntz
    1010// Created On       : Fri May 15 23:12:02 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Tue Nov  9 11:10:00 2021
    13 // Update Count     : 657
     11// Last Modified By : Henry Xue
     12// Last Modified On : Tue Jul 20 04:27:35 2021
     13// Update Count     : 658
    1414//
    1515
     
    4343#include "Common/CodeLocationTools.hpp"     // for forceFillCodeLocations
    4444#include "Common/CompilerError.h"           // for CompilerError
    45 #include "Common/DeclStats.hpp"             // for printDeclStats
    46 #include "Common/ResolvProtoDump.hpp"       // for dumpAsResolverProto
    4745#include "Common/Stats.h"
    4846#include "Common/PassVisitor.h"
     
    5351#include "ControlStruct/ExceptDecl.h"       // for translateExcept
    5452#include "ControlStruct/ExceptTranslate.h"  // for translateEHM
    55 #include "ControlStruct/FixLabels.hpp"      // for fixLabels
    5653#include "ControlStruct/Mutate.h"           // for mutate
    5754#include "GenPoly/Box.h"                    // for box
     
    6562#include "Parser/TypedefTable.h"            // for TypedefTable
    6663#include "ResolvExpr/AlternativePrinter.h"  // for AlternativePrinter
    67 #include "ResolvExpr/CandidatePrinter.hpp"  // for printCandidates
    6864#include "ResolvExpr/Resolver.h"            // for resolve
    6965#include "SymTab/Validate.h"                // for validate
     
    319315                // add the assignment statement after the initialization of a type parameter
    320316                PASS( "Validate", SymTab::validate( translationUnit, symtabp ) );
     317                if ( symtabp ) {
     318                        deleteAll( translationUnit );
     319                        return EXIT_SUCCESS;
     320                } // if
     321
     322                if ( expraltp ) {
     323                        PassVisitor<ResolvExpr::AlternativePrinter> printer( cout );
     324                        acceptAll( translationUnit, printer );
     325                        return EXIT_SUCCESS;
     326                } // if
     327
     328                if ( validp ) {
     329                        dump( translationUnit );
     330                        return EXIT_SUCCESS;
     331                } // if
     332
     333                PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );
     334                PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );
     335                PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );
     336                PASS( "Gen Init", InitTweak::genInit( translationUnit ) );
     337                PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) );
     338                if ( libcfap ) {
     339                        // generate the bodies of cfa library functions
     340                        LibCfa::makeLibCfa( translationUnit );
     341                } // if
     342
     343                if ( declstatsp ) {
     344                        CodeTools::printDeclStats( translationUnit );
     345                        deleteAll( translationUnit );
     346                        return EXIT_SUCCESS;
     347                } // if
     348
     349                if ( bresolvep ) {
     350                        dump( translationUnit );
     351                        return EXIT_SUCCESS;
     352                } // if
    321353
    322354                CodeTools::fillLocations( translationUnit );
     355
     356                if ( resolvprotop ) {
     357                        CodeTools::dumpAsResolvProto( translationUnit );
     358                        return EXIT_SUCCESS;
     359                } // if
    323360
    324361                if( useNewAST ) {
     
    328365                        }
    329366                        auto transUnit = convert( move( translationUnit ) );
    330 
    331                         forceFillCodeLocations( transUnit );
    332 
    333                         if ( symtabp ) {
    334                                 return EXIT_SUCCESS;
    335                         } // if
    336 
    337                         if ( expraltp ) {
    338                                 ResolvExpr::printCandidates( transUnit );
    339                                 return EXIT_SUCCESS;
    340                         } // if
    341 
    342                         if ( validp ) {
    343                                 dump( move( transUnit ) );
    344                                 return EXIT_SUCCESS;
    345                         } // if
    346 
    347                         PASS( "Translate Throws", ControlStruct::translateThrows( transUnit ) );
    348                         PASS( "Fix Labels", ControlStruct::fixLabels( transUnit ) );
    349                         PASS( "Fix Names", CodeGen::fixNames( transUnit ) );
    350                         PASS( "Gen Init", InitTweak::genInit( transUnit ) );
    351                         PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( transUnit ) );
    352 
    353                         if ( libcfap ) {
    354                                 // Generate the bodies of cfa library functions.
    355                                 LibCfa::makeLibCfa( transUnit );
    356                         } // if
    357 
    358                         if ( declstatsp ) {
    359                                 printDeclStats( transUnit );
    360                                 return EXIT_SUCCESS;
    361                         } // if
    362 
    363                         if ( bresolvep ) {
    364                                 dump( move( transUnit ) );
    365                                 return EXIT_SUCCESS;
    366                         } // if
    367 
    368                         if ( resolvprotop ) {
    369                                 dumpAsResolverProto( transUnit );
    370                                 return EXIT_SUCCESS;
    371                         } // if
    372 
    373367                        PASS( "Resolve", ResolvExpr::resolve( transUnit ) );
    374368                        if ( exprp ) {
     
    391385                        translationUnit = convert( move( transUnit ) );
    392386                } else {
    393                         if ( symtabp ) {
    394                                 deleteAll( translationUnit );
    395                                 return EXIT_SUCCESS;
    396                         } // if
    397 
    398                         if ( expraltp ) {
    399                                 PassVisitor<ResolvExpr::AlternativePrinter> printer( cout );
    400                                 acceptAll( translationUnit, printer );
    401                                 return EXIT_SUCCESS;
    402                         } // if
    403 
    404                         if ( validp ) {
    405                                 dump( translationUnit );
    406                                 return EXIT_SUCCESS;
    407                         } // if
    408 
    409                         PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );
    410                         PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );
    411                         PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );
    412                         PASS( "Gen Init", InitTweak::genInit( translationUnit ) );
    413                         PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) );
    414 
    415                         if ( libcfap ) {
    416                                 // Generate the bodies of cfa library functions.
    417                                 LibCfa::makeLibCfa( translationUnit );
    418                         } // if
    419 
    420                         if ( declstatsp ) {
    421                                 CodeTools::printDeclStats( translationUnit );
    422                                 deleteAll( translationUnit );
    423                                 return EXIT_SUCCESS;
    424                         } // if
    425 
    426                         if ( bresolvep ) {
    427                                 dump( translationUnit );
    428                                 return EXIT_SUCCESS;
    429                         } // if
    430 
    431                         CodeTools::fillLocations( translationUnit );
    432 
    433                         if ( resolvprotop ) {
    434                                 CodeTools::dumpAsResolvProto( translationUnit );
    435                                 return EXIT_SUCCESS;
    436                         } // if
    437 
    438387                        PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
    439388                        if ( exprp ) {
     
    498447                PASS( "Code Gen", CodeGen::generate( translationUnit, *output, ! genproto, prettycodegenp, true, linemarks ) );
    499448
    500                 CodeGen::FixMain::fix( translationUnit, *output,
    501                                 (PreludeDirector + "/bootloader.c").c_str() );
     449                CodeGen::FixMain::fix( *output, (PreludeDirector + "/bootloader.c").c_str() );
    502450                if ( output != &cout ) {
    503451                        delete output;
  • tests/.expect/declarationSpecifier.x64.txt

    rb7fd9daf rf95634e  
    11321132char **_X13cfa_args_argvPPc_1;
    11331133char **_X13cfa_args_envpPPc_1;
    1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
     1134signed int _X17cfa_main_returnedi_1 = ((signed int )0);
    11351135signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    11361136    __attribute__ ((unused)) signed int _X12_retval_maini_1;
     
    11491149    signed int _tmp_cp_ret6;
    11501150    signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6);
    1151     if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
    1152         {
    1153             ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    1154         }
    1155 
     1151    {
     1152        ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    11561153    }
    11571154
  • tests/.expect/declarationSpecifier.x86.txt

    rb7fd9daf rf95634e  
    11321132char **_X13cfa_args_argvPPc_1;
    11331133char **_X13cfa_args_envpPPc_1;
    1134 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
     1134signed int _X17cfa_main_returnedi_1 = ((signed int )0);
    11351135signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    11361136    __attribute__ ((unused)) signed int _X12_retval_maini_1;
     
    11491149    signed int _tmp_cp_ret6;
    11501150    signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6);
    1151     if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
    1152         {
    1153             ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    1154         }
    1155 
     1151    {
     1152        ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    11561153    }
    11571154
  • tests/.expect/gccExtensions.x64.txt

    rb7fd9daf rf95634e  
    324324char **_X13cfa_args_argvPPc_1;
    325325char **_X13cfa_args_envpPPc_1;
    326 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
     326signed int _X17cfa_main_returnedi_1 = ((signed int )0);
    327327signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    328328    __attribute__ ((unused)) signed int _X12_retval_maini_1;
     
    341341    signed int _tmp_cp_ret6;
    342342    signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6);
    343     if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
    344         {
    345             ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    346         }
    347 
     343    {
     344        ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    348345    }
    349346
  • tests/.expect/gccExtensions.x86.txt

    rb7fd9daf rf95634e  
    302302char **_X13cfa_args_argvPPc_1;
    303303char **_X13cfa_args_envpPPc_1;
    304 __attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
     304signed int _X17cfa_main_returnedi_1 = ((signed int )0);
    305305signed int main(signed int _X4argci_1, char **_X4argvPPc_1, char **_X4envpPPc_1){
    306306    __attribute__ ((unused)) signed int _X12_retval_maini_1;
     
    319319    signed int _tmp_cp_ret6;
    320320    signed int _X3reti_2 = (((void)(_tmp_cp_ret6=invoke_main(_X4argci_1, _X4argvPPc_1, _X4envpPPc_1))) , _tmp_cp_ret6);
    321     if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
    322         {
    323             ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    324         }
    325 
     321    {
     322        ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
    326323    }
    327324
  • tests/Makefile.am

    rb7fd9daf rf95634e  
    7575        pybin/tools.py \
    7676        long_tests.hfa \
    77         .in/parseconfig-all.txt \
    78         .in/parseconfig-errors.txt \
    79         .in/parseconfig-missing.txt \
    8077        io/.in/io.data \
    8178        io/.in/many_read.data \
  • tests/concurrent/mutexstmt/locks.cfa

    rb7fd9daf rf95634e  
    1 #include <mutex_stmt.hfa>
     1#include <mutex_stmt_locks.hfa>
    22#include <locks.hfa>
    33
    44const unsigned int num_times = 10000;
    55
    6 single_acquisition_lock m1, m2, m3, m4, m5;
     6owner_lock m1, m2, m3, m4, m5;
    77
    88thread T_Mutex {};
     
    1313        for (unsigned int i = 0; i < num_times; i++) {
    1414                mutex ( m1 ) count++;
    15                 mutex ( m1 ) { 
     15                mutex ( m1 ) {
    1616                        assert(!insideFlag);
    1717                        insideFlag = true;
  • tests/concurrent/mutexstmt/monitors.cfa

    rb7fd9daf rf95634e  
    11#include <monitor.hfa>
    2 #include <mutex_stmt.hfa>
    32#include <stdio.h>
    43#include <stdlib.hfa>
     
    1413bool insideFlag = false;
    1514int count = 0;
    16 bool startFlag = false;
    1715
    1816void main( T_Mutex & this ) {
  • tests/concurrent/semaphore.cfa

    rb7fd9daf rf95634e  
    22#include <locks.hfa>
    33#include <thread.hfa>
    4 #include <mutex_stmt.hfa>
    54
    65enum { num_blockers = 17, num_unblockers = 13 };
     
    2928                thrash();
    3029                P(ben);
    31                 if(((thread&)this).seqable.next != 0p) mutex(sout) sout | "Link not invalidated";
     30                if(((thread&)this).seqable.next != 0p) sout | acquire |"Link not invalidated";
    3231                thrash();
    3332        }
  • tests/concurrent/sleep.cfa

    rb7fd9daf rf95634e  
    11#include <fstream.hfa>
    22#include <thread.hfa>
    3 #include <mutex_stmt.hfa>
    43#include <time.hfa>
    54
     
    3029
    3130int main() {
    32         mutex( sout ) sout | "start";
     31        sout | acquire | "start";
    3332        {
    3433                slow_sleeper slow;
     
    3736                yield();
    3837        }
    39         mutex( sout ) sout | "done";
     38        sout | acquire | "done";
    4039}
    4140
  • tests/exceptions/.expect/type-check.txt

    rb7fd9daf rf95634e  
    1 exceptions/type-check.cfa:6:1 error: catch must have pointer to an exception type
    2 exceptions/type-check.cfa:7:1 error: catch must have pointer to an exception type
    3 exceptions/type-check.cfa:8:1 error: catchResume must have pointer to an exception type
    4 exceptions/type-check.cfa:9:1 error: catchResume must have pointer to an exception type
     1exceptions/type-check.cfa:8:1 error: catch must have pointer to an exception type
     2exceptions/type-check.cfa:9:1 error: catch must have pointer to an exception type
     3exceptions/type-check.cfa:10:1 error: catchResume must have pointer to an exception type
     4exceptions/type-check.cfa:11:1 error: catchResume must have pointer to an exception type
  • tests/exceptions/cancel/coroutine.cfa

    rb7fd9daf rf95634e  
    22
    33#include <coroutine.hfa>
     4#include <exception.hfa>
    45
    5 exception internal_error {};
    6 vtable(internal_error) internal_vt;
     6EHM_EXCEPTION(internal_error)();
     7EHM_VIRTUAL_TABLE(internal_error, internal_vt);
    78
    89coroutine WillCancel {};
  • tests/exceptions/cancel/thread.cfa

    rb7fd9daf rf95634e  
    22
    33#include <thread.hfa>
     4#include <exception.hfa>
    45
    5 exception internal_error {};
    6 vtable(internal_error) internal_vt;
     6EHM_EXCEPTION(internal_error)();
     7EHM_VIRTUAL_TABLE(internal_error, internal_vt);
    78
    89thread WillCancel {};
  • tests/exceptions/conditional.cfa

    rb7fd9daf rf95634e  
    44// up the non-trivial exception is reasonable to do.
    55
    6 exception num_error {
     6#include <exception.hfa>
     7
     8EHM_EXCEPTION(num_error)(
    79        int num;
    8 };
     10);
    911
    10 vtable(num_error) num_error_vt;
     12EHM_VIRTUAL_TABLE(num_error, num_error_vt);
    1113
    1214void caught_num_error(int expect, num_error * actual) {
  • tests/exceptions/data-except.cfa

    rb7fd9daf rf95634e  
    11// Test exceptions that add data but no functionality.
    22
    3 exception paired {
     3#include <exception.hfa>
     4
     5EHM_EXCEPTION(paired)(
    46        int first;
    57        int second;
    6 };
     8);
    79
    8 vtable(paired) paired_vt;
     10EHM_VIRTUAL_TABLE(paired, paired_vt);
    911
    1012const char * virtual_msg(paired * this) {
  • tests/exceptions/defaults.cfa

    rb7fd9daf rf95634e  
    44#include <exception.hfa>
    55
    6 exception log_message {
     6EHM_EXCEPTION(log_message)(
    77        char * msg;
    8 };
     8);
    99
    1010_EHM_DEFINE_COPY(log_message, )
     
    3232
    3333// I don't have a good use case for doing the same with termination.
    34 exception jump {};
     34EHM_EXCEPTION(jump)();
    3535void defaultTerminationHandler(jump &) {
    3636        printf("jump default handler.\n");
    3737}
    3838
    39 vtable(jump) jump_vt;
     39EHM_VIRTUAL_TABLE(jump, jump_vt);
    4040
    4141void jump_test(void) {
     
    4848}
    4949
    50 exception first {};
    51 vtable(first) first_vt;
     50EHM_EXCEPTION(first)();
     51EHM_VIRTUAL_TABLE(first, first_vt);
    5252
    53 exception unhandled_exception {};
    54 vtable(unhandled_exception) unhandled_vt;
     53EHM_EXCEPTION(unhandled_exception)();
     54EHM_VIRTUAL_TABLE(unhandled_exception, unhandled_vt);
    5555
    5656void unhandled_test(void) {
     
    6969}
    7070
    71 exception second {};
    72 vtable(second) second_vt;
     71EHM_EXCEPTION(second)();
     72EHM_VIRTUAL_TABLE(second, second_vt);
    7373
    7474void cross_test(void) {
  • tests/exceptions/finally.cfa

    rb7fd9daf rf95634e  
    11// Finally Clause Tests
    22
     3#include <exception.hfa>
    34#include "except-io.hfa"
    45
    5 exception myth {};
     6EHM_EXCEPTION(myth)();
    67
    7 vtable(myth) myth_vt;
     8EHM_VIRTUAL_TABLE(myth, myth_vt);
    89
    910int main(int argc, char * argv[]) {
  • tests/exceptions/interact.cfa

    rb7fd9daf rf95634e  
    11// Testing Interactions Between Termination and Resumption
    22
     3#include <exception.hfa>
    34#include "except-io.hfa"
    45
    5 exception star {};
    6 exception moon {};
     6EHM_EXCEPTION(star)();
     7EHM_EXCEPTION(moon)();
    78
    8 vtable(star) star_vt;
    9 vtable(moon) moon_vt;
     9EHM_VIRTUAL_TABLE(star, star_vt);
     10EHM_VIRTUAL_TABLE(moon, moon_vt);
    1011
    1112int main(int argc, char * argv[]) {
  • tests/exceptions/polymorphic.cfa

    rb7fd9daf rf95634e  
    11// Testing polymophic exception types.
    22
    3 forall(T &) exception proxy {};
     3#include <exception.hfa>
    44
    5 vtable(proxy(int)) proxy_int;
    6 vtable(proxy(char)) proxy_char;
     5EHM_FORALL_EXCEPTION(proxy, (T&), (T))();
     6
     7EHM_FORALL_VIRTUAL_TABLE(proxy, (int), proxy_int);
     8EHM_FORALL_VIRTUAL_TABLE(proxy, (char), proxy_char);
    79
    810void proxy_test(void) {
     
    3133}
    3234
    33 forall(T) exception cell {
     35EHM_FORALL_EXCEPTION(cell, (T), (T))(
    3436        T data;
    35 };
     37);
    3638
    37 vtable(cell(int)) int_cell;
    38 vtable(cell(char)) char_cell;
    39 vtable(cell(bool)) bool_cell;
     39EHM_FORALL_VIRTUAL_TABLE(cell, (int), int_cell);
     40EHM_FORALL_VIRTUAL_TABLE(cell, (char), char_cell);
     41EHM_FORALL_VIRTUAL_TABLE(cell, (bool), bool_cell);
    4042
    4143void cell_test(void) {
  • tests/exceptions/resume.cfa

    rb7fd9daf rf95634e  
    11// Resumption Exception Tests
    22
     3#include <exception.hfa>
    34#include "except-io.hfa"
    45
    5 exception yin {};
    6 exception yang {};
    7 exception zen {};
     6EHM_EXCEPTION(yin)();
     7EHM_EXCEPTION(yang)();
     8EHM_EXCEPTION(zen)();
    89
    9 vtable(yin) yin_vt;
    10 vtable(yang) yang_vt;
    11 vtable(zen) zen_vt;
     10EHM_VIRTUAL_TABLE(yin, yin_vt);
     11EHM_VIRTUAL_TABLE(yang, yang_vt);
     12EHM_VIRTUAL_TABLE(zen, zen_vt);
    1213
    1314void in_void(void);
  • tests/exceptions/terminate.cfa

    rb7fd9daf rf95634e  
    11// Termination Exception Tests
    22
     3#include <exception.hfa>
    34#include "except-io.hfa"
    45
    5 exception yin {};
    6 exception yang {};
    7 exception zen {};
     6EHM_EXCEPTION(yin)();
     7EHM_EXCEPTION(yang)();
     8EHM_EXCEPTION(zen)();
    89
    9 vtable(yin) yin_vt;
    10 vtable(yang) yang_vt;
    11 vtable(zen) zen_vt;
     10EHM_VIRTUAL_TABLE(yin, yin_vt);
     11EHM_VIRTUAL_TABLE(yang, yang_vt);
     12EHM_VIRTUAL_TABLE(zen, zen_vt);
    1213
    1314void in_void(void);
  • tests/exceptions/trash.cfa

    rb7fd9daf rf95634e  
    11// Make sure throw-catch during unwind does not trash internal data.
    22
    3 exception yin {};
    4 exception yang {};
     3#include <exception.hfa>
    54
    6 vtable(yin) yin_vt;
    7 vtable(yang) yang_vt;
     5EHM_EXCEPTION(yin)();
     6EHM_EXCEPTION(yang)();
     7
     8EHM_VIRTUAL_TABLE(yin, yin_vt);
     9EHM_VIRTUAL_TABLE(yang, yang_vt);
    810
    911int main(int argc, char * argv[]) {
  • tests/exceptions/type-check.cfa

    rb7fd9daf rf95634e  
    11// Check that the exception type check works.
    22
    3 exception truth {};
     3#include <exception.hfa>
     4
     5EHM_EXCEPTION(truth)();
    46
    57int main(int argc, char * argv[]) {
  • tests/io/io-acquire.cfa

    rb7fd9daf rf95634e  
    1010// Created On       : Mon Mar  1 18:40:09 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Oct  6 18:04:58 2021
    13 // Update Count     : 72
     12// Last Modified On : Tue Apr 27 11:49:34 2021
     13// Update Count     : 18
    1414//
    1515
    1616#include <fstream.hfa>
    1717#include <thread.hfa>
    18 #include <mutex_stmt.hfa>
    1918
    2019thread T {};
     
    2221        // output from parallel threads should not be scrambled
    2322
    24         for ( 100 ) {                                                                           // expression protection
    25                 mutex(sout) sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
     23        for ( 100 ) {                                                                           // local protection
     24                sout | acquire | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
    2625        }
    27         mutex( sout ) {                                                                         // statement protection
     26        {                                                                                                       // global protection (RAII)
     27                osacquire acq = { sout };
    2828                for ( 100 ) {
    2929                        sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
     
    3131        }
    3232        {                                                                                                       // duplicate protection demonstrating recursive lock
    33                 ofstream & h1( ofstream & os ) {                                // helper
    34                         mutex( os ) return os | 1 | 2 | 3 | 4;          // unnecessary mutex
    35                 }
    36                 ofstream & h2( ofstream & os ) {                                // helper
    37                         mutex( os ) return os | 6 | 7 | 8 | 9;          // unnecessary mutex
    38                 }
    39                 mutex( sout ) {                                                                 // unnecessary mutex
    40                         for ( 100 ) {
    41                                 mutex( sout ) {
    42                                         sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
    43                                         sout | h1 | 5 | h2;                                     // refactored code
    44                                 }
    45                         }
     33                osacquire acq = { sout };
     34                for ( 100 ) {
     35                        osacquire acq = { sout };
     36                        sout | acquire | 1 | 2 | 3 | 4 | 5 | acquire | 6 | 7 | 8 | 9;
     37                        sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
    4638                }
    4739        }
     
    5042
    5143        int a, b, c, d, e, f, g, h, i;
    52         for ( 100 ) {                                                                           // expression protection
    53                 mutex(sin) sin | a | b | c | d | e | f | g | h | i;
     44        for ( 100 ) {                                                                           // local protection
     45                sin | acquire | a | b | c | d | e | f | g | h | i;
    5446        }
    55         mutex( sin ) {                                                                          // statement protection
     47        {                                                                                                       // global protection (RAII)
     48                isacquire acq = { sin };
    5649                for ( 100 ) {
    5750                        sin  | a | b | c | d | e | f | g | h | i;
     
    5952        }
    6053        {                                                                                                       // duplicate protection demonstrating recursive lock
    61                 ifstream & h1( ifstream & is ) {                                // helper
    62                         mutex( is ) return is | a | b | c | d;          // unnecessary mutex
    63                 }
    64                 ifstream & h2( ifstream & is ) {                                // helper
    65                         mutex( is ) return is | f | g | h | i;          // unnecessary mutex
    66                 }
    67                 mutex( sin ) {                                                                  // unnecessary mutex
    68                         for ( 5 ) {
    69                                 mutex( sin ) {
    70                                         sin  | a | b | c | d | e | f | g | h | i;
    71                                         sin  | h1 | e | h2;                                     // refactored code
    72                                 }
    73                         }
     54                isacquire acq = { sin };
     55                for ( 100 ) {
     56                        isacquire acq = { sin };
     57                        sin | acquire | a | b | c | d | e | acquire | f | g | h | i;
     58                        sin | a | b | c | d | e | f | g | h | i;
    7459                }
    7560        }
  • tests/linking/io-acquire.cfa

    rb7fd9daf rf95634e  
    1717#include <fstream.hfa>
    1818#include <stdlib.hfa>
    19 #include <mutex_stmt.hfa>
    2019
    2120int main() {
    2221        int i;
    2322        if(threading_enabled()) {
    24                 mutex( stdout ) stdout | "YES";
     23                stdout | acquire | "YES";
    2524                stdin | i;
    2625        } else {
    27                 mutex( stdout ) stdout | "NO";
     26                stdout | acquire | "NO";
    2827                stdin | i;
    2928        }
  • tests/pybin/test_run.py

    rb7fd9daf rf95634e  
    6565        def toString( cls, retcode, duration ):
    6666                if settings.generating :
    67                         if   retcode == TestResult.SUCCESS:     key = 'pass'; text = "Done   "
    68                         elif retcode == TestResult.TIMEOUT:     key = 'time'; text = "TIMEOUT"
    69                         else :  key = 'fail';   text = "ERROR code %d" % retcode
     67                        if   retcode == TestResult.SUCCESS:     text = "Done   "
     68                        elif retcode == TestResult.TIMEOUT:     text = "TIMEOUT"
     69                        else :                                          text = "ERROR code %d" % retcode
    7070                else :
    71                         if   retcode == TestResult.SUCCESS:     key = 'pass'; text = "PASSED "
    72                         elif retcode == TestResult.TIMEOUT:     key = 'time'; text = "TIMEOUT"
    73                         else :  key = 'fail';   text = "FAILED with code %d" % retcode
     71                        if   retcode == TestResult.SUCCESS:     text = "PASSED "
     72                        elif retcode == TestResult.TIMEOUT:     text = "TIMEOUT"
     73                        else :                                          text = "FAILED with code %d" % retcode
    7474
    7575                text += "    C%s - R%s" % (fmtDur(duration[0]), fmtDur(duration[1]))
    76                 return key, text
     76                return text
  • tests/test.py

    rb7fd9daf rf95634e  
    257257
    258258                # update output based on current action
    259                 result_key, result_txt = TestResult.toString( retcode, duration )
     259                result_txt = TestResult.toString( retcode, duration )
    260260
    261261                #print result with error if needed
     
    265265                        text = text + '\n' + error
    266266
    267                 return retcode == TestResult.SUCCESS, result_key, text
     267                return retcode == TestResult.SUCCESS, text
    268268        except KeyboardInterrupt:
    269                 return False, 'keybrd', ""
     269                return False, ""
    270270        # except Exception as ex:
    271271        #       print("Unexpected error in worker thread running {}: {}".format(t.target(), ex), file=sys.stderr)
     
    283283
    284284        failed = False
    285         rescnts = {     'pass': 0, 'fail': 0, 'time': 0, 'keybrd': 0 }
    286         other = 0
    287285
    288286        # for each test to run
     
    296294                )
    297295
    298                 for i, (succ, code, txt) in enumerate(timed(results, timeout = settings.timeout.total), 1) :
    299                         if code in rescnts.keys():
    300                                 rescnts[code] += 1
    301                         else:
    302                                 other += 1
    303 
     296                for i, (succ, txt) in enumerate(timed(results, timeout = settings.timeout.total), 1) :
    304297                        if not succ :
    305298                                failed = True
     
    326319        # clean the workspace
    327320        make('clean', output_file=subprocess.DEVNULL, error=subprocess.DEVNULL)
    328 
    329         print("{} passes, {} failures, {} timeouts, {} cancelled, {} other".format(rescnts['pass'], rescnts['fail'], rescnts['time'], rescnts['keybrd'], other))
    330321
    331322        return failed
     
    452443                        failed = run_tests(local_tests, options.jobs)
    453444                        if failed:
     445                                result = 1
    454446                                if not settings.continue_:
    455447                                        break
  • tools/perf/process_stat_array.py

    rb7fd9daf rf95634e  
    11#!/usr/bin/python3
    22
    3 import argparse, json, math, os, sys, re
    4 from PIL import Image
    5 import numpy as np
     3import argparse, os, sys, re
    64
    75def dir_path(string):
     
    1311parser = argparse.ArgumentParser()
    1412parser.add_argument('--path', type=dir_path, default=".cfadata", help= 'paste path to biog.txt file')
    15 parser.add_argument('--out', type=argparse.FileType('w'), default=sys.stdout)
    1613
    1714try :
     
    2623counters = {}
    2724
    28 max_cpu = 0
    29 min_cpu = 1000000
    30 max_tsc = 0
    31 min_tsc = 18446744073709551615
    32 
    3325#open the files
    3426for filename in filenames:
     
    3931                with open(os.path.join(root, filename), 'r') as file:
    4032                        for line in file:
    41                                 raw = [int(x.strip()) for x in line.split(',')]
    42 
    43                                 ## from/to
    44                                 high = (raw[1] >> 32)
    45                                 low  = (raw[1] & 0xffffffff)
    46                                 data = [me, raw[0], high, low]
    47                                 max_cpu = max(max_cpu, high, low)
    48                                 min_cpu = min(min_cpu, high, low)
    49 
    50                                 ## number
    51                                 # high = (raw[1] >> 8)
    52                                 # low  = (raw[1] & 0xff)
    53                                 # data = [me, raw[0], high, low]
    54                                 # max_cpu = max(max_cpu, low)
    55                                 # min_cpu = min(min_cpu, low)
    56 
    57 
    58                                 max_tsc = max(max_tsc, raw[0])
    59                                 min_tsc = min(min_tsc, raw[0])
     33                                # data = [int(x.strip()) for x in line.split(',')]
     34                                data = [int(line.strip())]
     35                                data = [me, *data]
    6036                                merged.append(data)
    6137
    62         except Exception as e:
    63                 print(e)
     38        except:
    6439                pass
    6540
    66 
    67 print({"max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc})
    6841
    6942# Sort by timestamp (the second element)
     
    7447merged.sort(key=takeSecond)
    7548
    76 json.dump({"values":merged, "max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc}, args.out)
     49# for m in merged:
     50#       print(m)
    7751
    78 # vmin = merged[ 0][1]
    79 # vmax = float(merged[-1][1] - vmin) / 2500000000.0
    80 # # print(vmax)
     52single = []
     53curr = 0
    8154
    82 # bins = []
    83 # for _ in range(0, int(math.ceil(vmax * 10))):
    84 #       bins.append([0] * (32 * 32))
     55# merge the data
     56# for (me, time, value) in merged:
     57for (me, value) in merged:
     58        # check now much this changes
     59        old = counters[me]
     60        change = value - old
     61        counters[me] = value
    8562
    86 # # print(len(bins))
    87 # bins = np.array(bins)
     63        # add change to the current
     64        curr = curr + change
     65        single.append( value )
    8866
    89 # rejected = 0
    90 # highest  = 0
     67        pass
    9168
    92 # for x in merged:
    93 #       b = int(float(x[1] - vmin) / 250000000.0)
    94 #       from_ = x[2]
    95 #       if from_ < 0 or from_ > 32:
    96 #               rejected += 1
    97 #               continue;
    98 #       to_   = x[3]
    99 #       if to_ < 0 or to_ > 32:
    100 #               rejected += 1
    101 #               continue;
    102 #       idx = (to_ * 32) + from_
    103 #       bins[b][idx] = bins[b][idx] + 1
    104 #       highest = max(highest, bins[b][idx])
    105 
    106 # bins = np.array(map(lambda x: np.int8(x * 255.0 / float(highest)), bins))
    107 
    108 # print([highest, rejected])
    109 # print(bins.shape)
    110 
    111 # im = Image.fromarray(bins)
    112 # im.save('test.png')
    113 
    114 # vmax = merged[-1][1]
    115 
    116 # diff = float(vmax - vmin) / 2500000000.0
    117 # print([vmin, vmax])
    118 # print([vmax - vmin, diff])
    119 
    120 # print(len(merged))
    121 
    122 # for b in bins:
    123 #       print(b)
    124 
    125 # single = []
    126 # curr = 0
    127 
    128 # # merge the data
    129 # # for (me, time, value) in merged:
    130 # for (me, value) in merged:
    131 #       # check now much this changes
    132 #       old = counters[me]
    133 #       change = value - old
    134 #       counters[me] = value
    135 
    136 #       # add change to the current
    137 #       curr = curr + change
    138 #       single.append( value )
    139 
    140 #       pass
    141 
    142 # print(single)
     69print(single)
    14370
    14471# single = sorted(single)[:len(single)-100]
Note: See TracChangeset for help on using the changeset viewer.