Changes in / [f95634e:b7fd9daf]


Ignore:
Files:
86 added
15 deleted
155 edited

Legend:

Unmodified
Added
Removed
  • benchmark/Cargo.toml.in

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

    rf95634e rb7fd9daf  
    2121include $(top_srcdir)/tools/build/cfa.make
    2222
    23 AM_CFLAGS = -O2 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror
     23AM_CFLAGS = -O3 -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
     200mutexStmt.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
    199216schedint.csv:
    200217        echo "building $@"
     
    357374## =========================================================================================================
    358375
     376mutexStmt$(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
     394mutexStmt-lock1$(EXEEXT):
     395        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock1.cfa
     396
     397mutexStmt-lock2$(EXEEXT):
     398        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock2.cfa
     399
     400mutexStmt-lock4$(EXEEXT):
     401        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock4.cfa
     402
     403mutexStmt-lock8$(EXEEXT):
     404        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/lock8.cfa
     405
     406mutexStmt-cpp1$(EXEEXT):
     407        $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp1.cc
     408
     409mutexStmt-cpp2$(EXEEXT):
     410        $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp2.cc
     411
     412mutexStmt-cpp4$(EXEEXT):
     413        $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp4.cc
     414
     415mutexStmt-cpp8$(EXEEXT):
     416        $(BENCH_V_CXX)$(CXXCOMPILE) -std=c++17 $(srcdir)/mutexStmt/cpp8.cc
     417
     418mutexStmt-monitor1$(EXEEXT):
     419        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor1.cfa
     420
     421mutexStmt-monitor2$(EXEEXT):
     422        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor2.cfa
     423
     424mutexStmt-monitor4$(EXEEXT):
     425        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/monitor4.cfa
     426
     427mutexStmt-no-stmt-lock1$(EXEEXT):
     428        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock1.cfa
     429
     430mutexStmt-no-stmt-lock2$(EXEEXT):
     431        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock2.cfa
     432
     433mutexStmt-no-stmt-lock4$(EXEEXT):
     434        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock4.cfa
     435
     436mutexStmt-no-stmt-lock8$(EXEEXT):
     437        $(BENCH_V_CFA)$(CFACOMPILE) $(srcdir)/mutexStmt/no_stmt_lock8.cfa
     438
     439mutexStmt-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
    359447schedint$(EXEEXT) :             \
    360448        schedint-cfa1.run       \
     
    524612## =========================================================================================================
    525613
    526 %-tokio$(EXEEXT): $(srcdir)/readyQ/%.rs $(srcdir)/bench.rs
    527         cd $(builddir) && cargo build --release
    528         cp $(builddir)/target/release/$(basename $@) $@
     614RDQBENCHES = \
     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
     632rdq-benches:
     633        +make $(RDQBENCHES)
     634
     635clean-rdq-benches:
     636        rm -rf $(RDQBENCHES) $(builddir)/target go.mod
     637
     638rdq-%-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
     644rdq-%-cfa$(EXEEXT): $(srcdir)/readyQ/%.cfa $(srcdir)/readyQ/rq_bench.hfa
     645        $(BENCH_V_CFA)$(CFACOMPILE) $< -o $@
     646
     647go.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
     654rdq-%-go$(EXEEXT): $(srcdir)/readyQ/%.go $(srcdir)/readyQ/bench.go go.mod
     655        $(BENCH_V_GOC)go build -o $@ $< $(srcdir)/readyQ/bench.go
     656
     657rdq-%-fibre$(EXEEXT): $(srcdir)/readyQ/%.cpp
     658        $(BENCH_V_CXX)$(CXXCOMPILE) $< -o $@ -lfibre -std=c++17 $(AM_CFLAGS)
     659
     660# ## =========================================================================================================
     661
     662CLEANFILES = $(RDQBENCHES) go.mod go.sum
     663
     664clean-local:
     665        -rm -rf target
  • benchmark/bench.h

    rf95634e rb7fd9daf  
    2121        return 1000000000LL * ts.tv_sec + ts.tv_nsec;
    2222} // bench_time
     23
     24
     25#if defined(__cforall)
     26struct test_spinlock {
     27        volatile bool lock;
     28};
     29
     30static 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
     36static inline void unlock( test_spinlock & this ) {
     37        __atomic_clear( &this.lock, __ATOMIC_RELEASE );
     38}
     39#endif
    2340
    2441#ifndef BENCH_N
  • benchmark/bench.rs

    rf95634e rb7fd9daf  
    11use std::io::{self, Write};
     2use std::option;
    23use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
    34use std::time::{Instant,Duration};
     5use std::u128;
    46
    57use clap::{Arg, ArgMatches};
     
    2729
    2830impl BenchData {
    29         pub fn new(options: ArgMatches, nthreads: usize) -> BenchData {
     31        pub fn new(options: ArgMatches, nthreads: usize, default_it: option::Option<u64>) -> BenchData {
    3032                let (clock_mode, stop_count, duration) = if options.is_present("iterations") {
    3133                        (false,
    3234                        options.value_of("iterations").unwrap().parse::<u64>().unwrap(),
     35                        -1.0)
     36                } else if !default_it.is_none() {
     37                        (false,
     38                        default_it.unwrap(),
    3339                        -1.0)
    3440                } else {
     
    4854        }
    4955
     56        #[allow(dead_code)]
    5057        pub async fn wait(&self, start: &Instant) -> Duration{
    5158                loop {
     
    6976}
    7077
     78// ==================================================
     79pub fn _lehmer64( state: &mut u128 ) -> u64 {
     80        *state = state.wrapping_mul(0xda942042e4dd58b5);
     81        return (*state >> 64) as u64;
     82}
  • benchmark/io/http/filecache.cfa

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

    rf95634e rb7fd9daf  
    118118// Get a fix reply based on the return code
    119119const char * http_msgs[] = {
    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",
     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",
    127127};
    128128static_assert( KNOWN_CODES == (sizeof(http_msgs) / sizeof(http_msgs[0])) );
  • benchmark/io/http/main.cfa

    rf95634e rb7fd9daf  
    190190                        init_protocol();
    191191                        {
    192                                 Worker workers[options.clopts.nworkers];
     192                                Worker * workers = anew(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();
    214215                                {
    215216                                        char buffer[128];
     
    249250
    250251                                sout | "Stopping connection threads..." | nonl; flush( sout );
     252                                adelete(workers);
    251253                        }
    252254                        sout | "done";
  • benchmark/io/http/options.cfa

    rf95634e rb7fd9daf  
    2121        false, // log
    2222        false, // stats
     23        true, // interactive
     24        0, // redirect
     25        0, // redirect
    2326
    2427        { // file_cache
     
    5255        // bool sqkpoll = false;
    5356        // bool iokpoll = false;
    54         unsigned nentries = 16;
     57        unsigned nentries = 0;
    5558        bool isolate = false;
    5659
     
    6265                {'\0', "isolate",        "Create one cluster per processor", isolate, parse_settrue},
    6366                {'\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},
    6469                {'\0', "stats",          "Enable statistics", options.stats, parse_settrue},
     70                {'\0', "shell",          "Disable interactive mode", options.interactive, parse_setfalse},
    6571                {'\0', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog},
    6672                {'\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},
     
    7985        parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left );
    8086
    81         if( !is_pow2(nentries) ) {
     87        if( nentries != 0 && !is_pow2(nentries) ) {
    8288                unsigned v = nentries;
    8389                v--;
     
    131137
    132138        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        }
    133162}
  • benchmark/io/http/options.hfa

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

    rf95634e rb7fd9daf  
    1111#include <fstream.hfa>
    1212#include <iofwd.hfa>
     13#include <io/types.hfa>
     14#include <mutex_stmt.hfa>
    1315
    1416#include <assert.h>
     
    2628#define PLAINTEXT_MEMCPY
    2729#define PLAINTEXT_NOCOPY
     30#define LINKED_IO
    2831
    2932struct https_msg_str {
     
    5356}
    5457
    55 static inline int answer( int fd, const char * it, int len) {
     58static inline int answer( int fd, const char * it, int len ) {
    5659        while(len > 0) {
    5760                // Call write
    5861                int ret = cfa_send(fd, it, len, 0, CFA_IO_LAZY);
    5962                if( ret < 0 ) {
    60                         if( errno == ECONNRESET || errno == EPIPE ) return -ECONNRESET;
     63                        if( errno == ECONNRESET || errno == EPIPE ) { close(fd); return -ECONNRESET; }
    6164                        if( errno == EAGAIN || errno == EWOULDBLOCK) return -EAGAIN;
    6265
     
    7780}
    7881
    79 int answer_header( int fd, size_t size ) {
    80         char buffer[512];
    81         char * it = buffer;
     82static int fill_header(char * it, size_t size) {
    8283        memcpy(it, http_msgs[OK200]->msg, http_msgs[OK200]->len);
    8384        it += http_msgs[OK200]->len;
    8485        int len = http_msgs[OK200]->len;
    8586        len += snprintf(it, 512 - len, "%d \n\n", size);
     87        return len;
     88}
     89
     90static int answer_header( int fd, size_t size ) {
     91        char buffer[512];
     92        int len = fill_header(buffer, size);
    8693        return answer( fd, buffer, len );
    8794}
     
    135142}
    136143
    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 
    178 int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) {
     144static int sendfile( int pipe[2], int fd, int ans_fd, size_t count ) {
    179145        unsigned sflags = SPLICE_F_MOVE; // | SPLICE_F_MORE;
    180146        off_t offset = 0;
     
    207173}
    208174
     175static 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
     192enum FSM_STATE {
     193        Initial,
     194        Retry,
     195        Error,
     196        Done,
     197};
     198
     199struct FSM_Result {
     200        FSM_STATE state;
     201        int error;
     202};
     203
     204static inline void ?{}(FSM_Result & this) { this.state = Initial; this.error = 0; }
     205static inline bool is_error(FSM_Result & this) { return Error == this.state; }
     206static inline bool is_done(FSM_Result & this) { return Done == this.state; }
     207
     208static inline int error(FSM_Result & this, int error) {
     209        this.error = error;
     210        this.state = Error;
     211        return error;
     212}
     213
     214static inline int done(FSM_Result & this) {
     215        this.state = Done;
     216        return 0;
     217}
     218
     219static inline int retry(FSM_Result & this) {
     220        this.state = Retry;
     221        return 0;
     222}
     223
     224static 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
     237generator header_g {
     238        io_future_t f;
     239        const char * next;
     240        int fd; size_t len;
     241        FSM_Result res;
     242};
     243
     244static 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
     250static 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
     260static 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
     268static 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
     305struct 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
     311static 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
     318static 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
     331static 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
     367generator splice_out_g {
     368        io_future_t f;
     369        int pipe; int fd; size_t len;
     370        FSM_Result res;
     371};
     372
     373static 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
     379static 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
     392static 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
     400static 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
     429SHORT_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
     436int 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
    209538//=============================================================================================
    210539
     
    214543
    215544const char * original_http_msgs[] = {
    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",
     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",
    224553};
    225554
     
    251580                Time now = timeHiRes();
    252581                strftime( buff, 100, "%a, %d %b %Y %H:%M:%S %Z", now );
    253                 sout | "Updated date to '" | buff | "'";
     582                // if( options.log ) sout | "Updated date to '" | buff | "'";
    254583
    255584                for(i; KNOWN_CODES) {
     
    264593                this.idx = (this.idx + 1) % 2;
    265594
    266                 sout | "Date thread sleeping";
     595                // if( options.log ) sout | "Date thread sleeping";
    267596
    268597                sleep(1`s);
  • benchmark/io/http/protocol.hfa

    rf95634e rb7fd9daf  
    1616
    1717int answer_error( int fd, HttpCode code );
    18 int answer_header( int fd, size_t size );
    1918int answer_plaintext( int fd );
    2019int answer_empty( int fd );
     20int 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 
    24 int sendfile( int pipe[2], int fd, int ans_fd, size_t count );
  • benchmark/io/http/worker.cfa

    rf95634e rb7fd9daf  
    122122                        }
    123123
    124                         // Send the header
    125                         int ret = answer_header(fd, count);
    126                         if( ret == -ECONNRESET ) break REQUEST;
    127 
    128124                        // Send the desired file
    129                         ret = sendfile( this.pipe, fd, ans_fd, count);
     125                        int ret = answer_sendfile( this.pipe, fd, ans_fd, count);
    130126                        if( ret == -ECONNRESET ) break REQUEST;
    131127
     
    134130
    135131                if( options.log ) sout | "=== Connection closed ===";
    136                 close(fd);
    137132                continue CONNECTION;
    138133        }
  • benchmark/readyQ/cycle.cpp

    rf95634e rb7fd9daf  
    4141                        Fibre * threads[tthreads];
    4242                        Partner thddata[tthreads];
    43                         for(int i = 0; i < tthreads; i++) {
     43                        for(unsigned i = 0; i < tthreads; i++) {
    4444                                unsigned pi = (i + nthreads) % tthreads;
    4545                                thddata[i].next = &thddata[pi].self;
    4646                        }
    47                         for(int i = 0; i < tthreads; i++) {
     47                        for(unsigned 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(int i = 0; i < nthreads; i++) {
     55                        for(unsigned i = 0; i < nthreads; i++) {
    5656                                thddata[i].self.post();
    5757                        }
     
    6262                        printf("\nDone\n");
    6363
    64                         for(int i = 0; i < tthreads; i++) {
     64                        for(unsigned i = 0; i < tthreads; i++) {
    6565                                thddata[i].self.post();
    6666                                fibre_join( threads[i], nullptr );
  • benchmark/readyQ/cycle.go

    rf95634e rb7fd9daf  
    6060        atomic.StoreInt32(&stop, 1)
    6161        end := time.Now()
    62         delta := end.Sub(start)
     62        duration := end.Sub(start)
    6363
    6464        fmt.Printf("\nDone\n")
     
    7474
    7575        p := message.NewPrinter(language.English)
    76         p.Printf("Duration (ms)        : %f\n", delta.Seconds());
     76        p.Printf("Duration (ms)        : %d\n", duration.Milliseconds())
    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) / delta.Seconds())
    82         p.Printf("ns per ops           : %18.2f\n", float64(delta.Nanoseconds()) / float64(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))
    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)) / delta.Seconds())
    86         p.Printf("ns per ops/procs     : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(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)))
    8787
    8888}
  • benchmark/readyQ/cycle.rs

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

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

    rf95634e rb7fd9daf  
    124124                                                return (r as *mut MyData, true);
    125125                                        }
    126                                         let got = self.ptr.compare_and_swap(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst);
    127                                         if got == expected {
     126                                        let got = self.ptr.compare_exchange_weak(expected, ctx as *mut MyCtx as u64, Ordering::SeqCst, Ordering::SeqCst);
     127                                        if got == Ok(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));
     287        let exp = Arc::new(bench::BenchData::new(options, nprocs, None));
    288288        let mut results = Result::new();
    289289
  • benchmark/readyQ/transfer.cfa

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

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

    rf95634e rb7fd9daf  
    8080                }
    8181
    82                 printf("Took %'ld ms\n", (end - start)`ms);
     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);
    8386                printf("Yields per second   : %'18.2lf\n", ((double)global_counter) / (end - start)`s);
    8487                printf("ns per yields       : %'18.2lf\n", ((double)(end - start)`ns) / global_counter);
    85                 printf("Total yields        : %'15llu\n", global_counter);
    8688                printf("Yields per procs    : %'15llu\n", global_counter / nprocs);
    8789                printf("Yields/sec/procs    : %'18.2lf\n", (((double)global_counter) / nprocs) / (end - start)`s);
  • benchmark/readyQ/yield.cpp

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

    rf95634e rb7fd9daf  
    1616import random
    1717import re
     18import socket
    1819import subprocess
    1920import sys
     
    9596        return nopts
    9697
     98# returns the first option with key 'opt'
     99def 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
    97110def actions_eta(actions):
    98111        time = 0
    99112        for a in actions:
    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
     113                o = search_option(a, '-d')
     114                if o :
     115                        time += int(o)
    107116        return time
     117
     118taskset_maps = None
     119
     120def 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
     140def 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
     157def settaskset(actions):
     158        return [settaskset_one(a) for a in actions]
    108159
    109160if __name__ == "__main__":
     
    115166        parser.add_argument('--file', nargs='?', type=argparse.FileType('w'), default=sys.stdout)
    116167        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')
    117169        parser.add_argument('command', metavar='command', type=str, nargs=1, help='the command prefix to run')
    118170        parser.add_argument('candidates', metavar='candidates', type=str, nargs='*', help='the candidate suffix to run')
     
    170222
    171223        # ================================================================================
    172         # Figure out all the combinations to run
     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
    173239        if options.list:
    174240                for a in actions:
     
    180246        # Prepare to run
    181247
    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 
    186248        random.shuffle(actions)
    187249
     
    191253        first = True
    192254        for i, a in enumerate(actions):
    193                 sa = " ".join(a)
     255                sa = " ".join(a[3:] if withtaskset else a)
    194256                if first:
    195257                        first = False
     
    208270                                match = re.search("^(.*):(.*)$", s)
    209271                                if match:
    210                                         fields[match.group(1).strip()] = float(match.group(2).strip().replace(',',''))
    211 
    212                 options.file.write(json.dumps([a[0][2:], sa, fields]))
     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]))
    213278                options.file.flush()
    214279
  • doc/theses/andrew_beach_MMath/Makefile

    rf95634e rb7fd9daf  
    3131
    3232# The main rule, it does all the tex/latex processing.
    33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} Makefile | ${BUILD}
     33${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} termhandle.pstex resumhandle.pstex 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
    4246
    4347# Step through dvi & postscript to handle xfig specials.
  • doc/theses/andrew_beach_MMath/code/CondCatch.java

    rf95634e rb7fd9daf  
    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 
    228        private static long loop(int times) {
    239                long startTime = System.nanoTime();
    2410                for (int count = 0 ; count < times ; ++count) {
    2511                        try {
    26                                 cond_catch();
     12                                try {
     13                                        throw new EmptyException();
     14                                } catch (EmptyException exc) {
     15                                        if (!should_catch) {
     16                                                throw exc;
     17                                        }
     18                                }
    2719                        } catch (EmptyException exc) {
    2820                                // ...
     
    4638
    4739                long time = loop(times);
    48                 System.out.println("Run-Time (ns): " + time);
     40                System.out.format("Run-Time (s): %.1f%n", time / 1_000_000_000.);
    4941        }
    5042}
  • doc/theses/andrew_beach_MMath/code/ThrowEmpty.java

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

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

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

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.h>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
    8 
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    109
    1110bool should_catch = false;
    12 
    13 void throw_exception() {
    14         throw (empty_exception){&empty_vt};
    15 }
    16 
    17 void cond_catch() {
    18         try {
    19                 throw_exception();
    20         } catch (empty_exception * exc ; should_catch) {
    21                 asm volatile ("# catch block (conditional)");
    22         }
    23 }
    2411
    2512int main(int argc, char * argv[]) {
    2613        unsigned int times = 1;
    2714        if (1 < argc) {
    28                 times = strtol(argv[1], 0p, 10);
     15                times = strto(argv[1], 0p, 10);
    2916        }
    3017        if (2 < argc) {
    31                 should_catch = strtol(argv[2], 0p, 10);
     18                should_catch = (unsigned int)strto(argv[2], 0p, 2);
    3219        }
    3320
     
    3522        for (unsigned int count = 0 ; count < times ; ++count) {
    3623                try {
    37                         cond_catch();
     24                        throw (empty_exception){&empty_vt};
     25                } catch (empty_exception * exc ; should_catch) {
     26                        asm volatile ("# catch block (conditional)");
    3827                } catch (empty_exception * exc) {
    3928                        asm volatile ("# catch block (unconditional)");
     
    4130        }
    4231        Time end_time = timeHiRes();
    43         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     32        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4433}
  • doc/theses/andrew_beach_MMath/code/cond-catch.cpp

    rf95634e rb7fd9daf  
    44#include <exception>
    55#include <iostream>
     6#include <iomanip>
    67
     8using namespace std;
    79using namespace std::chrono;
    810
     
    1012
    1113bool should_catch = false;
    12 
    13 void throw_exception() {
    14         throw EmptyException();
    15 }
    16 
    17 void 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 }
    2714
    2815int main(int argc, char * argv[]) {
     
    3825    for (unsigned int count = 0 ; count < times ; ++count) {
    3926        try {
    40                         cond_catch();
     27                        try {
     28                                throw EmptyException();
     29                        } catch (EmptyException & exc) {
     30                                if (!should_catch) {
     31                                        throw;
     32                                }
     33                                asm volatile ("# catch block (conditional)");
     34                        }
    4135                } catch (EmptyException &) {
    4236                        asm volatile ("# catch block (unconditional)");
     
    4539        time_point<steady_clock> end_time = steady_clock::now();
    4640        nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time);
    47         std::cout << "Run-Time (ns): " << duration.count() << std::endl;
     41        cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl;
    4842}
  • doc/theses/andrew_beach_MMath/code/cond-fixup.cfa

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
    8 
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    109
    1110bool should_catch = false;
    12 
    13 void throw_exception() {
    14         throwResume (empty_exception){&empty_vt};
    15 }
    16 
    17 void cond_catch() {
    18         try {
    19                 throw_exception();
    20         } catchResume (empty_exception * exc ; should_catch) {
    21                 asm volatile ("# fixup block (conditional)");
    22         }
    23 }
    2411
    2512int main(int argc, char * argv[]) {
    2613        unsigned int times = 1;
    2714        if (1 < argc) {
    28                 times = strtol(argv[1], 0p, 10);
     15                times = strto(argv[1], 0p, 10);
    2916        }
    3017        if (2 < argc) {
    31                 should_catch = strtol(argv[2], 0p, 10);
     18                should_catch = (unsigned int)strto(argv[2], 0p, 2);
    3219        }
    3320
     
    3522        for (unsigned int count = 0 ; count < times ; ++count) {
    3623                try {
    37                         cond_catch();
     24                        throwResume (empty_exception){&empty_vt};
     25                } catchResume (empty_exception * exc ; should_catch) {
     26                        asm volatile ("# fixup block (conditional)");
    3827                } catchResume (empty_exception * exc) {
    3928                        asm volatile ("# fixup block (unconditional)");
     
    4130        }
    4231        Time end_time = timeHiRes();
    43         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     32        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4433}
  • doc/theses/andrew_beach_MMath/code/resume-detor.cfa

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
    8 
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    109
    1110struct WithDestructor {};
     
    1716void unwind_destructor(unsigned int frames) {
    1817        if (frames) {
    19 
    2018                WithDestructor object;
    2119                unwind_destructor(frames - 1);
     
    2927        unsigned int total_frames = 1;
    3028        if (1 < argc) {
    31                 times = strtol(argv[1], 0p, 10);
     29                times = strto(argv[1], 0p, 10);
    3230        }
    3331        if (2 < argc) {
    34                 total_frames = strtol(argv[2], 0p, 10);
     32                total_frames = strto(argv[2], 0p, 10);
    3533        }
    3634
     
    4442        }
    4543        Time end_time = timeHiRes();
    46         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     44        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4745}
  • doc/theses/andrew_beach_MMath/code/resume-empty.cfa

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

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
    8 
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    109
    1110void unwind_finally(unsigned int frames) {
     
    2524        unsigned int total_frames = 1;
    2625        if (1 < argc) {
    27                 times = strtol(argv[1], 0p, 10);
     26                times = strto(argv[1], 0p, 10);
    2827        }
    2928        if (2 < argc) {
    30                 total_frames = strtol(argv[2], 0p, 10);
     29                total_frames = strto(argv[2], 0p, 10);
    3130        }
    3231
     
    4039        }
    4140        Time end_time = timeHiRes();
    42         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     41        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4342}
  • doc/theses/andrew_beach_MMath/code/resume-other.cfa

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
     9exception not_raised_exception;
    810
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
    10 
    11 EHM_EXCEPTION(not_raised_exception)();
    12 
    13 void unwind_other(unsigned int frames) {
     11void nounwind_other(unsigned int frames) {
    1412        if (frames) {
    1513                try {
    16                         unwind_other(frames - 1);
     14                        nounwind_other(frames - 1);
    1715                } catchResume (not_raised_exception *) {
    1816                        asm volatile ("# fixup block (stack)");
     
    2725        unsigned int total_frames = 1;
    2826        if (1 < argc) {
    29                 times = strtol(argv[1], 0p, 10);
     27                times = strto(argv[1], 0p, 10);
    3028        }
    3129        if (2 < argc) {
    32                 total_frames = strtol(argv[2], 0p, 10);
     30                total_frames = strto(argv[2], 0p, 10);
    3331        }
    3432
     
    3634        for (int count = 0 ; count < times ; ++count) {
    3735                try {
    38                         unwind_other(total_frames);
     36                        nounwind_other(total_frames);
    3937                } catchResume (empty_exception *) {
    4038                        asm volatile ("# fixup block (base)");
     
    4240        }
    4341        Time end_time = timeHiRes();
    44         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     42        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4543}
  • doc/theses/andrew_beach_MMath/code/run.sh

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

    rf95634e rb7fd9daf  
    44# test.sh LANGUAGE TEST
    55#   Run the TEST in LANGUAGE.
     6# test.sh -a
     7#   Build all tests.
    68# test.sh -b SOURCE_FILE...
    79#   Build a test from SOURCE_FILE(s).
     10# test.sh -c
     11#   Clean all executables.
    812# test.sh -v LANGUAGE TEST FILE
    913#   View the result from TEST in LANGUAGE stored in FILE.
    1014
    11 readonly ITERATIONS=1000000 # 1 000 000, one million
     15readonly DIR=$(dirname "$(readlink -f "$0")")
     16cd $DIR
     17
     18readonly MIL=000000
     19# Various preset values used as arguments.
     20readonly ITERS_1M=1$MIL
     21readonly ITERS_10M=10$MIL
     22readonly ITERS_100M=100$MIL
     23readonly ITERS_1000M=1000$MIL
    1224readonly STACK_HEIGHT=100
    1325
     
    2335        case "$1" in
    2436        *.cfa)
    25                 # Requires a symbolic link.
    26                 mmake "${1%.cfa}" "$1" ./cfa -DNDEBUG -nodebug -O3 "$1" -o "${1%.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}"
    2743                ;;
    2844        *.cpp)
    29                 mmake "${1%.cpp}-cpp" "$1" g++ -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp"
     45                mmake "${1%.cpp}-cpp" "$1" g++-10 -DNDEBUG -O3 "$1" -o "${1%.cpp}-cpp"
    3046                ;;
    3147        *.java)
     
    3955)
    4056
    41 if [ "-b" = "$1" ]; then
     57if [ "-a" = "$1" ]; then
     58        for file in *.cfa *.cpp *.java; do
     59                build $file
     60        done
     61        exit 0
     62elif [ "-b" = "$1" ]; then
    4263        for file in "${@:2}"; do
    4364                build $file
    4465        done
    4566        exit 0
     67elif [ "-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
    4673elif [ "-v" = "$1" -a 4 = "$#" ]; then
    47     TEST_LANG="$2"
    48     TEST_CASE="$3"
    49     VIEW_FILE="$4"
     74        TEST_LANG="$2"
     75        TEST_CASE="$3"
     76        VIEW_FILE="$4"
    5077elif [ 2 -eq "$#" ]; then
    5178        TEST_LANG="$1"
     
    6390
    6491case "$TEST_CASE" in
    65 cond-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         ;;
    72 cond-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         ;;
    79 cross-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         ;;
    86 cross-finally)
    87         CFAT="./cross-finally $ITERATIONS"
    88         CFAR=unsupported
    89         CPP=unsupported
    90         JAVA="java CrossFinally $ITERATIONS"
    91         PYTHON="./cross_finally.py $ITERATIONS"
     92raise-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"
    9298        ;;
    9399raise-detor)
    94         CFAT="./throw-detor $ITERATIONS $STACK_HEIGHT"
    95         CFAR="./resume-detor $ITERATIONS $STACK_HEIGHT"
    96         CPP="./throw-detor-cpp $ITERATIONS $STACK_HEIGHT"
     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"
    97103        JAVA=unsupported
    98104        PYTHON=unsupported
    99105        ;;
    100 raise-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         ;;
    107106raise-finally)
    108         CFAT="./throw-finally $ITERATIONS $STACK_HEIGHT"
    109         CFAR="./resume-finally $ITERATIONS $STACK_HEIGHT"
     107        CFAT="./throw-finally $ITERS_1M $STACK_HEIGHT"
     108        CFAR="./resume-finally $ITERS_10M $STACK_HEIGHT"
    110109        CPP=unsupported
    111         JAVA="java ThrowFinally $ITERATIONS $STACK_HEIGHT"
    112         PYTHON="./throw_finally.py $ITERATIONS $STACK_HEIGHT"
     110        JAVA="java ThrowFinally $ITERS_1M $STACK_HEIGHT"
     111        PYTHON="./throw-finally.py $ITERS_1M $STACK_HEIGHT"
    113112        ;;
    114113raise-other)
    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"
     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        ;;
     120try-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        ;;
     127try-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        ;;
     134cond-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        ;;
     141cond-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        ;;
     148fixup-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        ;;
     155fixup-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"
    120161        ;;
    121162*)
     
    140181
    141182if [ -n "$VIEW_FILE" ]; then
    142     grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time (ns): !!;T;p'
    143     exit
     183        grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time.*: !!;T;p'
     184        exit
    144185fi
    145186
  • doc/theses/andrew_beach_MMath/code/throw-detor.cfa

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
    8 
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    109
    1110struct WithDestructor {};
     
    2827        unsigned int total_frames = 1;
    2928        if (1 < argc) {
    30                 times = strtol(argv[1], 0p, 10);
     29                times = strto(argv[1], 0p, 10);
    3130        }
    3231        if (2 < argc) {
    33                 total_frames = strtol(argv[2], 0p, 10);
     32                total_frames = strto(argv[2], 0p, 10);
    3433        }
    3534
     
    4342        }
    4443        Time end_time = timeHiRes();
    45         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     44        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4645}
  • doc/theses/andrew_beach_MMath/code/throw-detor.cpp

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

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
    8 
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    109
    1110void unwind_empty(unsigned int frames) {
    1211        if (frames) {
    1312                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 = strtol(argv[1], 0p, 10);
     23                times = strto(argv[1], 0p, 10);
    2424        }
    2525        if (2 < argc) {
    26                 total_frames = strtol(argv[2], 0p, 10);
     26                total_frames = strto(argv[2], 0p, 10);
    2727        }
    2828
     
    3636        }
    3737        Time end_time = timeHiRes();
    38         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     38        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    3939}
  • doc/theses/andrew_beach_MMath/code/throw-empty.cpp

    rf95634e rb7fd9daf  
    11// Throw Across Empty Function
    22#include <chrono>
     3#include <cstdio>
    34#include <cstdlib>
    45#include <exception>
    56#include <iostream>
     7#include <iomanip>
    68
     9using namespace std;
    710using namespace std::chrono;
    811
     
    1215        if (frames) {
    1316                unwind_empty(frames - 1);
     17                if (-1 == frames) printf("~");
    1418        } else {
    1519                throw (EmptyException){};
     
    3741        time_point<steady_clock> end_time = steady_clock::now();
    3842        nanoseconds duration = duration_cast<nanoseconds>(end_time - start_time);
    39         std::cout << "Run-Time (ns): " << duration.count() << std::endl;
     43        cout << "Run-Time (s): " << fixed << setprecision(1) << duration.count() / 1'000'000'000. << endl;
    4044}
  • doc/theses/andrew_beach_MMath/code/throw-finally.cfa

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
    89
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     10unsigned int frames;                                                                    // use global because of gcc thunk problem
    1011
    11 void unwind_finally(unsigned int frames) {
     12void unwind_finally(unsigned int dummy) {
    1213        if (frames) {
     14                frames -= 1;
    1315                try {
    14                         unwind_finally(frames - 1);
     16                        unwind_finally(42);
    1517                } finally {
    1618                        asm volatile ("# finally block");
    1719                }
    1820        } else {
     21                dummy = 42;
    1922                throw (empty_exception){&empty_vt};
    2023        }
     
    2528        unsigned int total_frames = 1;
    2629        if (1 < argc) {
    27                 times = strtol(argv[1], 0p, 10);
     30                times = strto(argv[1], 0p, 10);
    2831        }
    2932        if (2 < argc) {
    30                 total_frames = strtol(argv[2], 0p, 10);
     33                total_frames = strto(argv[2], 0p, 10);
    3134        }
     35        frames = total_frames;
    3236
    3337        Time start_time = timeHiRes();
    3438        for (int count = 0 ; count < times ; ++count) {
    3539                try {
    36                         unwind_finally(total_frames);
     40                        unwind_finally(42);
    3741                } catch (empty_exception *) {
    3842                        asm volatile ("# catch block");
     
    4044        }
    4145        Time end_time = timeHiRes();
    42         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     46        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4347}
  • doc/theses/andrew_beach_MMath/code/throw-other.cfa

    rf95634e rb7fd9daf  
    33#include <exception.hfa>
    44#include <fstream.hfa>
    5 #include <stdlib.hfa>
     5#include <stdlib.hfa>                                                                   // strto
    66
    7 EHM_EXCEPTION(empty_exception)();
     7exception empty_exception;
     8vtable(empty_exception) empty_vt;
     9exception not_raised_exception;
    810
    9 EHM_VIRTUAL_TABLE(empty_exception, empty_vt);
     11unsigned int frames;                                                                    // use global because of gcc thunk problem
    1012
    11 EHM_EXCEPTION(not_raised_exception)();
    12 
    13 void unwind_other(unsigned int frames) {
     13void unwind_other(unsigned int dummy) {
    1414        if (frames) {
     15                frames -= 1;
    1516                try {
    16                         unwind_other(frames - 1);
     17                        unwind_other(42);
    1718                } catch (not_raised_exception *) {
    1819                        asm volatile ("# catch block (stack)");
    1920                }
    2021        } else {
     22                dummy = 42;
    2123                throw (empty_exception){&empty_vt};
    2224        }
     
    2729        unsigned int total_frames = 1;
    2830        if (1 < argc) {
    29                 times = strtol(argv[1], 0p, 10);
     31                times = strto(argv[1], 0p, 10);
    3032        }
    3133        if (2 < argc) {
    32                 total_frames = strtol(argv[2], 0p, 10);
     34                total_frames = strto(argv[2], 0p, 10);
    3335        }
     36        frames = total_frames;
    3437
    3538        Time start_time = timeHiRes();
    3639        for (int count = 0 ; count < times ; ++count) {
    3740                try {
    38                         unwind_other(total_frames);
     41                        unwind_other(42);
    3942                } catch (empty_exception *) {
    4043                        asm volatile ("# catch block (base)");
     
    4245        }
    4346        Time end_time = timeHiRes();
    44         sout | "Run-Time (ns): " | (end_time - start_time)`ns;
     47        sout | "Run-Time (s): " | wd(0,1, (end_time - start_time)`ns / 1_000_000_000.);
    4548}
  • doc/theses/andrew_beach_MMath/code/throw-other.cpp

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

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

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

    rf95634e rb7fd9daf  
    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 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,
     10allowing programmers to learn \CFA on an as-needed basis.
    1011
    1112Only 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{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
    20 char @i@; int @i@; double @i@;
    21 int @f@(); double @f@();
    22 void @g@( int ); void @g@( double );
    23 \end{lstlisting}
     19\begin{cfa}
     20char i; int i; double i;
     21int f(); double f();
     22void g( int ); void g( double );
     23\end{cfa}
    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 a pointer-type but each
     48Reference-types are written the same way as pointer-types, but each
    4949asterisk (@*@) is replaced with a ampersand (@&@);
    50 this includes cv-qualifiers and multiple levels of reference.
    51 
    52 Generally, references act like pointers with an implicate dereferencing
     50this includes cv-qualifiers (\snake{const} and \snake{volatile})
     51and multiple levels of reference.
     52
     53Generally, references act like pointers with an implicit dereferencing
    5354operation added to each use of the variable.
    5455These automatic dereferences may be disabled with the address-of operator
     
    6364int && rri = ri;
    6465rri = 3;
    65 &ri = &j; // rebindable
     66&ri = &j;
    6667ri = 5;
    6768\end{cfa}
     
    7980\end{minipage}
    8081
    81 References are intended for pointer situations where dereferencing is the common usage,
    82 \ie the value is more important than the pointer.
     82References are intended to be used when the indirection of a pointer is
     83required, but the address is not as important as the value and dereferencing
     84is the common usage.
    8385Mutable references may be assigned to by converting them to a pointer
    84 with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above
     86with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above.
    8587
    8688\section{Operators}
    8789
    8890\CFA implements operator overloading by providing special names, where
    89 operator usages are translated into function calls using these names.
     91operator expressions are translated into function calls using these names.
    9092An operator name is created by taking the operator symbols and joining them with
    9193@?@s to show where the arguments go.
    9294For example,
    9395infixed multiplication is @?*?@, while prefix dereference is @*?@.
    94 This syntax make it easy to tell the difference between prefix operations
    95 (such as @++?@) and post-fix operations (@?++@).
    96 For example, plus and equality operators are defined for a point type.
     96This syntax makes it easy to tell the difference between prefix operations
     97(such as @++?@) and postfix operations (@?++@).
     98
     99As an example, here are the addition and equality operators for a point type.
    97100\begin{cfa}
    98101point ?+?(point a, point b) { return point{a.x + b.x, a.y + b.y}; }
     
    102105}
    103106\end{cfa}
    104 Note these special names are not limited to builtin
    105 operators, and hence, may be used with arbitrary types.
    106 \begin{cfa}
    107 double ?+?( 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.
    112 Because operators are never part of the type definition they may be added
    113 at any time, including on built-in types.
     107Note that this syntax works effectively as a textual transformation;
     108the compiler converts all operators into functions and then resolves them
     109normally. This means any combination of types may be used,
     110although nonsensical ones (like @double ?==?(point, int);@) are discouraged.
     111This feature is also used for all builtin operators as well,
     112although those are implicitly provided by the language.
    114113
    115114%\subsection{Constructors and Destructors}
    116 
    117 \CFA also provides constructors and destructors as operators, which means they
    118 are functions with special operator names rather than type names in \Cpp.
    119 While constructors and destructions are normally called implicitly by the compiler,
    120 the special operator names, allow explicit calls.
    121 
    122 % Placement new means that this is actually equivalent to C++.
     115In \CFA, constructors and destructors are operators, which means they are
     116functions with special operator names, rather than type names as in \Cpp.
     117Both constructors and destructors can be implicity called by the compiler,
     118however the operator names allow explicit calls.
     119% Placement new means that this is actually equivant to C++.
    123120
    124121The special name for a constructor is @?{}@, which comes from the
     
    129126struct Example { ... };
    130127void ?{}(Example & this) { ... }
     128{
     129        Example a;
     130        Example b = {};
     131}
    131132void ?{}(Example & this, char first, int num) { ... }
    132 Example a;              // implicit constructor calls
    133 Example b = {};
    134 Example c = {'a', 2};
    135 \end{cfa}
    136 Both @a@ and @b@ are initialized with the first constructor,
    137 while @c@ is initialized with the second.
    138 Constructor calls can be replaced with C initialization using special operator \lstinline{@=}.
    139 \begin{cfa}
    140 Example d @= {42};
    141 \end{cfa}
     133{
     134        Example c = {'a', 2};
     135}
     136\end{cfa}
     137Both @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.
     140Currently, there is no general way to skip initialization.
     141% I don't use @= anywhere in the thesis.
     142
    142143% I don't like the \^{} symbol but $^\wedge$ isn't better.
    143144Similarly, destructors use the special name @^?{}@ (the @^@ has no special
    144145meaning).
    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.
    147146\begin{cfa}
    148147void ^?{}(Example & this) { ... }
    149148{
    150         Example e;      // implicit constructor call
    151         ^?{}(e);                // explicit destructor call
    152         ?{}(e);         // explicit constructor call
    153 } // implicit destructor call
     149        Example d;
     150        ^?{}(d);
     151
     152        Example e;
     153} // Implicit call of ^?{}(e);
    154154\end{cfa}
    155155
     
    203203do_twice(i);
    204204\end{cfa}
    205 Any object with a type fulfilling the assertion may be passed as an argument to
     205Any value 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 is a better match.
    228 % Aaron's thesis might be a good reference here.
    229 
    230 To avoid typing long lists of assertions, constraints can be collect into
    231 convenient package called a @trait@, which can then be used in an assertion
     227would then be a better match.\cite{Moss19}
     228
     229To avoid typing long lists of assertions, constraints can be collected into
     230a convenient package called a @trait@, which can then be used in an assertion
    232231instead of the individual constraints.
    233232\begin{cfa}
     
    243242functions and variables, and are usually used to create a shorthand for, and
    244243give descriptive names to, common groupings of assertions describing a certain
    245 functionality, like @sumable@, @listable@, \etc.
     244functionality, like @summable@, @listable@, \etc.
    246245
    247246Polymorphic structures and unions are defined by qualifying an aggregate type
    248247with @forall@. The type variables work the same except they are used in field
    249 declarations instead of parameters, returns, and local variable declarations.
     248declarations instead of parameters, returns and local variable declarations.
    250249\begin{cfa}
    251250forall(dtype T)
     
    253252        node(T) * next;
    254253        T * data;
    255 }
     254};
    256255node(int) inode;
    257256\end{cfa}
     
    263262
    264263\section{Control Flow}
    265 \CFA has a number of advanced control-flow features: @generator@, @coroutine@, @monitor@, @mutex@ parameters, and @thread@.
     264\CFA has a number of advanced control-flow features: @generator@, @coroutine@,
     265@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. Instead
     272required to finish execution when control is handed back to the caller.
     273Instead,
    273274they may suspend execution at any time and be resumed later at the point of
    274 last suspension. (Generators are stackless and coroutines are stackful.) These
     275last suspension.
     276Coroutine
    275277types are not concurrent but share some similarities along with common
    276 underpinnings, so they are combined with the \CFA threading library. Further
    277 discussion in this section only refers to the coroutine because generators are
    278 similar.
     278underpinnings, so they are combined with the \CFA threading library.
     279% I had mention of generators, but they don't actually matter here.
    279280
    280281In \CFA, a coroutine is created using the @coroutine@ keyword, which is an
     
    293294};
    294295CountUp countup;
    295 for (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}[numbers=left,numberstyle=\scriptsize\sf]
     300\begin{cfa}
    301301void main(CountUp & this) {
    302         for (unsigned int up = 0;; ++up) {
    303                 this.next = up;
     302        for (unsigned int next = 0 ; true ; ++next) {
     303                this.next = next;
    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 resumer
    310 without terminating the coroutine's function(s).
     309@suspend@ statement is used to return execution to the coroutine's caller
     310without terminating the coroutine's function.
    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 @main@ line~\ref{suspend}.  The @resume@ function takes
    316 a reference to the coroutine structure and returns the same reference. The
    317 return value allows easy access to communication variables defined in the
    318 coroutine object. For example, the @next@ value for coroutine object @countup@
    319 is both generated and collected in the single expression:
    320 @resume(countup).next@.
     315statement. In this case there is only one and, hence, the difference between
     316subsequent calls is the state of variables inside the function and the
     317coroutine object.
     318The return value of @resume@ is a reference to the coroutine, to make it
     319convent to access fields of the coroutine in the same expression.
     320Here is a simple example in a helper function:
     321\begin{cfa}
     322unsigned int get_next(CountUp & this) {
     323        return resume(this).next;
     324}
     325\end{cfa}
     326
     327When the main function returns, the coroutine halts and can no longer be
     328resumed.
    321329
    322330\subsection{Monitor and Mutex Parameter}
    323 Concurrency does not guarantee ordering; without ordering results are
     331Concurrency does not guarantee ordering; without ordering, results are
    324332non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@
    325333(mutual exclusion) parameters. A monitor is another kind of aggregate, where
     
    327335@mutex@ parameters.
    328336
    329 A function that requires deterministic (ordered) execution, acquires mutual
     337A function that requires deterministic (ordered) execution acquires mutual
    330338exclusion on a monitor object by qualifying an object reference parameter with
    331 @mutex@.
    332 \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
    333 void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB);
    334 \end{lstlisting}
     339the @mutex@ qualifier.
     340\begin{cfa}
     341void example(MonitorA & mutex argA, MonitorB & mutex argB);
     342\end{cfa}
    335343When the function is called, it implicitly acquires the monitor lock for all of
    336344the mutex parameters without deadlock.  This semantics means all functions with
     
    339347
    340348\subsection{Thread}
    341 Functions, generators, and coroutines are sequential so there is only a single
     349Functions, generators and coroutines are sequential, so there is only a single
    342350(but potentially sophisticated) execution path in a program. Threads introduce
    343351multiple execution paths that continue independently.
    344352
    345353For threads to work safely with objects requires mutual exclusion using
    346 monitors and mutex parameters. For threads to work safely with other threads,
     354monitors and mutex parameters. For threads to work safely with other threads
    347355also requires mutual exclusion in the form of a communication rendezvous, which
    348356also supports internal synchronization as for mutex objects. For exceptions,
     
    362370{
    363371        StringWorker stringworker; // fork thread running in "main"
    364 } // implicitly join with thread / wait for completion
     372} // Implicit call to join(stringworker), waits for completion.
    365373\end{cfa}
    366374The thread main is where a new thread starts execution after a fork operation
  • doc/theses/andrew_beach_MMath/features.tex

    rf95634e rb7fd9daf  
    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, with some other information, in a handler.
     32exception. This code is given, along with some other information,
     33in a handler.
    3334
    3435A handler has three common features: the previously mentioned user code, a
    35 region of code it guards, and an exception label/condition that matches
    36 the raised exception.
     36region of code it guards and an exception label/condition that matches
     37against the raised exception.
    3738Only raises inside the guarded region and raising exceptions that match the
    3839label can be handled by a given handler.
     
    4142
    4243The @try@ statements of \Cpp, Java and Python are common examples. All three
    43 show the common features of guarded region, raise, matching and handler.
    44 \begin{cfa}
    45 try {                           // guarded region
    46         ...     
    47         throw exception;        // raise
    48         ...     
    49 } catch( exception ) {  // matching condition, with exception label
    50         ...                             // handler code
    51 }
    52 \end{cfa}
     44also show another common feature of handlers: they are grouped by the guarded
     45region.
    5346
    5447\subsection{Propagation}
    5548After an exception is raised comes what is usually the biggest step for the
    56 EHM: finding and setting up the handler for execution. The propagation from raise to
     49EHM: finding and setting up the handler for execution.
     50The propagation from raise to
    5751handler can be broken up into three different tasks: searching for a handler,
    5852matching against the handler and installing the handler.
     
    6054\paragraph{Searching}
    6155The EHM begins by searching for handlers that might be used to handle
    62 the exception. The search is restricted to
    63 handlers that have the raise site in their guarded
     56the exception.
     57The search will find handlers that have the raise site in their guarded
    6458region.
    6559The search includes handlers in the current function, as well as any in
     
    6761
    6862\paragraph{Matching}
    69 Each handler found is matched with the raised exception. The exception
     63Each handler found is with the raised exception. The exception
    7064label defines a condition that is used with the exception and decides if
    7165there is a match or not.
     66%
    7267In languages where the first match is used, this step is intertwined with
    7368searching; a match check is performed immediately after the search finds
     
    8479different course of action for this case.
    8580This situation only occurs with unchecked exceptions as checked exceptions
    86 (such as in Java) are guaranteed to find a matching handler.
     81(such as in Java) can make the guarantee.
    8782The unhandled action is usually very general, such as aborting the program.
    8883
    8984\paragraph{Hierarchy}
    9085A common way to organize exceptions is in a hierarchical structure.
    91 This pattern comes from object-orientated languages where the
     86This pattern comes from object-oriented languages where the
    9287exception hierarchy is a natural extension of the object hierarchy.
    9388
     
    9893A handler labeled with any given exception can handle exceptions of that
    9994type or any child type of that exception. The root of the exception hierarchy
    100 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types,
     95(here \code{C}{exception}) acts as a catch-all, leaf types catch single types
    10196and the exceptions in the middle can be used to catch different groups of
    10297related exceptions.
    10398
    10499This system has some notable advantages, such as multiple levels of grouping,
    105 the ability for libraries to add new exception types, and the isolation
     100the ability for libraries to add new exception types and the isolation
    106101between different sub-hierarchies.
    107102This design is used in \CFA even though it is not a object-orientated
    108 language; so different tools are used to create the hierarchy.
     103language, so different tools are used to create the hierarchy.
    109104
    110105% Could I cite the rational for the Python IO exception rework?
     
    124119from the raise to the handler and back again.
    125120So far, only communication of the exception's identity is covered.
    126 A common communication method for passing more information is putting fields into the exception instance
     121A common communication method for adding information to an exception
     122is putting fields into the exception instance
    127123and giving the handler access to them.
    128 Using reference fields pointing to data at the raise location allows data to be
    129 passed in both directions.
     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.
     126Passing references or pointers allows data at the raise location to be
     127updated, passing information in both directions.
    130128
    131129\section{Virtuals}
    132 \label{s:Virtuals}
     130\label{s:virtuals}
     131A common feature in many programming languages is a tool to pair code
     132(behaviour) with data.
     133In \CFA, this is done with the virtual system,
     134which allow type information to be abstracted away, recovered and allow
     135operations to be performed on the abstract objects.
     136
    133137Virtual types and casts are not part of \CFA's EHM nor are they required for
    134138an EHM.
    135139However, one of the best ways to support an exception hierarchy
    136140is via a virtual hierarchy and dispatch system.
    137 Ideally, the virtual system should have been part of \CFA before the work
     141Ideally, the virtual system would have been part of \CFA before the work
    138142on exception handling began, but unfortunately it was not.
    139143Hence, only the features and framework needed for the EHM were
    140 designed and implemented for this thesis. Other features were considered to ensure that
     144designed and implemented for this thesis.
     145Other features were considered to ensure that
    141146the structure could accommodate other desirable features in the future
    142147but are not implemented.
    143148The rest of this section only discusses the implemented subset of the
    144 virtual-system design.
     149virtual system design.
    145150
    146151The virtual system supports multiple ``trees" of types. Each tree is
     
    149154number of children.
    150155Any type that belongs to any of these trees is called a virtual type.
    151 For 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}
    156 vtype V0, V1(V0), V2(V0);
    157 vtype W0, W1(W0), W2(W1);
    158 \end{cfa}
    159 &
    160 \raisebox{-0.6\totalheight}{\input{vtable}}
    161 \end{tabular}
    162 \lstMakeShortInline@
    163 \end{flushleft}
    164156% A type's ancestors are its parent and its parent's ancestors.
    165157% The root type has no ancestors.
    166158% A type's descendants are its children and its children's descendants.
    167 Every 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
    169 details). Children inherit their parent's list of virtual members but may add
    170 and/or replace members.  For example,
    171 \begin{cfa}
    172 vtable W0 | { int ?<?( int, int ); int ?+?( int, int ); }
    173 vtable W1 | { int ?+?( int, int ); int w, int ?-?( int, int ); }
    174 \end{cfa}
    175 creates a virtual table for @W0@ initialized with the matching @<@ and @+@
    176 operations visible at this declaration context.  Similarly, @W1@ is initialized
    177 with @<@ from inheritance with @W0@, @+@ is replaced, and @-@ is added, where
    178 both operations are matched at this declaration context. It is important to
    179 note that these are virtual members, not virtual methods of object-orientated
    180 programming, and can be of any type. Finally, trait names can be used to
    181 specify 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.
    186 Function pointers that take a pointer to the virtual type are modified
    187 with each level of inheritance so that refers to the new type.
    188 This means an object can always be passed to a function in its virtual table
    189 as if it were a method.
    190 \todo{Clarify (with an example) virtual methods.}
    191 }%
    192 
    193 Up until this point the virtual system is similar to ones found in
    194 object-orientated languages but this is where \CFA diverges. Objects encapsulate a
    195 single set of methods in each type, universally across the entire program,
    196 and indeed all programs that use that type definition. Even if a type inherits and adds methods, it still encapsulate a
    197 single set of methods. In this sense,
    198 object-oriented types are ``closed" and cannot be altered.
    199 
    200 In \CFA, types do not encapsulate any code. Traits are local for each function and
    201 types can satisfy a local trait, stop satisfying it or, satisfy the same
    202 trait in a different way at any lexical location in the program where a function is call.
    203 In 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.
     159
     160For the purposes of illustration, a proposed, but unimplemented, syntax
     161will be used. Each virtual type is represented by a trait with an annotation
     162that makes it a virtual type. This annotation is empty for a root type, which
     163creates a new tree:
     164\begin{cfa}
     165trait root_type(T) virtual() {}
     166\end{cfa}
     167The annotation may also refer to any existing virtual type to make this new
     168type a child of that type and part of the same tree. The parent may itself
     169be 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}
     174trait child_a(T) virtual(root_type) {}
     175trait grandchild(T) virtual(child_a) {}
     176trait 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
     185Every virtual type also has a list of virtual members and a unique id.
     186Both are stored in a virtual table.
     187Every instance of a virtual type also has a pointer to a virtual table stored
     188in it, although there is no per-type virtual table as in many other languages.
     189
     190The list of virtual members is accumulated from the root type down the tree.
     191Every virtual type
     192inherits the list of virtual members from its parent and may add more
     193virtual members to the end of the list which are passed on to its children.
     194Again, using the unimplemented syntax this might look like:
     195\begin{cfa}
     196trait root_type(T) virtual() {
     197        const char * to_string(T const & this);
     198        unsigned int size;
     199}
     200
     201trait 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
     207As @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
     211It is important to note that these are virtual members, and may contain   
     212arbitrary fields, functions or otherwise.
     213The names ``size" and ``align" are reserved for the size and alignment of the
     214virtual type, and are always automatically initialized as such.
     215The 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
     217virtual type. This allows functions that refer to the polymorphic argument
     218to act as traditional virtual methods (@to_string@ in the example), as the
     219object can always be passed to a virtual method in its virtual table.
     220
     221Up until this point, the virtual system is similar to ones found in
     222object-oriented languages, but this is where \CFA diverges.
     223Objects encapsulate a single set of methods in each type,
     224universally across the entire program,
     225and indeed all programs that use that type definition.
     226The only way to change any method is to inherit and define a new type with
     227its own universal implementation. In this sense,
     228these object-oriented types are ``closed" and cannot be altered.
     229% Because really they are class oriented.
     230
     231In \CFA, types do not encapsulate any code.
     232Whether or not a type satisfies any given assertion, and hence any trait, is
     233context sensitive. Types can begin to satisfy a trait, stop satisfying it or
     234satisfy the same trait at any lexical location in the program.
     235In this sense, a type's implementation in the set of functions and variables
     236that allow it to satisfy a trait is ``open" and can change
     237throughout the program.
    204238This capability means it is impossible to pick a single set of functions
    205239that represent a type's implementation across a program.
     
    208242type. A user can define virtual tables that are filled in at their
    209243declaration and given a name. Anywhere that name is visible, even if it is
    210 defined locally inside a function \PAB{What does this mean? (although that means it does not have a
    211 static lifetime)}, it can be used.
     244defined locally inside a function (although in this case the user must ensure
     245it outlives any objects that use it), it can be used.
    212246Specifically, a virtual type is ``bound" to a virtual table that
    213247sets the virtual members for that object. The virtual members can be accessed
    214248through the object.
    215249
    216 While much of the virtual infrastructure is created, it is currently only used
     250This means virtual tables are declared and named in \CFA.
     251They are declared as variables, using the type
     252@vtable(VIRTUAL_TYPE)@ and any valid name. For example:
     253\begin{cfa}
     254vtable(virtual_type_name) table_name;
     255\end{cfa}
     256
     257Like any variable, they may be forward declared with the @extern@ keyword.
     258Forward declaring virtual tables is relatively common.
     259Many virtual types have an ``obvious" implementation that works in most
     260cases.
     261A pattern that has appeared in the early work using virtuals is to
     262implement a virtual table with the the obvious definition and place a forward
     263declaration of it in the header beside the definition of the virtual type.
     264
     265Even on the full declaration, no initializer should be used.
     266Initialization is automatic.
     267The type id and special virtual members ``size" and ``align" only depend on
     268the virtual type, which is fixed given the type of the virtual table, and
     269so the compiler fills in a fixed value.
     270The other virtual members are resolved using the best match to the member's
     271name and type, in the same context as the virtual table is declared using
     272\CFA's normal resolution rules.
     273
     274While much of the virtual infrastructure has been created,
     275it is currently only used
    217276internally for exception handling. The only user-level feature is the virtual
    218277cast, which is the same as the \Cpp \code{C++}{dynamic_cast}.
     
    223282Note, the syntax and semantics matches a C-cast, rather than the function-like
    224283\Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be
    225 a pointer to a virtual type.
     284pointers to virtual types.
    226285The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type
    227286of @TYPE@, and if true, returns a pointer to the
    228287@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
    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 
    235 Exceptions are defined by the trait system; there are a series of traits, and
    236 if a type satisfies them, then it can be used as an exception. The following
     288This allows the expression to be used as both a cast and a type check.
     289
     290\section{Exceptions}
     291
     292The syntax for declaring an exception is the same as declaring a structure
     293except the keyword:
     294\begin{cfa}
     295exception TYPE_NAME {
     296        FIELDS
     297};
     298\end{cfa}
     299
     300Fields are filled in the same way as a structure as well. However, an extra
     301field is added that contains the pointer to the virtual table.
     302It must be explicitly initialized by the user when the exception is
     303constructed.
     304
     305Here is an example of declaring an exception type along with a virtual table,
     306assuming the exception has an ``obvious" implementation and a default
     307virtual table makes sense.
     308
     309\begin{minipage}[t]{0.4\textwidth}
     310Header (.hfa):
     311\begin{cfa}
     312exception Example {
     313        int data;
     314};
     315
     316extern vtable(Example)
     317        example_base_vtable;
     318\end{cfa}
     319\end{minipage}
     320\begin{minipage}[t]{0.6\textwidth}
     321Implementation (.cfa):
     322\begin{cfa}
     323vtable(Example) example_base_vtable
     324\end{cfa}
     325\vfil
     326\end{minipage}
     327
     328%\subsection{Exception Details}
     329This is the only interface needed when raising and handling exceptions.
     330However, it is actually a shorthand for a more complex
     331trait-based interface.
     332
     333The language views exceptions through a series of traits.
     334If a type satisfies them, then it can be used as an exception. The following
    237335is the base trait all exceptions need to match.
    238336\begin{cfa}
     
    241339};
    242340\end{cfa}
    243 The trait is defined over two types, the exception type and the virtual table
     341The trait is defined over two types: the exception type and the virtual table
    244342type. Each exception type should have a single virtual table type.
    245343There are no actual assertions in this trait because the trait system
     
    247345completing the virtual system). The imaginary assertions would probably come
    248346from a trait defined by the virtual system, and state that the exception type
    249 is a virtual type, is a descendant of @exception_t@ (the base exception type),
    250 and note its virtual table type.
     347is a virtual type,
     348that that the type is a descendant of @exception_t@ (the base exception type)
     349and allow the user to find the virtual table type.
    251350
    252351% I did have a note about how it is the programmer's responsibility to make
     
    266365};
    267366\end{cfa}
    268 Both traits ensure a pair of types are an exception type, its virtual table
    269 type,
     367Both traits ensure a pair of types is an exception type and
     368its virtual table type,
    270369and defines one of the two default handlers. The default handlers are used
    271 as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}.
     370as fallbacks and are discussed in detail in \autoref{s:ExceptionHandling}.
    272371
    273372However, all three of these traits can be tricky to use directly.
    274373While there is a bit of repetition required,
    275374the largest issue is that the virtual table type is mangled and not in a user
    276 facing way. So these three macros are provided to wrap these traits to
     375facing way. So, these three macros are provided to wrap these traits to
    277376simplify referring to the names:
    278 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@, and @IS_RESUMPTION_EXCEPTION@.
     377@IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.
    279378
    280379All three take one or two arguments. The first argument is the name of the
     
    282381The second (optional) argument is a parenthesized list of polymorphic
    283382arguments. This argument is only used with polymorphic exceptions and the
    284 list is be passed to both types.
     383list is passed to both types.
    285384In the current set-up, the two types always have the same polymorphic
    286 arguments so these macros can be used without losing flexibility.
    287 
    288 For example consider a function that is polymorphic over types that have a
     385arguments, so these macros can be used without losing flexibility.
     386
     387For example, consider a function that is polymorphic over types that have a
    289388defined arithmetic exception:
    290389\begin{cfa}
     
    303402Both operations follow the same set of steps.
    304403First, a user raises an exception.
    305 Second, the exception propagates up the stack.
     404Second, the exception propagates up the stack, searching for a handler.
    306405Third, if a handler is found, the exception is caught and the handler is run.
    307406After that control continues at a raise-dependent location.
    308 Fourth, if a handler is not found, a default handler is run and, if it returns, then control
     407As an alternate to the third step,
     408if a handler is not found, a default handler is run and, if it returns,
     409then control
    309410continues after the raise.
    310411
    311 %This general description covers what the two kinds have in common.
    312 The differences in the two operations include how propagation is performed, where execution continues
    313 after an exception is caught and handled, and which default handler is run.
     412The differences between the two operations include how propagation is
     413performed, where execution continues after an exception is handled
     414and which default handler is run.
    314415
    315416\subsection{Termination}
    316417\label{s:Termination}
    317 Termination handling is the familiar EHM and used in most programming
     418Termination handling is the familiar kind of handling
     419used in most programming
    318420languages with exception handling.
    319421It is a dynamic, non-local goto. If the raised exception is matched and
     
    347449Then propagation starts with the search. \CFA uses a ``first match" rule so
    348450matching is performed with the copied exception as the search key.
    349 It starts from the raise in the throwing function and proceeds towards the base of the stack,
     451It starts from the raise site and proceeds towards base of the stack,
    350452from callee to caller.
    351453At each stack frame, a check is made for termination handlers defined by the
     
    361463\end{cfa}
    362464When viewed on its own, a try statement simply executes the statements
    363 in the \snake{GUARDED_BLOCK}, and when those are finished,
     465in the \snake{GUARDED_BLOCK} and when those are finished,
    364466the try statement finishes.
    365467
     
    387489termination exception types.
    388490The global default termination handler performs a cancellation
    389 (see \vref{s:Cancellation} for the justification) on the current stack with the copied exception.
    390 Since it is so general, a more specific handler is usually
    391 defined, possibly with a detailed message, and used for specific exception type, effectively overriding the default handler.
     491(as described in \vref{s:Cancellation})
     492on the current stack with the copied exception.
     493Since it is so general, a more specific handler can be defined,
     494overriding the default behaviour for the specific exception types.
     495
     496For example, consider an error reading a configuration file.
     497This is most likely a problem with the configuration file (@config_error@),
     498but the function could have been passed the wrong file name (@arg_error@).
     499In this case the function could raise one exception and then, if it is
     500unhandled, raise the other.
     501This is not usual behaviour for either exception so changing the
     502default 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}
    392511
    393512\subsection{Resumption}
    394513\label{s:Resumption}
    395514
    396 Resumption exception handling is the less familar EHM, but is
     515Resumption exception handling is less familar form of exception handling,
     516but is
    397517just as old~\cite{Goodenough75} and is simpler in many ways.
    398518It is a dynamic, non-local function call. If the raised exception is
     
    403523function once the error is corrected, and
    404524ignorable events, such as logging where nothing needs to happen and control
    405 should always continue from the raise point.
     525should always continue from the raise site.
     526
     527Except for the changes to fit into that pattern, resumption exception
     528handling is symmetric with termination exception handling, by design
     529(see \autoref{s:Termination}).
    406530
    407531A resumption raise is started with the @throwResume@ statement:
     
    409533throwResume EXPRESSION;
    410534\end{cfa}
    411 \todo{Decide on a final set of keywords and use them everywhere.}
    412 It works much the same way as the termination throw.
    413 The expression must return a reference to a resumption exception,
    414 where the resumption exception is any type that satisfies the trait
    415 @is_resumption_exception@ at the call site.
    416 The assertions from this trait are available to
    417 the exception system while handling the exception.
    418 
    419 At run-time, no exception copy is made, since
     535% The new keywords are currently ``experimental" and not used in this work.
     536It works much the same way as the termination raise, except the
     537type must satisfy the \snake{is_resumption_exception} that uses the
     538default handler: \defaultResumptionHandler.
     539This can be specialized for particular exception types.
     540
     541At run-time, no exception copy is made. Since
    420542resumption does not unwind the stack nor otherwise remove values from the
    421 current scope, so there is no need to manage memory to keep the exception in scope.
    422 
    423 Then propagation starts with the search. It starts from the raise in the
    424 resuming function and proceeds towards the base of the stack,
    425 from callee to caller.
    426 At each stack frame, a check is made for resumption handlers defined by the
    427 @catchResume@ clauses of a @try@ statement.
     543current scope, there is no need to manage memory to keep the exception
     544allocated.
     545
     546Then propagation starts with the search,
     547following the same search path as termination,
     548from the raise site to the base of stack and top of try statement to bottom.
     549However, the handlers on try statements are defined by @catchResume@ clauses.
    428550\begin{cfa}
    429551try {
     
    435557}
    436558\end{cfa}
    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 
    467 The @GUARDED_BLOCK@ and its associated nested guarded statements work the same
    468 for resumption as for termination, as does exception matching at each
    469 @catchResume@. Similarly, if no resumption handler is found during the search,
    470 then the currently visible default handler (\defaultResumptionHandler) is
    471 called and control continues after the raise statement if it returns. Finally,
    472 there is also a global @defaultResumptionHandler@, which can be overridden,
    473 that is polymorphic over all resumption exceptions but performs a termination
    474 throw on the exception rather than a cancellation.
    475 
    476 Throwing the exception in @defaultResumptionHandler@ has the positive effect of
    477 walking the stack a second time for a recovery handler. Hence, a programmer has
    478 two chances for help with a problem, fixup or recovery, should either kind of
    479 handler appear on the stack. However, this dual stack walk leads to following
    480 apparent anomaly:
    481 \begin{cfa}
    482 try {
    483         throwResume E;
    484 } catch (E) {
    485         // this handler runs
    486 }
    487 \end{cfa}
    488 because the @catch@ appears to handle a @throwResume@, but a @throwResume@ only
    489 matches 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.
    493 Note, termination and resumption handlers may be used together
     559Note that termination handlers and resumption handlers may be used together
    494560in a single try statement, intermixing @catch@ and @catchResume@ freely.
    495561Each type of handler only interacts with exceptions from the matching
    496562kind of raise.
     563Like @catch@ clauses, @catchResume@ clauses have no effect if an exception
     564is not raised.
     565
     566The matching rules are exactly the same as well.
     567The 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.
     570After the block has finished running, control jumps to the raise site, where
     571the just handled exception came from, and continues executing after it,
     572not after the try statement.
     573
     574For instance, a resumption used to send messages to the logger may not
     575need to be handled at all. Putting the following default handler
     576at the global scope can make handling that exception optional by default.
     577\begin{cfa}
     578void defaultResumptionHandler(log_message &) {
     579    // Nothing, it is fine not to handle logging.
     580}
     581// ... No change at raise sites. ...
     582throwResume (log_message){strlit_log, "Begin event processing."}
     583\end{cfa}
    497584
    498585\subsubsection{Resumption Marking}
     
    501588not unwind the stack. A side effect is that, when a handler is matched
    502589and run, its try block (the guarded statements) and every try statement
    503 searched before it are still on the stack. There presence can lead to
    504 the \emph{recursive resumption problem}.
     590searched before it are still on the stack. Their presence can lead to
     591the recursive resumption problem.\cite{Buhr00a}
     592% Other possible citation is MacLaren77, but the form is different.
    505593
    506594The recursive resumption problem is any situation where a resumption handler
     
    516604When this code is executed, the guarded @throwResume@ starts a
    517605search and matches the handler in the @catchResume@ clause. This
    518 call is placed on the stack above the try-block. Now the second raise in the handler
    519 searches the same try block, matches, and puts another instance of the
     606call is placed on the stack above the try-block.
     607Now the second raise in the handler searches the same try block,
     608matches again and then puts another instance of the
    520609same handler on the stack leading to infinite recursion.
    521610
    522 While this situation is trivial and easy to avoid, much more complex cycles can
    523 form with multiple handlers and different exception types.  The key point is
    524 that the programmer's intuition expects every raise in a handler to start
    525 searching \emph{below} the @try@ statement, making it difficult to understand
    526 and fix the problem.
    527 
     611While this situation is trivial and easy to avoid, much more complex cycles
     612can form with multiple handlers and different exception types.
    528613To prevent all of these cases, each try statement is ``marked" from the
    529 time the exception search reaches it to either when a matching handler
    530 completes or when the search reaches the base
     614time the exception search reaches it to either when a handler completes
     615handling that exception or when the search reaches the base
    531616of the stack.
    532617While a try statement is marked, its handlers are never matched, effectively
     
    537622\end{center}
    538623
    539 There are other sets of marking rules that could be used,
    540 for instance, marking just the handlers that caught the exception,
     624There are other sets of marking rules that could be used.
     625For instance, marking just the handlers that caught the exception
    541626would also prevent recursive resumption.
    542 However, the rule selected mirrors what happens with termination,
    543 and hence, matches programmer intuition that a raise searches below a try.
    544 
    545 In detail, the marked try statements are the ones that would be removed from
     627However, the rules selected mirror what happens with termination,
     628so this reduces the amount of rules and patterns a programmer has to know.
     629
     630The marked try statements are the ones that would be removed from
    546631the stack for a termination exception, \ie those on the stack
    547632between the handler and the raise statement.
     
    580665// Handle a failure relating to f2 further down the stack.
    581666\end{cfa}
    582 In this example the file that experienced the IO error is used to decide
     667In this example, the file that experienced the IO error is used to decide
    583668which handler should be run, if any at all.
    584669
     
    609694
    610695\subsection{Comparison with Reraising}
    611 Without conditional catch, the only approach to match in more detail is to reraise
    612 the exception after it has been caught, if it could not be handled.
     696In languages without conditional catch -- that is, no ability to match an
     697exception based on something other than its type -- it can be mimicked
     698by matching all exceptions of the right type, checking any additional
     699conditions inside the handler and re-raising the exception if it does not
     700match those.
     701
     702Here is a minimal example comparing both patterns, using @throw;@
     703(no operand) to start a re-raise.
    613704\begin{center}
    614 \begin{tabular}{l|l}
     705\begin{tabular}{l r}
    615706\begin{cfa}
    616707try {
    617         do_work_may_throw();
    618 } catch(excep_t * ex; can_handle(ex)) {
    619 
    620         handle(ex);
    621 
    622 
    623 
    624 }
     708    do_work_may_throw();
     709} catch(exception_t * exc ;
     710                can_handle(exc)) {
     711    handle(exc);
     712}
     713
     714
     715
    625716\end{cfa}
    626717&
    627718\begin{cfa}
    628719try {
    629         do_work_may_throw();
    630 } catch(excep_t * ex) {
    631         if (can_handle(ex)) {
    632                 handle(ex);
     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}
     731At first glance, catch-and-reraise may appear to just be a quality-of-life
     732feature, but there are some significant differences between the two
     733strategies.
     734
     735A simple difference that is more important for \CFA than many other languages
     736is that the raise site changes with a re-raise, but does not with a
     737conditional catch.
     738This is important in \CFA because control returns to the raise site to run
     739the per-site default handler. Because of this, only a conditional catch can
     740allow the original raise to continue.
     741
     742The more complex issue comes from the difference in how conditional
     743catches and re-raises handle multiple handlers attached to a single try
     744statement. A conditional catch will continue checking later handlers while
     745a re-raise will skip them.
     746If the different handlers could handle some of the same exceptions,
     747translating a try statement that uses one to use the other can quickly
     748become non-trivial:
     749
     750\noindent
     751Original, 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}
     760Translated, 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);
    633769        } else {
    634770                throw;
     
    636772}
    637773\end{cfa}
    638 \end{tabular}
    639 \end{center}
    640 Notice catch-and-reraise increases complexity by adding additional data and
    641 code to the exception process. Nevertheless, catch-and-reraise can simulate
    642 conditional catch straightforwardly, when exceptions are disjoint, \ie no
    643 inheritance.
    644 
    645 However, catch-and-reraise simulation becomes unusable for exception inheritance.
    646 \begin{flushleft}
    647 \begin{cfa}[xleftmargin=6pt]
    648 exception E1;
    649 exception E2(E1); // inheritance
    650 \end{cfa}
    651 \begin{tabular}{l|l}
    652 \begin{cfa}
    653 try {
    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}
    667 try {
    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}
    681 The derived exception @E2@ must be ordered first in the catch list, otherwise
    682 the 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
    685 problem, an enclosing @try@ statement is need to catch @E2@ for @bar@ from the
    686 reraise, and its handler must duplicate the inner handler code for @bar@. To
    687 generalize, this fix for any amount of inheritance and complexity of try
    688 statement requires a technique called \emph{try-block
    689 splitting}~\cite{Krischer02}, which is not discussed in this thesis. It is
    690 sufficient to state that conditional catch is more expressive than
    691 catch-and-reraise in terms of complexity.
    692 
    693 \begin{comment}
    694 That is, they have the same behaviour in isolation.
    695 Two things can expose differences between these cases.
    696 
    697 One is the existence of multiple handlers on a single try statement.
    698 A reraise skips all later handlers for a try statement but a conditional
    699 catch does not.
    700 % Hence, if an earlier handler contains a reraise later handlers are
    701 % implicitly skipped, with a conditional catch they are not.
    702 Still, they are equivalently powerful,
    703 both can be used two mimic the behaviour of the other,
    704 as reraise can pack arbitrary code in the handler and conditional catches
    705 can 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 
    712 The question then becomes ``Which is a better default?"
    713 We believe that not skipping possibly useful handlers is a better default.
    714 If a handler can handle an exception it should and if the handler can not
    715 handle the exception then it is probably safer to have that explicitly
    716 described in the handler itself instead of implicitly described by its
    717 ordering 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 
    723 The other issue is all of the discussion above assumes that the only
    724 way to tell apart two raises is the exception being raised and the remaining
    725 search path.
    726 This is not true generally, the current state of the stack can matter in
    727 a number of cases, even only for a stack trace after an program abort.
    728 But \CFA has a much more significant need of the rest of the stack, the
    729 default 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.
    733 This becomes a problem combined with the stack unwinding used in termination
    734 exception handling.
    735 The stack is unwound before the handler is installed, and hence before any
    736 reraises can run. So if a reraise happens the previous stack is gone,
    737 the place on the stack where the default handler was supposed to run is gone,
    738 if the default handler was a local function it may have been unwound too.
    739 There is no reasonable way to restore that information, so the reraise has
    740 to be considered as a new raise.
    741 This is the strongest advantage conditional catches have over reraising,
    742 they 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.
     774(There is a simpler solution if @handle_a@ never raises exceptions,
     775using 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% }
    748782%
    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}
     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% }
     792In similar simple examples, translating from re-raise to conditional catch
     793takes less code but it does not have a general, trivial solution either.
     794
     795So, given that the two patterns do not trivially translate into each other,
     796it becomes a matter of which on should be encouraged and made the default.
     797From the premise that if a handler could handle an exception then it
     798should, it follows that checking as many handlers as possible is preferred.
     799So, conditional catch and checking later handlers is a good default.
    753800
    754801\section{Finally Clauses}
    755802\label{s:FinallyClauses}
    756 Finally clauses are used to preform unconditional clean-up when leaving a
     803Finally clauses are used to perform unconditional cleanup when leaving a
    757804scope and are placed at the end of a try statement after any handler clauses:
    758805\begin{cfa}
     
    766813The @FINALLY_BLOCK@ is executed when the try statement is removed from the
    767814stack, including when the @GUARDED_BLOCK@ finishes, any termination handler
    768 finishes, or during an unwind.
     815finishes or during an unwind.
    769816The only time the block is not executed is if the program is exited before
    770817the stack is unwound.
     
    772819Execution of the finally block should always finish, meaning control runs off
    773820the end of the block. This requirement ensures control always continues as if
    774 the finally clause is not present, \ie finally is for cleanup not changing
     821the finally clause is not present, \ie finally is for cleanup, not changing
    775822control flow.
    776823Because of this requirement, local control flow out of the finally block
    777824is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or
    778825@return@ that causes control to leave the finally block. Other ways to leave
    779 the finally block, such as a long jump or termination are much harder to check,
    780 and at best requiring additional run-time overhead, and so are only
     826the finally block, such as a @longjmp@ or termination are much harder to check,
     827and at best require additional run-time overhead, and so are only
    781828discouraged.
    782829
    783 Not all languages with unwinding have finally clauses. Notably \Cpp does
     830Not all languages with unwinding have finally clauses. Notably, \Cpp does
    784831without it as destructors, and the RAII design pattern, serve a similar role.
    785832Although destructors and finally clauses can be used for the same cases,
    786833they have their own strengths, similar to top-level function and lambda
    787834functions with closures.
    788 Destructors take more work for their creation, but if there is clean-up code
     835Destructors take more work to create, but if there is clean-up code
    789836that needs to be run every time a type is used, they are much easier
    790 to set-up.
    791 On the other hand finally clauses capture the local context, so is easy to
    792 use when the clean-up is not dependent on the type of a variable or requires
     837to set up for each use. % It's automatic.
     838On the other hand, finally clauses capture the local context, so are easy to
     839use when the cleanup is not dependent on the type of a variable or requires
    793840information from multiple variables.
    794841
     
    797844Cancellation is a stack-level abort, which can be thought of as as an
    798845uncatchable termination. It unwinds the entire current stack, and if
    799 possible forwards the cancellation exception to a different stack.
     846possible, forwards the cancellation exception to a different stack.
    800847
    801848Cancellation is not an exception operation like termination or resumption.
    802849There is no special statement for starting a cancellation; instead the standard
    803 library function @cancel_stack@ is called passing an exception. Unlike a
    804 raise, this exception is not used in matching only to pass information about
     850library function @cancel_stack@ is called, passing an exception. Unlike a
     851raise, this exception is not used in matching, only to pass information about
    805852the cause of the cancellation.
    806 Finaly, since a cancellation only unwinds and forwards, there is no default handler.
    807 
    808 After @cancel_stack@ is called the exception is copied into the EHM's memory
     853Finally, as no handler is provided, there is no default handler.
     854
     855After @cancel_stack@ is called, the exception is copied into the EHM's memory
    809856and the current stack is unwound.
    810857The behaviour after that depends on the kind of stack being cancelled.
    811858
    812859\paragraph{Main Stack}
    813 The main stack is the one used by the program main at the start of execution,
     860The main stack is the one used by
     861the program's main function at the start of execution,
    814862and is the only stack in a sequential program.
    815 After the main stack is unwound there is a program-level abort.
    816 
    817 The reasons for this semantics in a sequential program is that there is no more code to execute.
    818 This semantics also applies to concurrent programs, too, even if threads are running.
    819 That is, if any threads starts a cancellation, it implies all threads terminate.
    820 Keeping the same behaviour in sequential and concurrent programs is simple.
    821 Also, even in concurrent programs there may not currently be any other stacks
    822 and even if other stacks do exist, main has no way to know where they are.
     863After the main stack is unwound, there is a program-level abort.
     864
     865The first reason for this behaviour is for sequential programs where there
     866is only one stack, and hence no stack to pass information to.
     867Second, even in concurrent programs, the main stack has no dependency
     868on another stack and no reliable way to find another living stack.
     869Finally, keeping the same behaviour in both sequential and concurrent
     870programs is simple and easy to understand.
    823871
    824872\paragraph{Thread Stack}
     
    832880and an implicit join (from a destructor call). The explicit join takes the
    833881default handler (@defaultResumptionHandler@) from its calling context while
    834 the implicit join provides its own; which does a program abort if the
     882the implicit join provides its own, which does a program abort if the
    835883@ThreadCancelled@ exception cannot be handled.
    836884
     
    850898
    851899With explicit join and a default handler that triggers a cancellation, it is
    852 possible to cascade an error across any number of threads, cleaning up each
     900possible to cascade an error across any number of threads,
     901alternating between the resumption (possibly termination) and cancellation,
     902cleaning up each
    853903in turn, until the error is handled or the main thread is reached.
    854904
     
    858908After a coroutine stack is unwound, control returns to the @resume@ function
    859909that most recently resumed it. @resume@ reports a
    860 @CoroutineCancelled@ exception, which contains a references to the cancelled
     910@CoroutineCancelled@ exception, which contains a reference to the cancelled
    861911coroutine and the exception used to cancel it.
    862912The @resume@ function also takes the \defaultResumptionHandler{} from the
    863913caller's context and passes it to the internal report.
    864914
    865 A coroutine only knows of two other coroutines, its starter and its last resumer.
     915A coroutine only knows of two other coroutines,
     916its starter and its last resumer.
    866917The starter has a much more distant connection, while the last resumer just
    867918(in terms of coroutine state) called resume on this coroutine, so the message
     
    869920
    870921With a default handler that triggers a cancellation, it is possible to
    871 cascade an error across any number of coroutines, cleaning up each in turn,
     922cascade an error across any number of coroutines,
     923alternating between the resumption (possibly termination) and cancellation,
     924cleaning up each in turn,
    872925until 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
    875 talk about handling a cancellation in the last sentence. Which is correct?}
  • doc/theses/andrew_beach_MMath/future.tex

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

    rf95634e rb7fd9daf  
    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 one public feature, virtual
    17 cast (see the virtual cast feature \vpageref{p:VirtualCast}),
    18 substantial structure is required to support it,
     16While the \CFA virtual system currently has only two public features, virtual
     17cast and virtual tables,
     18substantial structure is required to support them,
    1919and provide features for exception handling and the standard library.
    2020
    2121\subsection{Virtual Type}
    22 Virtual types only have one change to their structure: the addition of a
    23 pointer to the virtual table, which is called the \emph{virtual-table pointer}.
    24 Internally, the field is called \snake{virtual_table}.
    25 The field is fixed after construction. It is always the first field in the
     22A virtual type~(see \autoref{s:virtuals}) has a pointer to a virtual table,
     23called the \emph{virtual-table pointer},
     24which binds each instance of a virtual type to a virtual table.
     25Internally, the field is called \snake{virtual_table}
     26and is fixed after construction.
     27This pointer is also the table's id and how the system accesses the
     28virtual table and the virtual members there.
     29It is always the first field in the
    2630structure so that its location is always known.
    27 \todo{Talk about constructors for virtual types (after they are working).}
    28 
    29 The virtual table pointer binds an instance of a virtual type
    30 to a virtual table.
    31 The pointer is also the table's id and how the system accesses the
    32 virtual table and the virtual members there.
    33 
    34 \subsection{Type Id}
    35 Every virtual type has a unique id.
    36 Type ids can be compared for equality,
    37 which checks if the types reperented are the same,
    38 or used to access the type's type information.
    39 The type information currently is only the parent's type id or, if the
     31
     32% We have no special rules for these constructors.
     33Virtual table pointers are passed to the constructors of virtual types
     34as part of field-by-field construction.
     35
     36\subsection{Type ID}
     37Every virtual type has a unique ID.
     38These are used in type equality, to check if the representation of two values
     39are the same, and to access the type's type information.
     40This uniqueness means across a program composed of multiple translation
     41units (TU), not uniqueness across all programs or even across multiple
     42processes on the same machine.
     43
     44Our approach for program uniqueness is using a static declaration for each
     45type ID, where the run-time storage address of that variable is guaranteed to
     46be unique during program execution.
     47The type ID storage can also be used for other purposes,
     48and is used for type information.
     49
     50The problem is that a type ID may appear in multiple TUs that compose a
     51program (see \autoref{ss:VirtualTable}), so the initial solution would seem
     52to be make it external in each translation unit. However, the type ID must
     53have a declaration in (exactly) one of the TUs to create the storage.
     54No other declaration related to the virtual type has this property, so doing
     55this through standard C declarations would require the user to do it manually.
     56
     57Instead, the linker is used to handle this problem.
     58% I did not base anything off of C++17; they are solving the same problem.
     59A new feature has been added to \CFA for this purpose, the special attribute
     60\snake{cfa_linkonce}, which uses the special section @.gnu.linkonce@.
     61When used as a prefix (\eg @.gnu.linkonce.example@), the linker does
     62not combine these sections, but instead discards all but one with the same
     63full name.
     64
     65So, each type ID must be given a unique section name with the \snake{linkonce}
     66prefix. Luckily, \CFA already has a way to get unique names, the name mangler.
     67For example, this could be written directly in \CFA:
     68\begin{cfa}
     69__attribute__((cfa_linkonce)) void f() {}
     70\end{cfa}
     71This is translated to:
     72\begin{cfa}
     73__attribute__((section(".gnu.linkonce._X1fFv___1"))) void _X1fFv___1() {}
     74\end{cfa}
     75This is done internally to access the name mangler.
     76This attribute is useful for other purposes, any other place a unique
     77instance required, and should eventually be made part of a public and
     78stable feature in \CFA.
     79
     80\subsection{Type Information}
     81
     82There is data stored at the type ID's declaration, the type information.
     83The type information currently is only the parent's type ID or, if the
    4084type has no parent, the null pointer.
    41 
    42 The id's are implemented as pointers to the type's type information instance.
    43 Dereferencing the pointer gets the type information.
    44 The ancestors of a virtual type are found by traversing type ids through
     85The ancestors of a virtual type are found by traversing type IDs through
    4586the type information.
    46 The information pushes the issue of creating a unique value (for
    47 the type id) to the problem of creating a unique instance (for type
    48 information), which the linker can solve.
    49 
    50 The advanced linker support is used here to avoid having to create
    51 a new declaration to attach this data to.
    52 With C/\CFA's header/implementation file divide for something to appear
    53 exactly once it must come from a declaration that appears in exactly one
    54 implementation file; the declarations in header files may exist only once
    55 they can be included in many different translation units.
    56 Therefore, structure's declaration will not work.
    57 Neither will attaching the type information to the virtual table -- although
    58 a vtable declarations are in implemention files they are not unique, see
    59 \autoref{ss:VirtualTable}.
    60 Instead the same type information is generated multiple times and then
    61 the new attribute \snake{cfa_linkone} is used to removed duplicates.
     87An example using helper macros looks like:
     88\begin{cfa}
     89struct INFO_TYPE(TYPE) {
     90        INFO_TYPE(PARENT) const * parent;
     91};
     92
     93__attribute__((cfa_linkonce))
     94INFO_TYPE(TYPE) const INFO_NAME(TYPE) = {
     95        &INFO_NAME(PARENT),
     96};
     97\end{cfa}
    6298
    6399Type information is constructed as follows:
    64 \begin{enumerate}
     100\begin{enumerate}[nosep]
    65101\item
    66 Use the type's name to generate a name for the type information structure.
    67 This is saved so it may be reused.
     102Use the type's name to generate a name for the type information structure,
     103which is saved so it can be reused.
    68104\item
    69105Generate a new structure definition to store the type
    70 information. The layout is the same in each case, just the parent's type id,
     106information. The layout is the same in each case, just the parent's type ID,
    71107but the types used change from instance to instance.
    72 The generated name is used for both this structure and, if relivant, the
     108The generated name is used for both this structure and, if relevant, the
    73109parent pointer.
    74110If the virtual type is polymorphic then the type information structure is
    75111polymorphic as well, with the same polymorphic arguments.
    76112\item
    77 A seperate name for instances is generated from the type's name.
     113A separate name for instances is generated from the type's name.
    78114\item
    79 The definition is generated and initialised.
    80 The parent id is set to the null pointer or to the address of the parent's
     115The definition is generated and initialized.
     116The parent ID is set to the null pointer or to the address of the parent's
    81117type information instance. Name resolution handles the rest.
    82118\item
    83119\CFA's name mangler does its regular name mangling encoding the type of
    84 the declaration into the instance name. This gives a completely unique name
     120the declaration into the instance name.
     121This process gives a completely unique name
    85122including different instances of the same polymorphic type.
    86123\end{enumerate}
    87 \todo{The list is making me realise, some of this isn't ordered.}
    88124
    89125Writing that code manually, with helper macros for the early name mangling,
     
    100136\end{cfa}
    101137
     138\begin{comment}
    102139\subsubsection{\lstinline{cfa\_linkonce} Attribute}
    103 % I just realised: This is an extension of the inline keyword.
     140% I just realized: This is an extension of the inline keyword.
    104141% An extension of C's at least, it is very similar to C++'s.
    105142Another feature added to \CFA is a new attribute: \texttt{cfa\_linkonce}.
     
    113150file as if it was a forward declaration, except no definition is required.
    114151
    115 This technique is used for type-id instances. A link-once definition is
     152This technique is used for type ID instances. A link-once definition is
    116153generated each time the structure is seen. This will result in multiple
    117154copies but the link-once attribute ensures all but one are removed for a
     
    126163everything that comes after the special prefix, then only one is used
    127164and the other is discarded.
     165\end{comment}
    128166
    129167\subsection{Virtual Table}
    130168\label{ss:VirtualTable}
    131 Each virtual type has a virtual table type that stores its type id and
     169Each virtual type has a virtual table type that stores its type ID and
    132170virtual members.
    133 Each virtual type instance is bound to a table instance that is filled with
    134 the values of virtual members.
    135 Both the layout of the fields and their value are decided by the rules given
     171An instance of a virtual type is bound to a virtual table instance,
     172which have the values of the virtual members.
     173Both the layout of the fields (in the virtual table type)
     174and their value (in the virtual table instance) are decided by the rules given
    136175below.
    137176
    138 The layout always comes in three parts.
    139 \todo{Add labels to the virtual table layout figure.}
    140 The first section is just the type id at the head of the table. It is always
     177The layout always comes in three parts (see \autoref{f:VirtualTableLayout}).
     178The first section is just the type ID at the head of the table. It is always
    141179there to ensure that it can be found even when the accessing code does not
    142180know which virtual type it has.
    143 The second section are all the virtual members of the parent, in the same
     181The second section is all the virtual members of the parent, in the same
    144182order as they appear in the parent's virtual table. Note that the type may
    145 change slightly as references to the ``this" will change. This is limited to
     183change slightly as references to the ``this" change. This is limited to
    146184inside pointers/references and via function pointers so that the size (and
    147185hence the offsets) are the same.
     
    150188
    151189\begin{figure}
     190\begin{center}
    152191\input{vtable-layout}
     192\end{center}
    153193\caption{Virtual Table Layout}
    154194\label{f:VirtualTableLayout}
    155 \todo*{Improve the Virtual Table Layout diagram.}
    156195\end{figure}
    157196
     
    160199This, combined with the fixed offset to the virtual table pointer, means that
    161200for any virtual type, it is always safe to access its virtual table and,
    162 from there, it is safe to check the type id to identify the exact type of the
     201from there, it is safe to check the type ID to identify the exact type of the
    163202underlying object, access any of the virtual members and pass the object to
    164203any of the method-like virtual members.
     
    168207the context of the declaration.
    169208
    170 The type id is always fixed; with each virtual table type having
    171 exactly one possible type id.
     209The type ID is always fixed, with each virtual table type having
     210exactly one possible type ID.
    172211The virtual members are usually filled in by type resolution.
    173212The best match for a given name and type at the declaration site is used.
    174213There are two exceptions to that rule: the @size@ field, the type's size,
    175 is set using a @sizeof@ expression and the @align@ field, the
     214is set using a @sizeof@ expression, and the @align@ field, the
    176215type's alignment, is set using an @alignof@ expression.
    177216
    178 \subsubsection{Concurrency Integration}
     217Most of these tools are already inside the compiler. Using simple
     218code transformations early on in compilation allows most of that work to be
     219handed off to the existing tools. \autoref{f:VirtualTableTransformation}
     220shows an example transformation; this example shows an exception virtual table.
     221It also shows the transformation on the full declaration.
     222For a forward declaration, the @extern@ keyword is preserved and the
     223initializer is not added.
     224
     225\begin{figure}[htb]
     226\begin{cfa}
     227vtable(example_type) example_name;
     228\end{cfa}
     229\transformline
     230% Check mangling.
     231\begin{cfa}
     232const 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}
    179245Coroutines and threads need instances of @CoroutineCancelled@ and
    180246@ThreadCancelled@ respectively to use all of their functionality. When a new
     
    183249at the definition of the main function.
    184250
    185 This is showned through code re-writing in
    186 \autoref{f:ConcurrencyTypeTransformation} and
    187 \autoref{f:ConcurrencyMainTransformation}.
    188 In both cases the original declaration is not modified,
     251These transformations are shown through code re-writing in
     252\autoref{f:CoroutineTypeTransformation} and
     253\autoref{f:CoroutineMainTransformation}.
     254Threads use the same pattern, with some names and types changed.
     255In both cases, the original declaration is not modified,
    189256only new ones are added.
    190257
    191 \begin{figure}
     258\begin{figure}[htb]
    192259\begin{cfa}
    193260coroutine Example {
     
    207274extern CoroutineCancelled_vtable & _default_vtable;
    208275\end{cfa}
    209 \caption{Concurrency Type Transformation}
    210 \label{f:ConcurrencyTypeTransformation}
     276\caption{Coroutine Type Transformation}
     277\label{f:CoroutineTypeTransformation}
    211278\end{figure}
    212279
    213 \begin{figure}
     280\begin{figure}[htb]
    214281\begin{cfa}
    215282void main(Example & this) {
     
    229296        &_default_vtable_object_declaration;
    230297\end{cfa}
    231 \caption{Concurrency Main Transformation}
    232 \label{f:ConcurrencyMainTransformation}
     298\caption{Coroutine Main Transformation}
     299\label{f:CoroutineMainTransformation}
    233300\end{figure}
    234301
     
    242309\begin{cfa}
    243310void * __cfa__virtual_cast(
    244         struct __cfavir_type_td parent,
    245         struct __cfavir_type_id const * child );
    246 \end{cfa}
    247 The type id of target type of the virtual cast is passed in as @parent@ and
     311        struct __cfavir_type_id * parent,
     312        struct __cfavir_type_id * const * child );
     313\end{cfa}
     314The type ID for the target type of the virtual cast is passed in as
     315@parent@ and
    248316the cast target is passed in as @child@.
    249 
    250 For generated C code wraps both arguments and the result with type casts.
     317The generated C code wraps both arguments and the result with type casts.
    251318There is also an internal check inside the compiler to make sure that the
    252319target type is a virtual type.
     
    255322The virtual cast either returns the original pointer or the null pointer
    256323as the new type.
    257 So the function does the parent check and returns the appropriate value.
    258 The parent check is a simple linear search of child's ancestors using the
     324The function does the parent check and returns the appropriate value.
     325The parent check is a simple linear search of the child's ancestors using the
    259326type information.
    260327
    261328\section{Exceptions}
    262 % Anything about exception construction.
     329% The implementation of exception types.
     330
     331Creating exceptions can be roughly divided into two parts:
     332the exceptions themselves and the virtual system interactions.
     333
     334Creating an exception type is just a matter of prepending the field 
     335with the virtual table pointer to the list of the fields
     336(see \autoref{f:ExceptionTypeTransformation}).
     337
     338\begin{figure}[htb]
     339\begin{cfa}
     340exception new_exception {
     341        // EXISTING FIELDS
     342};
     343\end{cfa}
     344\transformline
     345\begin{cfa}
     346struct 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
     355The integration between exceptions and the virtual system is a bit more
     356complex simply because of the nature of the virtual system prototype.
     357The primary issue is that the virtual system has no way to detect when it
     358should generate any of its internal types and data. This is handled by
     359the exception code, which tells the virtual system when to generate
     360its components.
     361
     362All types associated with a virtual type,
     363the types of the virtual table and the type ID,
     364are generated when the virtual type (the exception) is first found.
     365The type ID (the instance) is generated with the exception, if it is
     366a monomorphic type.
     367However, if the exception is polymorphic, then a different type ID has to
     368be generated for every instance. In this case, generation is delayed
     369until a virtual table is created.
     370% There are actually some problems with this, which is why it is not used
     371% for monomorphic types.
     372When a virtual table is created and initialized, two functions are created
     373to fill in the list of virtual members.
     374The first is the @copy@ function that adapts the exception's copy constructor
     375to work with pointers, avoiding some issues with the current copy constructor
     376interface.
     377Second is the @msg@ function that returns a C-string with the type's name,
     378including any polymorphic parameters.
    263379
    264380\section{Unwinding}
     
    274390stack. On function entry and return, unwinding is handled directly by the
    275391call/return code embedded in the function.
    276 In many cases, the position of the instruction pointer (relative to parameter
    277 and local declarations) is enough to know the current size of the stack
    278 frame.
    279 
     392
     393% Discussing normal stack unwinding:
    280394Usually, the stack-frame size is known statically based on parameter and
    281 local variable declarations. Even with dynamic stack-size, the information
     395local variable declarations. Even for a dynamic stack-size, the information
    282396to determine how much of the stack has to be removed is still contained
    283397within the function.
     
    285399bumping the hardware stack-pointer up or down as needed.
    286400Constructing/destructing values within a stack frame has
    287 a similar complexity but can add additional work and take longer.
    288 
    289 Unwinding across multiple stack frames is more complex because that
     401a similar complexity but larger constants.
     402
     403% Discussing multiple frame stack unwinding:
     404Unwinding across multiple stack frames is more complex, because that
    290405information is no longer contained within the current function.
    291 With seperate compilation a function has no way of knowing what its callers
    292 are so it can't know how large those frames are.
    293 Without altering the main code path it is also hard to pass that work off
    294 to the caller.
    295 
    296 The traditional unwinding mechanism for C is implemented by saving a snap-shot
    297 of a function's state with @setjmp@ and restoring that snap-shot with
     406With separate compilation,
     407a function does not know its callers nor their frame layout.
     408Even using the return address, that information is encoded in terms of
     409actions in code, intermixed with the actions required to finish the function.
     410Without changing the main code path it is impossible to select one of those
     411two groups of actions at the return site.
     412
     413The traditional unwinding mechanism for C is implemented by saving a snapshot
     414of a function's state with @setjmp@ and restoring that snapshot with
    298415@longjmp@. This approach bypasses the need to know stack details by simply
    299 reseting to a snap-shot of an arbitrary but existing function frame on the
    300 stack. It is up to the programmer to ensure the snap-shot is valid when it is
    301 reset and that all required clean-up from the unwound stacks is performed.
    302 This approach is fragile and requires extra work in the surrounding code.
    303 
    304 With respect to the extra work in the surounding code,
    305 many languages define clean-up actions that must be taken when certain
    306 sections of the stack are removed. Such as when the storage for a variable
    307 is removed from the stack or when a try statement with a finally clause is
     416resetting to a snapshot of an arbitrary but existing function frame on the
     417stack. It is up to the programmer to ensure the snapshot is valid when it is
     418reset and that all required cleanup from the unwound stacks is performed.
     419Because it does not automate or check any of this cleanup,
     420it can be easy to make mistakes and always must be handled manually.
     421
     422With respect to the extra work in the surrounding code,
     423many languages define cleanup actions that must be taken when certain
     424sections of the stack are removed, such as when the storage for a variable
     425is removed from the stack, possibly requiring a destructor call,
     426or when a try statement with a finally clause is
    308427(conceptually) popped from the stack.
    309 None of these should be handled by the user --- that would contradict the
    310 intention of these features --- so they need to be handled automatically.
     428None of these cases should be handled by the user -- that would contradict the
     429intention of these features -- so they need to be handled automatically.
    311430
    312431To safely remove sections of the stack, the language must be able to find and
    313 run these clean-up actions even when removing multiple functions unknown at
     432run these cleanup actions even when removing multiple functions unknown at
    314433the beginning of the unwinding.
    315434
     
    317436library that provides tools for stack walking, handler execution, and
    318437unwinding. What follows is an overview of all the relevant features of
    319 libunwind needed for this work, and how \CFA uses them to implement exception
    320 handling.
     438libunwind needed for this work.
     439Following that is the description of the \CFA code that uses libunwind
     440to implement termination.
    321441
    322442\subsection{libunwind Usage}
     
    348468In plain C (which \CFA currently compiles down to) this
    349469flag only handles the cleanup attribute:
     470%\label{code:cleanup}
    350471\begin{cfa}
    351472void clean_up( int * var ) { ... }
     
    355476in this case @clean_up@, run when the variable goes out of scope.
    356477This feature is enough to mimic destructors,
    357 but not try statements which can effect
     478but not try statements that affect
    358479the unwinding.
    359480
    360481To get full unwinding support, all of these features must be handled directly
    361 in assembly and assembler directives; partiularly the cfi directives
     482in assembly and assembler directives; particularly the cfi directives
    362483\snake{.cfi_lsda} and \snake{.cfi_personality}.
    363484
     
    399520@_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs
    400521the cleanup phase and uses a different means to decide when to stop
    401 (see \vref{s:ForcedUnwind}).
     522(see \autoref{s:ForcedUnwind}).
    402523\end{enumerate}
    403524
    404525The @exception_class@ argument is a copy of the
    405526\code{C}{exception}'s @exception_class@ field,
    406 which is a number that identifies the exception handling mechanism
     527which is a number that identifies the EHM
    407528that created the exception.
    408529
     
    410531provided storage object. It has two public fields: the @exception_class@,
    411532which is described above, and the @exception_cleanup@ function.
    412 The clean-up function is used by the EHM to clean-up the exception, if it
     533The cleanup function is used by the EHM to clean up the exception. If it
    413534should need to be freed at an unusual time, it takes an argument that says
    414535why it had to be cleaned up.
     
    432553of the most recent stack frame. It continues to call personality functions
    433554traversing the stack from newest to oldest until a function finds a handler or
    434 the end of the stack is reached. In the latter case, raise exception returns
    435 @_URC_END_OF_STACK@.
    436 
    437 Second, when a handler is matched, raise exception moves to the clean-up
    438 phase and walks the stack a second time.
     555the end of the stack is reached. In the latter case,
     556@_Unwind_RaiseException@ returns @_URC_END_OF_STACK@.
     557
     558Second, when a handler is matched, @_Unwind_RaiseException@
     559moves to the cleanup phase and walks the stack a second time.
    439560Once again, it calls the personality functions of each stack frame from newest
    440561to oldest. This pass stops at the stack frame containing the matching handler.
    441 If that personality function has not install a handler, it is an error.
    442 
    443 If an error is encountered, raise exception returns either
     562If that personality function has not installed a handler, it is an error.
     563
     564If an error is encountered, @_Unwind_RaiseException@ returns either
    444565@_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the
    445566error occurred.
     
    452573        _Unwind_Stop_Fn, void *);
    453574\end{cfa}
    454 It also unwinds the stack but it does not use the search phase. Instead another
     575It also unwinds the stack but it does not use the search phase. Instead,
     576another
    455577function, the stop function, is used to stop searching. The exception is the
    456 same as the one passed to raise exception. The extra arguments are the stop
     578same as the one passed to @_Unwind_RaiseException@.
     579The extra arguments are the stop
    457580function and the stop parameter. The stop function has a similar interface as a
    458581personality function, except it is also passed the stop parameter.
     
    494617needs its own exception context.
    495618
    496 The exception context should be retrieved by calling the function
     619The current exception context should be retrieved by calling the function
    497620\snake{this_exception_context}.
    498621For sequential execution, this function is defined as
     
    519642The first step of a termination raise is to copy the exception into memory
    520643managed by the exception system. Currently, the system uses @malloc@, rather
    521 than reserved memory or the stack top. The exception handling mechanism manages
     644than reserved memory or the stack top. The EHM manages
    522645memory for the exception as well as memory for libunwind and the system's own
    523646per-exception storage.
     
    554677\newsavebox{\stackBox}
    555678\begin{lrbox}{\codeBox}
    556 \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}]
     679\begin{cfa}
    557680unsigned num_exceptions = 0;
    558681void throws() {
     
    573696    throws();
    574697}
    575 \end{lstlisting}
     698\end{cfa}
    576699\end{lrbox}
    577700
    578701\begin{lrbox}{\stackBox}
    579702\begin{lstlisting}
    580 | try-finally
    581 | try-catch (Example)
     703| finally block (Example)
     704| try block
    582705throws()
    583 | try-finally
    584 | try-catch (Example)
     706| finally block (Example)
     707| try block
    585708throws()
    586 | try-finally
    587 | try-catch (Example)
     709| finally block (Example)
     710| try block
    588711throws()
    589712main()
     
    598721\label{f:MultipleExceptions}
    599722\end{figure}
    600 \todo*{Work on multiple exceptions code sample.}
    601723
    602724All exceptions are stored in nodes, which are then linked together in lists
    603725one list per stack, with the
    604726list head stored in the exception context. Within each linked list, the most
    605 recently thrown exception is at the head followed by older thrown
     727recently thrown exception is at the head, followed by older thrown
    606728exceptions. This format allows exceptions to be thrown, while a different
    607729exception is being handled. The exception at the head of the list is currently
     
    614736exception into managed memory. After the exception is handled, the free
    615737function is used to clean up the exception and then the entire node is
    616 passed to free, returning the memory back to the heap.
     738passed to @free@, returning the memory back to the heap.
    617739
    618740\subsection{Try Statements and Catch Clauses}
    619741The try statement with termination handlers is complex because it must
    620 compensate for the C code-generation versus
     742compensate for the C code-generation versus proper
    621743assembly-code generated from \CFA. Libunwind
    622744requires an LSDA and personality function for control to unwind across a
    623745function. The LSDA in particular is hard to mimic in generated C code.
    624746
    625 The workaround is a function called @__cfaehm_try_terminate@ in the standard
    626 library. The contents of a try block and the termination handlers are converted
    627 into functions. These are then passed to the try terminate function and it
    628 calls them.
     747The workaround is a function called \snake{__cfaehm_try_terminate} in the
     748standard \CFA library. The contents of a try block and the termination
     749handlers are converted into nested functions. These are then passed to the
     750try terminate function and it calls them, appropriately.
    629751Because this function is known and fixed (and not an arbitrary function that
    630 happens to contain a try statement), the LSDA can be generated ahead
     752happens to contain a try statement), its LSDA can be generated ahead
    631753of time.
    632754
    633 Both the LSDA and the personality function are set ahead of time using
     755Both the LSDA and the personality function for \snake{__cfaehm_try_terminate}
     756are set ahead of time using
    634757embedded assembly. This assembly code is handcrafted using C @asm@ statements
    635758and contains
    636 enough information for a single try statement the function repersents.
     759enough information for the single try statement the function represents.
    637760
    638761The three functions passed to try terminate are:
    639762\begin{description}
    640 \item[try function:] This function is the try block, it is where all the code
     763\item[try function:] This function is the try block. It is where all the code
    641764from inside the try block is placed. It takes no parameters and has no
    642765return value. This function is called during regular execution to run the try
     
    646769decides if a catch clause matches the termination exception. It is constructed
    647770from the conditional part of each handler and runs each check, top to bottom,
    648 in turn, first checking to see if the exception type matches and then if the
    649 condition is true. It takes a pointer to the exception and returns 0 if the
    650 exception is not handled here. Otherwise the return value is the id of the
     771in turn, to see if the exception matches this handler.
     772The match is performed in two steps: first, a virtual cast is used to check
     773if the raised exception is an instance of the declared exception type or
     774one of its descendant types, and then the condition is evaluated, if
     775present.
     776The match function takes a pointer to the exception and returns 0 if the
     777exception is not handled here. Otherwise, the return value is the ID of the
    651778handler that matches the exception.
    652779
     
    659786\end{description}
    660787All three functions are created with GCC nested functions. GCC nested functions
    661 can be used to create closures,
    662 in other words functions that can refer to the state of other
    663 functions on the stack. This approach allows the functions to refer to all the
     788can be used to create closures;
     789in other words,
     790functions that can refer to variables in their lexical scope even though
     791those variables are part of a different function.
     792This approach allows the functions to refer to all the
    664793variables in scope for the function containing the @try@ statement. These
    665794nested functions and all other functions besides @__cfaehm_try_terminate@ in
     
    669798
    670799\autoref{f:TerminationTransformation} shows the pattern used to transform
    671 a \CFA try statement with catch clauses into the approprate C functions.
    672 \todo{Explain the Termination Transformation figure.}
     800a \CFA try statement with catch clauses into the appropriate C functions.
    673801
    674802\begin{figure}
     
    728856\caption{Termination Transformation}
    729857\label{f:TerminationTransformation}
    730 \todo*{Improve (compress?) Termination Transformations.}
    731858\end{figure}
    732859
     
    738865Instead of storing the data in a special area using assembly,
    739866there is just a linked list of possible handlers for each stack,
    740 with each node on the list reperenting a try statement on the stack.
     867with each node on the list representing a try statement on the stack.
    741868
    742869The head of the list is stored in the exception context.
     
    744871to the head of the list.
    745872Instead of traversing the stack, resumption handling traverses the list.
    746 At each node, the EHM checks to see if the try statement the node repersents
     873At each node, the EHM checks to see if the try statement the node represents
    747874can handle the exception. If it can, then the exception is handled and
    748 the operation finishes, otherwise the search continues to the next node.
     875the operation finishes; otherwise, the search continues to the next node.
    749876If the search reaches the end of the list without finding a try statement
    750 that can handle the exception, the default handler is executed and the
    751 operation finishes.
     877with a handler clause
     878that can handle the exception, the default handler is executed.
     879If the default handler returns, control continues after the raise statement.
    752880
    753881Each node has a handler function that does most of the work.
    754882The handler function is passed the raised exception and returns true
    755883if the exception is handled and false otherwise.
    756 
    757884The handler function checks each of its internal handlers in order,
    758 top-to-bottom, until it funds a match. If a match is found that handler is
     885top-to-bottom, until it finds a match. If a match is found that handler is
    759886run, after which the function returns true, ignoring all remaining handlers.
    760887If no match is found the function returns false.
    761 The match is performed in two steps, first a virtual cast is used to see
    762 if the thrown exception is an instance of the declared exception or one of
    763 its descendant type, then check to see if passes the custom predicate if one
    764 is defined. This ordering gives the type guarantee used in the predicate.
     888The match is performed in two steps. First a virtual cast is used to see
     889if the raised exception is an instance of the declared exception type or one
     890of its descendant types, if so, then the second step is to see if the
     891exception passes the custom predicate
     892if 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.
    765895
    766896\autoref{f:ResumptionTransformation} shows the pattern used to transform
    767 a \CFA try statement with catch clauses into the approprate C functions.
    768 \todo{Explain the Resumption Transformation figure.}
     897a \CFA try statement with catchResume clauses into the appropriate
     898C functions.
    769899
    770900\begin{figure}
     
    807937\caption{Resumption Transformation}
    808938\label{f:ResumptionTransformation}
    809 \todo*{Improve (compress?) Resumption Transformations.}
    810939\end{figure}
    811940
    812941% Recursive Resumption Stuff:
    813942\autoref{f:ResumptionMarking} shows search skipping
    814 (see \vpageref{s:ResumptionMarking}), which ignores parts of
     943(see \autoref{s:ResumptionMarking}), which ignores parts of
    815944the stack
    816 already examined, is accomplished by updating the front of the list as the
    817 search continues. Before the handler at a node is called, the head of the list
     945already examined, and is accomplished by updating the front of the list as
     946the search continues.
     947Before the handler is called at a matching node, the head of the list
    818948is updated to the next node of the current node. After the search is complete,
    819949successful or not, the head of the list is reset.
     
    822952been checked are not on the list while a handler is run. If a resumption is
    823953thrown during the handling of another resumption, the active handlers and all
    824 the other handler checked up to this point are not checked again.
     954the other handlers checked up to this point are not checked again.
    825955% No paragraph?
    826956This structure also supports new handlers added while the resumption is being
    827957handled. These are added to the front of the list, pointing back along the
    828 stack --- the first one points over all the checked handlers ---
     958stack -- the first one points over all the checked handlers --
    829959and the ordering is maintained.
    830960
    831961\begin{figure}
     962\centering
    832963\input{resumption-marking}
    833964\caption{Resumption Marking}
    834965\label{f:ResumptionMarking}
    835 \todo*{Label Resumption Marking to aid clarity.}
    836966\end{figure}
    837967
     
    851981\section{Finally}
    852982% Uses destructors and GCC nested functions.
    853 A finally clause is placed into a GCC nested-function with a unique name,
    854 and no arguments or return values.
    855 This nested function is then set as the cleanup
    856 function of an empty object that is declared at the beginning of a block placed
    857 around the context of the associated @try@ statement.
    858 
    859 The rest is handled by GCC. The try block and all handlers are inside this
    860 block. At completion, control exits the block and the empty object is cleaned
     983
     984%\autoref{code:cleanup}
     985A finally clause is handled by converting it into a once-off destructor.
     986The code inside the clause is placed into a GCC nested-function
     987with a unique name, and no arguments or return values.
     988This nested function is
     989then set as the cleanup function of an empty object that is declared at the
     990beginning of a block placed around the context of the associated try
     991statement, as shown in \autoref{f:FinallyTransformation}.
     992
     993\begin{figure}
     994\begin{cfa}
     995try {
     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
     1021The rest is handled by GCC.
     1022The TRY BLOCK
     1023contains the try block itself as well as all code generated for handlers.
     1024Once that code has completed,
     1025control exits the block and the empty object is cleaned
    8611026up, which runs the function that contains the finally code.
    8621027
     
    8641029% Stack selections, the three internal unwind functions.
    8651030
    866 Cancellation also uses libunwind to do its stack traversal and unwinding,
    867 however it uses a different primary function: @_Unwind_ForcedUnwind@. Details
    868 of its interface can be found in the Section~\vref{s:ForcedUnwind}.
     1031Cancellation also uses libunwind to do its stack traversal and unwinding.
     1032However, it uses a different primary function: @_Unwind_ForcedUnwind@. Details
     1033of its interface can be found in Section~\vref{s:ForcedUnwind}.
    8691034
    8701035The first step of cancellation is to find the cancelled stack and its type:
     
    8871052passed to the forced-unwind function. The general pattern of all three stop
    8881053functions is the same: continue unwinding until the end of stack and
    889 then preform the appropriate transfer.
     1054then perform the appropriate transfer.
    8901055
    8911056For main stack cancellation, the transfer is just a program abort.
  • doc/theses/andrew_beach_MMath/intro.tex

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

    rf95634e rb7fd9daf  
    33
    44Performance is of secondary importance for most of this project.
    5 Instead, the focus is to get the features working. The only performance
     5Instead, the focus was to get the features working. The only performance
    66requirement is to ensure the tests for correctness run in a reasonable
    7 amount of time.
     7amount of time. Hence, only a few basic performance tests were performed to
     8check this requirement.
    89
    910\section{Test Set-Up}
    1011Tests were run in \CFA, C++, Java and Python.
    1112In addition there are two sets of tests for \CFA,
    12 one for termination and one for resumption exceptions.
    13 
    14 C++ is the most comparable language because both it and \CFA use the same
     13one with termination and one with resumption.
     14
     15GCC C++ is the most comparable language because both it and \CFA use the same
    1516framework, libunwind.
    16 In fact, the comparison is almost entirely a quality of implementation
    17 comparison: \CFA's EHM has had significantly less time to be optimized and
     17In fact, the comparison is almost entirely in quality of implementation.
     18Specifically, \CFA's EHM has had significantly less time to be optimized and
    1819does not generate its own assembly. It does have a slight advantage in that
    19 there are some features it handles directly instead of through utility functions,
    20 but otherwise \Cpp has a significant advantage.
    21 
    22 Java is another very popular language with similar termination semantics.
    23 It is implemented in a very different environment, a virtual machine with
     20\Cpp has to do some extra bookkeeping to support its utility functions,
     21but otherwise \Cpp should have a significant advantage.
     22
     23Java, a popular language with similar termination semantics,
     24is implemented in a very different environment, a virtual machine with
    2425garbage collection.
    25 It also implements the @finally@ clause on @try@ blocks allowing for a direct
     26It also implements the finally clause on try blocks allowing for a direct
    2627feature-to-feature comparison.
    27 As with \Cpp, Java's implementation is mature, optimizations
    28 and has extra features.
    29 
    30 Python is used as an alternative point of comparison because of the \CFA EHM's
    31 current performance goals, which is not to be prohibitively slow while the
     28As with \Cpp, Java's implementation is mature, has more optimizations
     29and extra features as compared to \CFA.
     30
     31Python is used as an alternative comparison because of the \CFA EHM's
     32current performance goals, which is to not be prohibitively slow while the
    3233features are designed and examined. Python has similar performance goals for
    3334creating quick scripts and its wide use suggests it has achieved those goals.
     
    3637resumption exceptions. Even the older programming languages with resumption
    3738seem to be notable only for having resumption.
    38 So instead, resumption is compared to a less similar but much more familiar
    39 feature, termination exceptions.
     39On the other hand, the functional equivalents to resumption are too new.
     40There does not seem to be any standard implementations in well-known
     41languages; 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.
     44Instead, resumption is compared to its simulation in other programming
     45languages: fixup functions that are explicitly passed into a function.
    4046
    4147All tests are run inside a main loop that repeatedly performs a test.
    4248This approach avoids start-up or tear-down time from
    4349affecting the timing results.
    44 Each test is run a million times.
    45 The Java versions of the test run this loop an extra 1000 times before
    46 beginning to actual test to ``warm-up" the JVM.
     50The number of times the loop is run is configurable from the command line;
     51the number used in the timing runs is given with the results per test.
     52The Java tests run the main loop 1000 times before
     53beginning the actual test to ``warm up" the JVM.
     54% All other languages are precompiled or interpreted.
    4755
    4856Timing is done internally, with time measured immediately before and
     
    5159unhandled exceptions in \Cpp and Java as that would cause the process to
    5260terminate.
    53 Luckily, performance on the ``give-up and kill the process" path is not
     61Luckily, performance on the ``give up and kill the process" path is not
    5462critical.
    5563
    5664The exceptions used in these tests are always based off of
    57 a base exception. This requirement minimizes performance differences based
     65the base exception for the language.
     66This requirement minimizes performance differences based
    5867on the object model used to represent the exception.
    5968
     
    6271For example, empty inline assembly blocks are used in \CFA and \Cpp to
    6372prevent excessive optimizations while adding no actual work.
    64 Each test was run eleven times. The top three and bottom three results were
    65 discarded and the remaining five values are averaged.
    66 
    67 The tests are compiled with gcc-10 for \CFA and g++-10 for \Cpp. Java is
    68 compiled with 11.0.11. Python with 3.8. The tests were run on:
    69 \begin{itemize}[nosep]
    70 \item
    71 ARM 2280 Kunpeng 920 48-core 2$\times$socket \lstinline{@} 2.6 GHz running Linux v5.11.0-25
    72 \item
    73 AMD 6380 Abu Dhabi 16-core 4$\times$socket \lstinline{@} 2.5 GHz running Linux v5.11.0-25
    74 \end{itemize}
    7573
    7674% We don't use catch-alls but if we did:
    7775% Catch-alls are done by catching the root exception type (not using \Cpp's
    7876% \code{C++}{catch(...)}).
     77
     78When collecting data, each test is run eleven times. The top three and bottom
     79three results are discarded and the remaining five values are averaged.
     80The test are run with the latest (still pre-release) \CFA compiler,
     81using gcc-10 10.3.0 as a backend.
     82g++-10 10.3.0 is used for \Cpp.
     83Java tests are complied and run with Oracle OpenJDK version 11.0.11.
     84Python used CPython version 3.8.10.
     85The 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}
     92These represent the two major families of hardware architecture.
    7993
    8094\section{Tests}
     
    8397They should provide a guide as to where the EHM's costs are found.
    8498
    85 \paragraph{Raise and Handle}
    86 The first group measures the cost of a try statement when exceptions are raised
    87 and \emph{the stack is unwound}.  Each test has has a repeating function like
    88 the following
     99\paragraph{Stack Traversal}
     100This group of tests measures the cost of traversing the stack
     101(and in termination, unwinding it).
     102Inside the main loop is a call to a recursive function.
     103This function calls itself F times before raising an exception.
     104F is configurable from the command line, but is usually 100.
     105This builds up many stack frames, and any contents they may have,
     106before the raise.
     107The exception is always handled at the base of the stack.
     108For example the Empty test for \CFA resumption looks like:
    89109\begin{cfa}
    90110void unwind_empty(unsigned int frames) {
    91111        if (frames) {
    92112                unwind_empty(frames - 1);
    93         } else throw (empty_exception){&empty_vt};
     113        } else {
     114                throwResume (empty_exception){&empty_vt};
     115        }
    94116}
    95117\end{cfa}
    96 which is called M times, where each call recurses to a depth of N, an
    97 exception is raised, the stack is a unwound, and the exception caught.
     118Other test cases have additional code around the recursive call adding
     119something besides simple stack frames to the stack.
     120Note that both termination and resumption have to traverse over
     121the stack but only termination has to unwind it.
    98122\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.
    99127\item Empty:
    100 This test measures the cost of raising (stack walking) an exception through empty
    101 empty stack frames to an empty handler. (see above)
     128The repeating function is empty except for the necessary control code.
     129As other traversal tests add to this, it is the baseline for the group
     130as the cost comes from traversing over and unwinding a stack frame
     131that has no other interactions with the exception system.
    102132\item Destructor:
    103 
    104 This test measures the cost of raising an exception through non-empty frames
    105 where each frame has an object requiring destruction, to an empty
    106 handler. Hence, there are N destructor calls during unwinding.
    107 \begin{cfa}
    108 if (frames) {
    109         WithDestructor object;
    110         unwind_empty(frames - 1);
    111 \end{cfa}
     133The repeating function creates an object with a destructor before calling
     134itself.
     135Comparing this to the empty test gives the time to traverse over and
     136unwind a destructor.
    112137\item Finally:
    113 This test measures the cost of establishing a try block with an empty finally
    114 clause on the front side of the recursion and running the empty finally clause
    115 on the back side of the recursion during stack unwinding.
    116 \begin{cfa}
    117 if (frames) {
    118         try {
    119                 unwind_finally(frames - 1);
    120         } finally {}
    121 \end{cfa}
     138The repeating function calls itself inside a try block with a finally clause
     139attached.
     140Comparing this to the empty test gives the time to traverse over and
     141unwind a finally clause.
    122142\item Other Handler:
    123 This test is like the finally test but the try block has a catch clause for an
    124 exception that is not raised, so catch matching is executed during stack
    125 unwinding but the match never successes until the catch at the bottom of the
    126 stack.
    127 \begin{cfa}
    128 if (frames) {
    129         try {
    130                 unwind_other(frames - 1);
    131         } catch (not_raised_exception *) {}
    132 \end{cfa}
     143The repeating function calls itself inside a try block with a handler that
     144does not match the raised exception, but is of the same kind of handler.
     145This means that the EHM has to check each handler, and continue
     146over all of them until it reaches the base of the stack.
     147Comparing this to the empty test gives the time to traverse over and
     148unwind a handler.
    133149\end{itemize}
    134150
    135151\paragraph{Cross Try Statement}
    136 The 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
    138 around:
     152This group of tests measures the cost for setting up exception handling,
     153if it is
     154not used because the exceptional case did not occur.
     155Tests repeatedly cross (enter, execute and leave) a try statement but never
     156perform a raise.
     157\begin{itemize}[nosep]
     158\item Handler:
     159The try statement has a handler (of the appropriate kind).
     160\item Finally:
     161The try statement has a finally clause.
     162\end{itemize}
     163
     164\paragraph{Conditional Matching}
     165This group measures the cost of conditional matching.
     166Only \CFA implements the language level conditional match,
     167the other languages mimic it with an ``unconditional" match (it still
     168checks the exception's type) and conditional re-raise if it is not supposed
     169to handle that exception.
     170
     171Here is the pattern shown in \CFA and \Cpp. Java and Python use the same
     172pattern as \Cpp, but with their own syntax.
     173
     174\begin{minipage}{0.45\textwidth}
    139175\begin{cfa}
    140176try {
    141 } catch (not_raised_exception *) {}
    142 \end{cfa}
    143 \begin{itemize}[nosep]
    144 \item Handler:
    145 The try statement has a handler.
    146 \item Finally:
    147 The try statement replaces the handler with a finally clause.
    148 \end{itemize}
    149 
    150 \paragraph{Conditional Matching}
    151 This final group measures the cost of conditional matching.
    152 Only \CFA implements the language level conditional match,
    153 the other languages must mimic with an ``unconditional" match (it still
    154 checks the exception's type) and conditional re-raise if it was not supposed
    155 to handle that exception.
    156 \begin{center}
    157 \begin{tabular}{ll}
    158 \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp, Java, Python} \\
    159 \begin{cfa}
    160 try {
    161         throw_exception();
    162 } catch (empty_exception * exc;
    163                  should_catch) {
     177        ...
     178} catch (exception_t * e ;
     179                should_catch(e)) {
     180        ...
    164181}
    165182\end{cfa}
    166 &
    167 \begin{cfa}
     183\end{minipage}
     184\begin{minipage}{0.55\textwidth}
     185\begin{lstlisting}[language=C++]
    168186try {
    169         throw_exception();
    170 } catch (EmptyException & exc) {
    171         if (!should_catch) throw;
     187        ...
     188} catch (std::exception & e) {
     189        if (!should_catch(e)) throw;
     190        ...
    172191}
    173 \end{cfa}
    174 \end{tabular}
    175 \end{center}
     192\end{lstlisting}
     193\end{minipage}
    176194\begin{itemize}[nosep]
    177195\item Match All:
     
    180198The condition is always false. (Never matches or always re-raises.)
    181199\end{itemize}
     200
     201\paragraph{Resumption Simulation}
     202A slightly altered version of the Empty Traversal test is used when comparing
     203resumption to fix-up routines.
     204The handler, the actual resumption handler or the fix-up routine,
     205always captures a variable at the base of the loop,
     206and receives a reference to a variable at the raise site, either as a
     207field 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.
    182209
    183210%\section{Cost in Size}
     
    192219
    193220\section{Results}
    194 In cases where a feature is not supported by a language the test is skipped
    195 for that language.
    196 \PAB{Report all values.
    197 
    198 Similarly, if a test does not change between resumption
    199 and termination in \CFA, then only one test is written and the result
    200 was 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
    241 Raise Empty   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    242 Raise D'tor   & 0.0 & 0.0 & 0.0 & N/A & N/A \\
    243 Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\
    244 Raise Other   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    245 Cross Handler & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    246 Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\
    247 Match All     & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    248 Match None    & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     221% First, introduce the tables.
     222\autoref{t:PerformanceTermination},
     223\autoref{t:PerformanceResumption}
     224and~\autoref{t:PerformanceFixupRoutines}
     225show the test results.
     226In cases where a feature is not supported by a language, the test is skipped
     227for that language and the result is marked N/A.
     228There are also cases where the feature is supported but measuring its
     229cost is impossible. This happened with Java, which uses a JIT that optimizes
     230away the tests and cannot be stopped.\cite{Dice21}
     231These tests are marked N/C.
     232To get results in a consistent range (1 second to 1 minute is ideal,
     233going higher is better than going low) N, the number of iterations of the
     234main loop in each test, is varied between tests. It is also given in the
     235results and has a value in the millions.
     236
     237An anomaly in some results came from \CFA's use of GCC nested functions.
     238These nested functions are used to create closures that can access stack
     239variables in their lexical scope.
     240However, if they do so, then they can cause the benchmark's run time to
     241increase by an order of magnitude.
     242The simplest solution is to make those values global variables instead
     243of function-local variables.
     244% Do we know if editing a global inside nested function is a problem?
     245Tests that had to be modified to avoid this problem have been marked
     246with 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}
     259N\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
     262Empty Traversal (1M)   & 23.0  & 9.6   & 17.6  & 23.4      & 30.6  & 13.6  & 15.5  & 14.7  \\
     263D'tor Traversal (1M)   & 48.1  & 23.5  & N/A   & N/A       & 64.2  & 29.2  & N/A   & N/A   \\
     264Finally Traversal (1M) & 3.2*  & N/A   & 17.6  & 29.2      & 3.9*  & N/A   & 15.5  & 19.0  \\
     265Other Traversal (1M)   & 3.3*  & 23.9  & 17.7  & 32.8      & 3.9*  & 24.5  & 15.5  & 21.6  \\
     266Cross Handler (1B)     & 6.5   & 0.9   & N/C   & 38.0      & 9.6   & 0.8   & N/C   & 32.1  \\
     267Cross Finally (1B)     & 0.8   & N/A   & N/C   & 44.6      & 0.6   & N/A   & N/C   & 37.3  \\
     268Match All (10M)        & 30.5  & 20.6  & 11.2  & 3.9       & 36.9  & 24.6  & 10.7  & 3.1   \\
     269Match None (10M)       & 30.6  & 50.9  & 11.2  & 5.0       & 36.9  & 71.9  & 10.7  & 4.1   \\
    249270\hline
    250271\end{tabular}
    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
    291 Raise Empty   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    292 Raise D'tor   & 0.0 & 0.0 & 0.0 & N/A & N/A \\
    293 Raise Finally & 0.0 & 0.0 & N/A & 0.0 & 0.0 \\
    294 Raise Other   & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    295 Cross Handler & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    296 Cross Finally & 0.0 & N/A & N/A & 0.0 & 0.0 \\
    297 Match All     & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
    298 Match None    & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
     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
     280N\hspace{8pt}
     281                        & AMD     & ARM  \\
     282\hline
     283Empty Traversal (10M)   & 1.4     & 1.2  \\
     284D'tor Traversal (10M)   & 1.8     & 1.0  \\
     285Finally Traversal (10M) & 1.8     & 1.0  \\
     286Other Traversal (10M)   & 22.6    & 25.8 \\
     287Cross Handler (1B)      & 9.0     & 11.9 \\
     288Match All (100M)        & 2.3     & 3.2  \\
     289Match None (100M)       & 3.0     & 3.8  \\
    299290\hline
    300291\end{tabular}
    301 
    302 One result not directly related to \CFA but important to keep in
    303 mind is that, for exceptions, the standard intuition about which languages
    304 should 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
    306 rarely considered to be the common case, the more optimized languages
    307 make that case expense. In addition, languages with high-level
    308 representations have a much easier time scanning the stack as there is less
    309 to decode.
    310 
    311 This observation means that while \CFA does not actually keep up with Python in every
    312 case, it is usually no worse than roughly half the speed of \Cpp. This performance is good
    313 enough for the prototyping purposes of the project.
    314 
    315 The test case where \CFA falls short is Raise Other, the case where the
    316 stack is unwound including a bunch of non-matching handlers.
    317 This slowdown seems to come from missing optimizations.
    318 
    319 Importantly, 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
    322 behind their \Cpp counter-parts.
    323 
    324 This suggests that the performance issue in Raise Other is just an
    325 optimization not being applied. Later versions of gcc may be able to
    326 optimize this case further, at least down to the half of \Cpp mark.
    327 A \CFA compiler that directly produced assembly could do even better as it
    328 would not have to work across some of \CFA's current abstractions, like
    329 the try terminate function.
    330 
    331 Resumption exception handling is also incredibly fast. Often an order of
    332 magnitude or two better than the best termination speed.
    333 There is a simple explanation for this; traversing a linked list is much   
    334 faster than examining and unwinding the stack. When resumption does not do as
    335 well its when more try statements are used per raise. Updating the internal
    336 linked list is not very expensive but it does add up.
    337 
    338 The relative speed of the Match All and Match None tests (within each
    339 language) can also show the effectiveness conditional matching as compared
    340 to catch and rethrow.
    341 \begin{itemize}[nosep]
    342 \item
    343 Java and Python get similar values in both tests.
    344 Between the interpreted code, a higher level representation of the call
    345 stack and exception reuse it it is possible the cost for a second
    346 throw can be folded into the first.
    347 % Is this due to optimization?
    348 \item
    349 Both types of \CFA are slightly slower if there is not a match.
    350 For termination this likely comes from unwinding a bit more stack through
    351 libunwind instead of executing the code normally.
    352 For resumption there is extra work in traversing more of the list and running
    353 more checks for a matching exceptions.
    354 % Resumption is a bit high for that but this is my best theory.
    355 \item
    356 Then there is \Cpp, which takes 2--3 times longer to catch and rethrow vs.
    357 just the catch. This is very high, but it does have to repeat the same
    358 process of unwinding the stack and may have to parse the LSDA of the function
    359 with the catch and rethrow twice, once before the catch and once after the
    360 rethrow.
    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}
    364 The difference in relative performance does show that there are savings to
    365 be made by performing the check without catching the exception.
     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}
     304N\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
     307Resume 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.
     313One result not directly related to \CFA but important to keep in mind is that,
     314for exceptions, the standard intuition about which languages should go
     315faster often does not hold.
     316For 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.
     319The most likely explanation is that
     320the generally faster languages have made ``common cases fast" at the expense
     321of the rarer cases. Since exceptions are considered rare, they are made
     322expensive to help speed up common actions, such as entering and leaving try
     323statements.
     324Python, on the other hand, while generally slower than the other languages,
     325uses exceptions more and has not sacrificed their performance.
     326In addition, languages with high-level representations have a much
     327easier time scanning the stack as there is less to decode.
     328
     329As stated,
     330the performance tests are not attempting to show \CFA has a new competitive
     331way of implementing exception handling.
     332The only performance requirement is to insure the \CFA EHM has reasonable
     333performance for prototyping.
     334Although that may be hard to exactly quantify, I believe it has succeeded
     335in that regard.
     336Details 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
     343and closer to \Cpp than other languages.
     344This result is to be expected,
     345as \CFA is closer to \Cpp than the other languages.
     346
     347\item[D'tor Traversal]
     348Running destructors causes a huge slowdown in the two languages that support
     349them. \CFA has a higher proportionate slowdown but it is similar to \Cpp's.
     350Considering the amount of work done in destructors is effectively zero
     351(an assembly comment), the cost
     352must come from the change of context required to run the destructor.
     353
     354\item[Finally Traversal]
     355Performance is similar to Empty Traversal in all languages that support finally
     356clauses. Only Python seems to have a larger than random noise change in
     357its run time and it is still not large.
     358Despite the similarity between finally clauses and destructors,
     359finally clauses seem to avoid the spike that run time destructors have.
     360Possibly some optimization removes the cost of changing contexts.
     361
     362\item[Other Traversal]
     363For \Cpp, stopping to check if a handler applies seems to be about as
     364expensive as stopping to run a destructor.
     365This results in a significant jump.
     366
     367Other languages experience a small increase in run time.
     368The small increase likely comes from running the checks,
     369but they could avoid the spike by not having the same kind of overhead for
     370switching to the check's context.
     371
     372\item[Cross Handler]
     373Here, \CFA falls behind \Cpp by a much more significant margin.
     374This is likely due to the fact that \CFA has to insert two extra function
     375calls, while \Cpp does not have to execute any other instructions.
     376Python is much further behind.
     377
     378\item[Cross Finally]
     379\CFA's performance now matches \Cpp's from Cross Handler.
     380If the code from the finally clause is being inlined,
     381which is just an asm comment, than there are no additional instructions
     382to execute again when exiting the try statement normally.
     383
     384\item[Conditional Match]
     385Both of the conditional matching tests can be considered on their own.
     386However, for evaluating the value of conditional matching itself, the
     387comparison of the two sets of results is useful.
     388Consider the massive jump in run time for \Cpp going from match all to match
     389none, which none of the other languages have.
     390Some strange interaction is causing run time to more than double for doing
     391twice as many raises.
     392Java and Python avoid this problem and have similar run time for both tests,
     393possibly through resource reuse or their program representation.
     394However, \CFA is built like \Cpp, and avoids the problem as well.
     395This matches
     396the pattern of the conditional match, which makes the two execution paths
     397very similar.
     398
     399\end{description}
     400
     401\subsection{Resumption \texorpdfstring{(\autoref{t:PerformanceResumption})}{}}
     402
     403Moving on to resumption, there is one general note:
     404resumption is \textit{fast}. The only test where it fell
     405behind termination is Cross Handler.
     406In every other case, the number of iterations had to be increased by a
     407factor of 10 to get the run time in an appropriate range
     408and 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]
     414See above for the general speed-up notes.
     415This result is not surprising as resumption's linked-list approach
     416means that traversing over stack frames without a resumption handler is
     417$O(1)$.
     418
     419\item[D'tor Traversal]
     420Resumption does have the same spike in run time that termination has.
     421The run time is actually very similar to Finally Traversal.
     422As resumption does not unwind the stack, both destructors and finally
     423clauses are run while walking down the stack during the recursive returns.
     424So it follows their performance is similar.
     425
     426\item[Finally Traversal]
     427Same as D'tor Traversal,
     428except termination did not have a spike in run time on this test case.
     429
     430\item[Other Traversal]
     431Traversing across handlers reduces resumption's advantage as it actually
     432has to stop and check each one.
     433Resumption still came out ahead (adjusting for iterations) but by much less
     434than the other cases.
     435
     436\item[Cross Handler]
     437The only test case where resumption could not keep up with termination,
     438although the difference is not as significant as many other cases.
     439It is simply a matter of where the costs come from:
     440both termination and resumption have some work to set up or tear down a
     441handler. It just so happens that resumption's work is slightly slower.
     442
     443\item[Conditional Match]
     444Resumption shows a slight slowdown if the exception is not matched
     445by the first handler, which follows from the fact the second handler now has
     446to be checked. However, the difference is not large.
     447
     448\end{description}
     449
     450\subsection{Resumption/Fixup \texorpdfstring{(\autoref{t:PerformanceFixupRoutines})}{}}
     451
     452Finally are the results of the resumption/fixup routine comparison.
     453These results are surprisingly varied. It is possible that creating a closure
     454has more to do with performance than passing the argument through layers of
     455calls.
     456At 100 stack frames, resumption and manual fixup routines have similar
     457performance in \CFA.
     458More experiments could try to tease out the exact trade-offs,
     459but the prototype's only performance goal is to be reasonable.
     460It is already in that range, and \CFA's fixup routine simulation is
     461one of the faster simulations as well.
     462Plus, exceptions add features and remove syntactic overhead,
     463so even at similar performance, resumptions have advantages
     464over fixup routines.
  • doc/theses/andrew_beach_MMath/resumption-marking.fig

    rf95634e rb7fd9daf  
    88-2
    991200 2
    10 6 5985 1530 6165 3105
    11 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 1620 90 90 6075 1620 6075 1710
    12 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 2340 90 90 6075 2340 6075 2430
    13 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6075 3015 90 90 6075 3015 6075 3105
    14 2 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
    17 2 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
    21 6 3465 1530 3645 3105
    22 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 1620 90 90 3555 1620 3555 1710
    23 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 2340 90 90 3555 2340 3555 2430
    24 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3555 3015 90 90 3555 3015 3555 3105
    25 2 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
    28 2 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
    32 6 2115 1530 2295 3105
    33 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 1620 90 90 2205 1620 2205 1710
    34 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 2340 90 90 2205 2340 2205 2430
    35 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 2205 3015 90 90 2205 3015 2205 3105
    36 2 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
    39 2 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
    43101 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 1620 90 90 4905 1620 4905 1710
    44 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 3015 90 90 4905 3015 4905 3105
    45111 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 945 90 90 4905 945 4905 1035
    46121 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 2340 90 90 4905 2340 4905 2430
    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          2790 1620 2430 1620
    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          4095 2340 3735 2340
    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          6660 1620 6300 1620
    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          5490 945 5130 945
     131 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 1620 90 90 1665 1620 1665 1710
     141 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 2340 90 90 1665 2340 1665 2430
     151 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1665 3060 90 90 1665 3060 1665 3150
     161 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 1620 90 90 3195 1620 3195 1710
     171 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 2340 90 90 3195 2340 3195 2430
     181 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3195 3060 90 90 3195 3060 3195 3150
     191 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 1620 90 90 6525 1620 6525 1710
     201 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 2340 90 90 6525 2340 6525 2430
     211 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4905 3060 90 90 4905 3060 4905 3150
     221 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 6525 3060 90 90 6525 3060 6525 3150
    59232 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
    6024        1 1 1.00 60.00 120.00
     
    6630        1 1 1.00 60.00 120.00
    6731         4770 1080 4590 1260 4590 2070 4770 2250
    68 4 0 0 50 -1 0 12 0.0000 4 135 1170 1980 3375 Initial State\001
    69 4 0 0 50 -1 0 12 0.0000 4 135 1170 3420 3375 Found Handler\001
    70 4 0 0 50 -1 0 12 0.0000 4 165 810 4770 3375 Try block\001
    71 4 0 0 50 -1 0 12 0.0000 4 135 900 4770 3555 in Handler\001
    72 4 0 0 50 -1 0 12 0.0000 4 165 1530 5940 3375 Handling Complete\001
     322 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
     352 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
     382 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
     412 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
     442 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
     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         6525 2475 6525 2925
     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         1260 1620 1485 1620
     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         1980 1440 1755 1440
     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         2790 2340 3015 2340
     592 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
     622 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
     652 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
     682 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
     712 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
     744 1 0 50 -1 0 12 0.0000 0 135 1170 1665 3375 Initial State\001
     754 1 0 50 -1 0 12 0.0000 0 135 1170 3195 3375 Found Handler\001
     764 1 0 50 -1 0 12 0.0000 0 165 1530 6570 3375 Handling Complete\001
     774 2 0 50 -1 0 12 0.0000 0 135 720 1485 2385 handlers\001
     784 1 0 50 -1 0 12 0.0000 0 135 900 4905 3375 Handler in\001
     794 1 0 50 -1 0 12 0.0000 0 165 810 4905 3600 Try block\001
     804 0 0 50 -1 0 12 0.0000 0 135 360 855 1665 head\001
     814 0 0 50 -1 0 12 0.0000 4 120 810 2025 1485 execution\001
     824 0 0 50 -1 0 12 0.0000 0 135 360 2385 2385 head\001
     834 0 0 50 -1 0 12 0.0000 4 120 810 3645 1665 execution\001
     844 0 0 50 -1 0 12 0.0000 0 135 360 4095 990 head\001
     854 0 0 50 -1 0 12 0.0000 4 120 810 5310 810 execution\001
     864 0 0 50 -1 0 12 0.0000 0 135 360 5715 1665 head\001
     874 0 0 50 -1 0 12 0.0000 4 120 810 6885 1485 execution\001
  • doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex

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

    rf95634e rb7fd9daf  
    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.
    34
    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"
     5@misc{Dice21,
     6    author      = {Dave Dice},
     7    year        = 2021,
     8    month       = aug,
     9    howpublished= {personal communication}
    1110}
    1211
    13 @book{knuth.book,
    14         author =        "Donald Knuth",
    15         title =         "The \TeX book",
    16         year =          "1986",
    17         publisher =     "Addison-Wesley",
    18         address =       "Reading, Massachusetts"
     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}},
    1917}
    2018
    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"
     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},
    2825}
     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

    rf95634e rb7fd9daf  
    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}]{@}{@}}
    214212% Annotations from Peter:
    215213\newcommand{\PAB}[1]{{\color{blue}PAB: #1}}
  • doc/theses/andrew_beach_MMath/vtable-layout.fig

    rf95634e rb7fd9daf  
    88-2
    991200 2
    10 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
    11          1620 1665
    12102 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
    1311         3510 1890 3645 1755
     
    16142 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
    1715         3645 1305 3645 1755
     162 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
     17         2115 1935 2250 1935
     182 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
     202 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
     21         2250 1350 2115 1350
    18224 0 0 50 -1 0 12 0.0000 4 165 630 2295 1305 type_id\001
    19234 0 0 50 -1 0 12 0.0000 4 165 1170 2295 1500 parent_field0\001
  • doc/theses/mubeen_zulfiqar_MMath/allocator.tex

    rf95634e rb7fd9daf  
    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
    2829\begin{itemize}
    2930\item
     
    117118We 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.
    118119
    119 \subsubsection void * aalloc( size_t dim, size_t elemSize )
     120\subsubsection void * aalloc( size\_t dim, size\_t elemSize )
    120121aalloc 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.
    121122\paragraph{Usage}
    122123aalloc takes two parameters.
     124
    123125\begin{itemize}
    124126\item
     
    129131It 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.
    130132
    131 \subsubsection void * resize( void * oaddr, size_t size )
     133\subsubsection void * resize( void * oaddr, size\_t size )
    132134resize 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.
    133135\paragraph{Usage}
    134136resize takes two parameters.
     137
    135138\begin{itemize}
    136139\item
     
    141144It 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.
    142145
    143 \subsubsection void * resize( void * oaddr, size_t nalign, size_t size )
     146\subsubsection void * resize( void * oaddr, size\_t nalign, size\_t size )
    144147This 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.
    145148\paragraph{Usage}
    146149This resize takes three parameters. It takes an additional parameter of nalign as compared to the above resize (FIX ME: cite above resize).
     150
    147151\begin{itemize}
    148152\item
     
    155159It returns an object with the size and alignment given in the parameters. On failure, it returns a NULL pointer.
    156160
    157 \subsubsection void * amemalign( size_t alignment, size_t dim, size_t elemSize )
     161\subsubsection void * amemalign( size\_t alignment, size\_t dim, size\_t elemSize )
    158162amemalign 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.
    159163\paragraph{Usage}
    160164amemalign takes three parameters.
     165
    161166\begin{itemize}
    162167\item
     
    169174It 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.
    170175
    171 \subsubsection void * cmemalign( size_t alignment, size_t dim, size_t elemSize )
     176\subsubsection void * cmemalign( size\_t alignment, size\_t dim, size\_t elemSize )
    172177cmemalign 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.
    173178\paragraph{Usage}
    174179cmemalign takes three parameters.
     180
    175181\begin{itemize}
    176182\item
     
    183189It 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.
    184190
    185 \subsubsection size_t malloc_alignment( void * addr )
    186 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.
    187 \paragraph{Usage}
    188 malloc_alignment takes one parameters.
     191\subsubsection size\_t malloc\_alignment( void * addr )
     192malloc\_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}
     194malloc\_alignment takes one parameters.
     195
    189196\begin{itemize}
    190197\item
    191198addr: the address of the currently allocated dynamic object.
    192199\end{itemize}
    193 malloc_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 )
    196 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.
    197 \paragraph{Usage}
    198 malloc_zero_fill takes one parameters.
     200malloc\_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 )
     203malloc\_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}
     205malloc\_zero\_fill takes one parameters.
     206
    199207\begin{itemize}
    200208\item
    201209addr: the address of the currently allocated dynamic object.
    202210\end{itemize}
    203 malloc_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 )
    206 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.
    207 \paragraph{Usage}
    208 malloc_size takes one parameters.
     211malloc\_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 )
     214malloc\_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}
     216malloc\_size takes one parameters.
     217
    209218\begin{itemize}
    210219\item
    211220addr: the address of the currently allocated dynamic object.
    212221\end{itemize}
    213 malloc_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 )
     222malloc\_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 )
    216225This 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.
    217226\paragraph{Usage}
    218227This realloc takes three parameters. It takes an additional parameter of nalign as compared to the default realloc.
     228
    219229\begin{itemize}
    220230\item
     
    237247It returns a dynamic object of the size of type T. On failure, it return NULL pointer.
    238248
    239 \subsubsection T * aalloc( size_t dim )
     249\subsubsection T * aalloc( size\_t dim )
    240250This 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.
    241251\paragraph{Usage}
    242252aalloc takes one parameters.
     253
    243254\begin{itemize}
    244255\item
     
    247258It 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.
    248259
    249 \subsubsection T * calloc( size_t dim )
     260\subsubsection T * calloc( size\_t dim )
    250261This 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.
    251262\paragraph{Usage}
    252263This calloc takes one parameter.
     264
    253265\begin{itemize}
    254266\item
     
    257269It 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.
    258270
    259 \subsubsection T * resize( T * ptr, size_t size )
     271\subsubsection T * resize( T * ptr, size\_t size )
    260272This 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.
    261273\paragraph{Usage}
    262274This resize takes two parameters.
     275
    263276\begin{itemize}
    264277\item
     
    269282It 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.
    270283
    271 \subsubsection T * realloc( T * ptr, size_t size )
     284\subsubsection T * realloc( T * ptr, size\_t size )
    272285This 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.
    273286\paragraph{Usage}
    274287This realloc takes two parameters.
     288
    275289\begin{itemize}
    276290\item
     
    281295It 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.
    282296
    283 \subsubsection T * memalign( size_t align )
     297\subsubsection T * memalign( size\_t align )
    284298This 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.
    285299\paragraph{Usage}
    286300memalign takes one parameters.
     301
    287302\begin{itemize}
    288303\item
     
    291306It returns a dynamic object of the size of type T that is aligned to given parameter align. On failure, it return NULL pointer.
    292307
    293 \subsubsection T * amemalign( size_t align, size_t dim )
     308\subsubsection T * amemalign( size\_t align, size\_t dim )
    294309This 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.
    295310\paragraph{Usage}
    296311amemalign takes two parameters.
     312
    297313\begin{itemize}
    298314\item
     
    303319It 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.
    304320
    305 \subsubsection T * cmemalign( size_t align, size_t dim  )
     321\subsubsection T * cmemalign( size\_t align, size\_t dim  )
    306322This 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.
    307323\paragraph{Usage}
    308324cmemalign takes two parameters.
     325
    309326\begin{itemize}
    310327\item
     
    315332It 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.
    316333
    317 \subsubsection T * aligned_alloc( size_t align )
    318 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.
    319 \paragraph{Usage}
    320 This aligned_alloc takes one parameter.
     334\subsubsection T * aligned\_alloc( size\_t align )
     335This 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}
     337This aligned\_alloc takes one parameter.
     338
    321339\begin{itemize}
    322340\item
     
    325343It returns a dynamic object of the size of type T that is aligned to the given parameter. On failure, it return NULL pointer.
    326344
    327 \subsubsection int posix_memalign( T ** ptr, size_t align )
    328 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.
    329 \paragraph{Usage}
    330 This posix_memalign takes two parameter.
     345\subsubsection int posix\_memalign( T ** ptr, size\_t align )
     346This 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}
     348This posix\_memalign takes two parameter.
     349
    331350\begin{itemize}
    332351\item
     
    335354align: required alignment of the dynamic object.
    336355\end{itemize}
     356
    337357It 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.
    338358
     
    349369It 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.
    350370
    351 \subsection{Alloc Interface}
     371\subsection Alloc Interface
    352372In 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.
    353373This interface helps programmers in three major ways.
     374
    354375\begin{itemize}
    355376\item
     
    371392This 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.
    372393It represents the required number of members in the array allocation as in CFA's aalloc (FIX ME: cite aalloc).
    373 This parameter should be of type size_t.
     394This parameter should be of type size\_t.
    374395
    375396Example: int a = alloc( 5 )
     
    377398
    378399\paragraph{Align}
    379 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.
     400This 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.
    380401
    381402Example: int b = alloc( 5 , 64`align )
     
    385406This 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.
    386407Three types of parameters can be passed using `fill.
     408
    387409\begin{itemize}
    388410\item
  • doc/theses/mubeen_zulfiqar_MMath/background.tex

    rf95634e rb7fd9daf  
    2323====================
    2424
    25 \cite{Wasik08}
     25\section{Background}
     26
     27% FIXME: cite wasik
     28\cite{wasik.thesis}
     29
     30\subsection{Memory Allocation}
     31With 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}
     34dlmalloc (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}
     37Hoard (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}
     40jemalloc (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}
     43ptmalloc (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}
     46rpmalloc (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}
     49tbb 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}
     52tcmalloc (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}
     55There 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.
  • doc/theses/mubeen_zulfiqar_MMath/benchmarks.tex

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

    rf95634e rb7fd9daf  
    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}
    67 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.
    68 
    69 \paragraph{dlmalloc}
    70 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)
    71 
    72 \paragraph{hoard}
    73 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)
    74 
    75 \paragraph{jemalloc}
    76 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.
    77 
    78 \paragraph{ptmalloc}
    79 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.
    80 
    81 \paragraph{rpmalloc}
    82 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.
    83 
    84 \paragraph{tbb malloc}
    85 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.
    86 
    87 \paragraph{tc malloc}
    88 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.
    89 
    90 \subsection{Benchmarks}
    91 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.
    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.
    10163
    10264\section{Research Objectives}
  • doc/theses/mubeen_zulfiqar_MMath/performance.tex

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

    rf95634e rb7fd9daf  
    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

    rf95634e rb7fd9daf  
    8484\usepackage{graphicx}
    8585\usepackage{comment} % Removes large sections of the document.
     86\usepackage{tabularx}
    8687
    8788% Hyperlinks make it very easy to navigate an electronic document.
     
    191192% Tip: Putting each sentence on a new line is a way to simplify later editing.
    192193%----------------------------------------------------------------------
     194\begin{sloppypar}
     195
    193196\input{intro}
    194197\input{background}
     
    197200\input{performance}
    198201\input{conclusion}
     202
     203\end{sloppypar}
    199204
    200205%----------------------------------------------------------------------
  • doc/theses/thierry_delisle_PhD/.gitignore

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

    rf95634e rb7fd9daf  
    2020        practice \
    2121        io \
     22        eval_micro \
     23        eval_macro \
    2224}}
    2325
     
    3537        pivot_ring \
    3638        system \
     39        cycle \
    3740}
    3841
  • doc/theses/thierry_delisle_PhD/thesis/thesis.tex

    rf95634e rb7fd9daf  
    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 \chapter{Micro-Benchmarks}
    239 \chapter{Larger-Scale applications}
     237% \chapter{Theoretical and Existance Proofs}
     238\input{text/eval_micro.tex}
     239\input{text/eval_macro.tex}
    240240\part{Conclusion \& Annexes}
    241241
  • doc/user/user.tex

    rf95634e rb7fd9daf  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Mon May 31 09:03:34 2021
    14 %% Update Count     : 5071
     13%% Last Modified On : Sun Oct 10 12:45:00 2021
     14%% Update Count     : 5095
    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 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 ";
     4446The 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 ";
    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, 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}$
     4468To lock a stream across multiple I/O operations, he long form of the mutex statement is used, \eg:
     4469\begin{cfa}
     4470®mutex( sout )® {
    44724471        sout | 1;
    4473         sout | ®acquire® | 2 | 3;                               $\C{// unnecessary, but ok to acquire and release again}$
     4472        ®mutex() sout® | 2 | 3;                         $\C{// unnecessary, but ok because of recursive lock}$
    44744473        sout | 4;
    4475 }       // implicitly release the lock when "acq" is deallocated
    4476 \end{cfa}
    4477 Note, the unnecessary ©acquire© manipulator works because the recursive stream-lock can be acquired/released multiple times by the owner thread.
     4474} // implicitly release sout lock
     4475\end{cfa}
     4476Note, 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.
    44784477Hence, calls to functions that also acquire a stream lock for their output do not result in \Index{deadlock}.
    44794478
    44804479The previous values written by threads 1 and 2 can be read in concurrently:
    44814480\begin{cfa}
    4482 {       // acquire sin lock for block duration
    4483         ®isacquire acq = { sin };®                              $\C{// named stream locker}$
     4481®mutex( sin )® {
    44844482        int x, y, z, w;
    44854483        sin | x;
    4486         sin | ®acquire® | y | z;                                $\C{// unnecessary, but ok to acquire and release again}$
     4484        ®mutex() sin® | y | z;                          $\C{// unnecessary, but ok because of recursive lock}$
    44874485        sin | w;
    4488 }       // implicitly release the lock when "acq" is deallocated
     4486} // implicitly release sin lock
    44894487\end{cfa}
    44904488Again, the order of the reading threads is non-deterministic.
     
    44934491\Textbf{WARNING:} The general problem of \Index{nested locking} can occur if routines are called in an I/O sequence that block, \eg:
    44944492\begin{cfa}
    4495 sout | ®acquire® | "data:" | rtn( mon );        $\C{// mutex call on monitor}$
     4493®mutex() sout® | "data:" | rtn( mon );  $\C{// mutex call on monitor}$
    44964494\end{cfa}
    44974495If 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.
     
    45004498\begin{cfa}
    45014499int ®data® = rtn( mon );
    4502 sout | acquire | "data:" | ®data®;
     4500mutex() sout | "data:" | ®data®;
    45034501\end{cfa}
    45044502
     
    45064504\section{String Stream}
    45074505
    4508 All 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.
     4506The 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.
     4508The only string stream operations different from a file stream are:
     4509\begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
     4510\item
     4511constructors 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}
     4513void ?{}( ostrstream &, char buf[], size_t size );
     4514void ?{}( 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}
     4519ostrstream & write( ostrstream & os, FILE * stream = stdout );
     4520\end{cfa}
     4521There is no ©read© for ©istrstream©.
     4522\end{itemize}
     4523
    45104524\begin{figure}
    45114525\begin{cfa}
     
    45204534        double x = 12345678.9, y = 98765.4321e-11;
    45214535
    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}$
     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}$
    45284542        ®istrstream isstr = { buf2 };®
    4529         isstr | i | j | k | x | y;
    4530         sout | i | j | k | x | y;
    4531 }
     4543        char s[10];
     4544        isstr | i | j | k | x | y | s;
     4545        sout  | i | j | k | x | y | s;
     4546}
     4547
     45483 0x5          7 1.234568e+07 987.654n abc
     45493 0x5          7 1.234568e+07 987.654n abc
     45503 0x5          7 1.234568e+07 987.654n abc
     455112 14 15 3.5 70000. abc
    45324552\end{cfa}
    45334553\caption{String Stream Processing}
    45344554\label{f:StringStreamProcessing}
    45354555\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}
    4542 The constructor functions:
    4543 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
    4544 \item
    4545 create 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 
    4552 ostrstream & write( ostrstream & os, FILE * stream = stdout );
    4553 
    4554 void ?{}( ostrstream &, char buf[], size_t size );
    4555 
    4556 // *********************************** istrstream ***********************************
    4557 
    4558 void ?{}( istrstream & is, char buf[] );
    4559 \end{cfa}
    4560 \caption{String Stream Functions}
    4561 \label{f:StringStreamFunctions}
    4562 \end{figure}
    4563 
    45644556
    45654557\begin{comment}
  • libcfa/prelude/bootloader.cf

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

    rf95634e rb7fd9daf  
    1010// Created On       : Fri Jul 21 16:21:03 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jul 21 13:31:34 2021
    13 // Update Count     : 129
     12// Last Modified On : Sat Aug 14 08:45:54 2021
     13// Update Count     : 133
    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
    109113// exponentiation operator implementation
    110114
  • libcfa/src/Makefile.am

    rf95634e rb7fd9daf  
    4848        math.hfa \
    4949        time_t.hfa \
     50        bits/algorithm.hfa \
    5051        bits/align.hfa \
    5152        bits/containers.hfa \
     
    7778        memory.hfa \
    7879        parseargs.hfa \
     80        parseconfig.hfa \
    7981        rational.hfa \
    8082        stdlib.hfa \
     
    8587        containers/pair.hfa \
    8688        containers/result.hfa \
     89        containers/string.hfa \
     90        containers/string_res.hfa \
    8791        containers/vector.hfa \
    8892        device/cpu.hfa
     
    9094libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \
    9195        assert.cfa \
    92         bits/algorithm.hfa \
    9396        bits/debug.cfa \
    9497        exception.c \
     
    106109        concurrency/invoke.h \
    107110        concurrency/future.hfa \
    108         concurrency/kernel/fwd.hfa
     111        concurrency/kernel/fwd.hfa \
     112        concurrency/mutex_stmt.hfa
    109113
    110114inst_thread_headers_src = \
     
    192196        $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@}
    193197
     198concurrency/io/call.cfa: $(srcdir)/concurrency/io/call.cfa.in
     199        ${AM_V_GEN}python3 $< > $@
     200
    194201#----------------------------------------------------------------------------------------------------------------
    195202libcfa_la_SOURCES = ${libsrc}
  • libcfa/src/concurrency/clib/cfathread.cfa

    rf95634e rb7fd9daf  
    1414//
    1515
     16// #define EPOLL_FOR_SOCKETS
     17
    1618#include "fstream.hfa"
    1719#include "locks.hfa"
     
    2325#include "cfathread.h"
    2426
     27extern "C" {
     28                #include <string.h>
     29                #include <errno.h>
     30}
     31
    2532extern void ?{}(processor &, const char[], cluster &, thread$ *);
    2633extern "C" {
    2734      extern void __cfactx_invoke_thread(void (*main)(void *), void * this);
     35        extern int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
    2836}
    2937
    3038extern Time __kernel_get_time();
     39extern unsigned register_proc_id( void );
    3140
    3241//================================================================================
    33 // Thread run y the C Interface
     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
    34225
    35226struct cfathread_object {
     
    245436        // Mutex
    246437        struct cfathread_mutex {
    247                 fast_lock impl;
     438                linear_backoff_then_block_lock impl;
    248439        };
    249440        int cfathread_mutex_init(cfathread_mutex_t *restrict mut, const cfathread_mutexattr_t *restrict) __attribute__((nonnull (1))) { *mut = new(); return 0; }
     
    260451        // Condition
    261452        struct cfathread_condition {
    262                 condition_variable(fast_lock) impl;
     453                condition_variable(linear_backoff_then_block_lock) impl;
    263454        };
    264455        int cfathread_cond_init(cfathread_cond_t *restrict cond, const cfathread_condattr_t *restrict) __attribute__((nonnull (1))) { *cond = new(); return 0; }
     
    288479        // IO operations
    289480        int cfathread_socket(int domain, int type, int protocol) {
    290                 return socket(domain, type, protocol);
     481                return socket(domain, type
     482                #if defined(EPOLL_FOR_SOCKETS)
     483                        | SOCK_NONBLOCK
     484                #endif
     485                , protocol);
    291486        }
    292487        int cfathread_bind(int socket, const struct sockaddr *address, socklen_t address_len) {
     
    299494
    300495        int cfathread_accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len) {
    301                 return cfa_accept4(socket, address, address_len, 0, CFA_IO_LAZY);
     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
    302510        }
    303511
    304512        int cfathread_connect(int socket, const struct sockaddr *address, socklen_t address_len) {
    305                 return cfa_connect(socket, address, address_len, CFA_IO_LAZY);
     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
    306526        }
    307527
     
    315535
    316536        ssize_t cfathread_sendmsg(int socket, const struct msghdr *message, int flags) {
    317                 return cfa_sendmsg(socket, message, flags, CFA_IO_LAZY);
     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;
    318552        }
    319553
    320554        ssize_t cfathread_write(int fildes, const void *buf, size_t nbyte) {
    321555                // Use send rather then write for socket since it's faster
    322                 return cfa_send(fildes, buf, nbyte, 0, CFA_IO_LAZY);
     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;
    323571        }
    324572
     
    336584                msg.msg_controllen = 0;
    337585
    338                 ssize_t ret = cfa_recvmsg(socket, &msg, flags, CFA_IO_LAZY);
     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
    339599
    340600                if(address_len) *address_len = msg.msg_namelen;
     
    344604        ssize_t cfathread_read(int fildes, void *buf, size_t nbyte) {
    345605                // Use recv rather then read for socket since it's faster
    346                 return cfa_recv(fildes, buf, nbyte, 0, CFA_IO_LAZY);
    347         }
    348 
    349 }
     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}
  • libcfa/src/concurrency/invoke.h

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

    rf95634e rb7fd9daf  
    9090        static inline unsigned __flush( struct $io_context & );
    9191        static inline __u32 __release_sqes( struct $io_context & );
    92         extern void __kernel_unpark( thread$ * thrd );
     92        extern void __kernel_unpark( thread$ * thrd, unpark_hint );
    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 ) );
     120                        __kernel_unpark( fulfil( *future, cqe.res, false ), UNPARK_LOCAL );
    121121                }
    122122
     
    183183                ctx.proc->io.pending = false;
    184184
    185                 ready_schedule_lock();
    186185                __cfa_io_drain( proc );
    187                 ready_schedule_unlock();
    188186                // for(i; 2) {
    189187                //      unsigned idx = proc->rdq.id + i;
     
    311309                // Make the sqes visible to the submitter
    312310                __atomic_store_n(sq.kring.tail, tail + have, __ATOMIC_RELEASE);
    313                 sq.to_submit++;
     311                sq.to_submit += have;
    314312
    315313                ctx->proc->io.pending = true;
    316314                ctx->proc->io.dirty   = true;
    317315                if(sq.to_submit > 30 || !lazy) {
     316                        ready_schedule_lock();
    318317                        __cfa_io_flush( ctx->proc );
     318                        ready_schedule_unlock();
    319319                }
    320320        }
  • libcfa/src/concurrency/io/types.hfa

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

    rf95634e rb7fd9daf  
    2222#include <errno.h>
    2323#include <stdio.h>
     24#include <string.h>
    2425#include <signal.h>
    2526#include <unistd.h>
     
    3132#include "kernel_private.hfa"
    3233#include "preemption.hfa"
     34#include "strstream.hfa"
     35#include "device/cpu.hfa"
    3336
    3437//Private includes
     
    193196
    194197                        if( !readyThread ) {
     198                                ready_schedule_lock();
    195199                                __cfa_io_flush( this );
     200                                ready_schedule_unlock();
     201
    196202                                readyThread = __next_thread_slow( this->cltr );
    197203                        }
     
    231237                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    232238
    233                                 __disable_interrupts_hard();
    234                                 eventfd_t val;
    235                                 eventfd_read( this->idle, &val );
    236                                 __enable_interrupts_hard();
     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                                }
    237256
    238257                                #if !defined(__CFA_NO_STATISTICS__)
     
    261280
    262281                        if(this->io.pending && !this->io.dirty) {
     282                                ready_schedule_lock();
    263283                                __cfa_io_flush( this );
     284                                ready_schedule_unlock();
    264285                        }
    265286
     
    301322
    302323                                // Don't block if we are done
    303                                 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
     324                                if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) {
     325                                        ready_schedule_unlock();
     326                                        break MAIN_LOOP;
     327                                }
    304328
    305329                                __STATS( __tls_stats()->ready.sleep.halts++; )
     
    325349                                }
    326350
    327                                         __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
     351                                __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
    328352                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    329353
    330                                 // __disable_interrupts_hard();
    331                                 eventfd_t val;
    332                                 eventfd_read( this->idle, &val );
    333                                 // __enable_interrupts_hard();
     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                                }
    334371
    335372                                        __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); )
     
    393430        /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next );
    394431        __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;
    405432
    406433        __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name);
     
    457484                if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) {
    458485                        // The thread was preempted, reschedule it and reset the flag
    459                         schedule_thread$( thrd_dst );
     486                        schedule_thread$( thrd_dst, UNPARK_LOCAL );
    460487                        break RUNNING;
    461488                }
     
    541568// Scheduler routines
    542569// KERNEL ONLY
    543 static void __schedule_thread( thread$ * thrd ) {
     570static void __schedule_thread( thread$ * thrd, unpark_hint hint ) {
    544571        /* paranoid */ verify( ! __preemption_enabled() );
    545572        /* paranoid */ verify( ready_schedule_islocked());
     
    561588        // Dereference the thread now because once we push it, there is not guaranteed it's still valid.
    562589        struct cluster * cl = thrd->curr_cluster;
    563         __STATS(bool outside = thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
     590        __STATS(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
    564591
    565592        // push the thread to the cluster ready-queue
    566         push( cl, thrd, local );
     593        push( cl, thrd, hint );
    567594
    568595        // variable thrd is no longer safe to use
     
    589616}
    590617
    591 void schedule_thread$( thread$ * thrd ) {
     618void schedule_thread$( thread$ * thrd, unpark_hint hint ) {
    592619        ready_schedule_lock();
    593                 __schedule_thread( thrd );
     620                __schedule_thread( thrd, hint );
    594621        ready_schedule_unlock();
    595622}
     
    642669}
    643670
    644 void __kernel_unpark( thread$ * thrd ) {
     671void __kernel_unpark( thread$ * thrd, unpark_hint hint ) {
    645672        /* paranoid */ verify( ! __preemption_enabled() );
    646673        /* paranoid */ verify( ready_schedule_islocked());
     
    650677        if(__must_unpark(thrd)) {
    651678                // Wake lost the race,
    652                 __schedule_thread( thrd );
     679                __schedule_thread( thrd, hint );
    653680        }
    654681
     
    657684}
    658685
    659 void unpark( thread$ * thrd ) {
     686void unpark( thread$ * thrd, unpark_hint hint ) {
    660687        if( !thrd ) return;
    661688
     
    663690                disable_interrupts();
    664691                        // Wake lost the race,
    665                         schedule_thread$( thrd );
     692                        schedule_thread$( thrd, hint );
    666693                enable_interrupts(false);
    667694        }
     
    920947                        /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count);
    921948                        /* paranoid */ verify( it->local_data->this_stats );
     949                        // __print_stats( it->local_data->this_stats, cltr->print_stats, "Processor", it->name, (void*)it );
    922950                        __tally_stats( cltr->stats, it->local_data->this_stats );
    923951                        it = &(*it)`next;
     
    929957                // this doesn't solve all problems but does solve many
    930958                // so it's probably good enough
     959                disable_interrupts();
    931960                uint_fast32_t last_size = ready_mutate_lock();
    932961
     
    936965                // Unlock the RWlock
    937966                ready_mutate_unlock( last_size );
     967                enable_interrupts();
    938968        }
    939969
  • libcfa/src/concurrency/kernel.hfa

    rf95634e rb7fd9daf  
    151151struct __attribute__((aligned(128))) __timestamp_t {
    152152        volatile unsigned long long tv;
    153 };
    154 
    155 static inline void  ?{}(__timestamp_t & this) { this.tv = 0; }
     153        volatile unsigned long long ma;
     154};
     155
     156// Aligned timestamps which are used by the relaxed ready queue
     157struct __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
     163static inline void  ?{}(__timestamp_t & this) { this.tv = 0; this.ma = 0; }
    156164static inline void ^?{}(__timestamp_t & this) {}
    157165
     
    169177                // Array of times
    170178                __timestamp_t * volatile tscs;
     179
     180                // Array of stats
     181                __help_cnts_t * volatile help;
    171182
    172183                // Number of lanes (empty or not)
  • libcfa/src/concurrency/kernel/fwd.hfa

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

    rf95634e rb7fd9daf  
    100100// Other Forward Declarations
    101101extern void __wake_proc(processor *);
     102extern int cfa_main_returned;                                                   // from interpose.cfa
    102103
    103104//-----------------------------------------------------------------------------
     
    200201        __cfadbg_print_safe(runtime_core, "Kernel : Main cluster ready\n");
    201202
     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
    202227        // Start by initializing the main thread
    203228        // SKULLDUGGERY: the mainThread steals the process main thread
     
    210235        __cfadbg_print_safe(runtime_core, "Kernel : Main thread ready\n");
    211236
    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 
    239237        //initialize the global state variables
    240238        __cfaabi_tls.this_processor = mainProcessor;
     
    252250        // Add the main thread to the ready queue
    253251        // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread
    254         schedule_thread$(mainThread);
     252        schedule_thread$(mainThread, UNPARK_LOCAL);
    255253
    256254        // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX
     
    271269
    272270static void __kernel_shutdown(void) {
     271        if(!cfa_main_returned) return;
    273272        /* paranoid */ verify( __preemption_enabled() );
    274273        disable_interrupts();
     
    486485        link.next = 0p;
    487486        link.ts   = -1llu;
    488         preferred = -1u;
     487        preferred = ready_queue_new_preferred();
    489488        last_proc = 0p;
    490489        #if defined( __CFA_WITH_VERIFY__ )
  • libcfa/src/concurrency/kernel_private.hfa

    rf95634e rb7fd9daf  
    4646}
    4747
    48 void schedule_thread$( thread$ * ) __attribute__((nonnull (1)));
     48void schedule_thread$( thread$ *, unpark_hint hint ) __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, bool local);
     302__attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint);
    303303
    304304//-----------------------------------------------------------------------
     
    321321
    322322//-----------------------------------------------------------------------
     323// get preferred ready for new thread
     324unsigned ready_queue_new_preferred();
     325
     326//-----------------------------------------------------------------------
    323327// Increase the width of the ready queue (number of lanes) by 4
    324328void ready_queue_grow  (struct cluster * cltr);
  • libcfa/src/concurrency/locks.hfa

    rf95634e rb7fd9daf  
    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 
    350 static 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 
    384326        if(2 != compare_val && try_lock_contention(this)) return true;
    385327        // block until signalled
     
    402344static inline void on_notify(linear_backoff_then_block_lock & this, struct thread$ * t ) { unpark(t); }
    403345static inline size_t on_wait(linear_backoff_then_block_lock & this) { unlock(this); return 0; }
    404 static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock_improved(this); }
     346static inline void on_wakeup(linear_backoff_then_block_lock & this, size_t recursion ) { lock(this); }
    405347
    406348//-----------------------------------------------------------------------------
  • libcfa/src/concurrency/monitor.cfa

    rf95634e rb7fd9daf  
    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
     996void 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
     1048void unlock( monitor$ * this ) { __leave( this ); }
     1049
    9921050// Local Variables: //
    9931051// mode: c //
  • libcfa/src/concurrency/monitor.hfa

    rf95634e rb7fd9daf  
    6565        free( th );
    6666}
     67
     68static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } )
     69void 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
    6778
    6879//-----------------------------------------------------------------------------
     
    149160void __waitfor_internal( const __waitfor_mask_t & mask, int duration );
    150161
     162// lock and unlock routines for mutex statements to use
     163void lock( monitor$ * this );
     164void unlock( monitor$ * this );
     165
    151166// Local Variables: //
    152167// mode: c //
  • libcfa/src/concurrency/ready_queue.cfa

    rf95634e rb7fd9daf  
    100100        #define __kernel_rseq_unregister rseq_unregister_current_thread
    101101#elif defined(CFA_HAVE_LINUX_RSEQ_H)
    102         void __kernel_raw_rseq_register  (void);
    103         void __kernel_raw_rseq_unregister(void);
     102        static void __kernel_raw_rseq_register  (void);
     103        static void __kernel_raw_rseq_unregister(void);
    104104
    105105        #define __kernel_rseq_register __kernel_raw_rseq_register
     
    246246// Cforall Ready Queue used for scheduling
    247247//=======================================================================
     248unsigned 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
    248255void ?{}(__ready_queue_t & this) with (this) {
    249256        #if defined(USE_CPU_WORK_STEALING)
     
    251258                lanes.data = alloc( lanes.count );
    252259                lanes.tscs = alloc( lanes.count );
     260                lanes.help = alloc( cpu_info.hthrd_count );
    253261
    254262                for( idx; (size_t)lanes.count ) {
    255263                        (lanes.data[idx]){};
    256264                        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;
    257271                }
    258272        #else
    259273                lanes.data  = 0p;
    260274                lanes.tscs  = 0p;
     275                lanes.help  = 0p;
    261276                lanes.count = 0;
    262277        #endif
     
    270285        free(lanes.data);
    271286        free(lanes.tscs);
     287        free(lanes.help);
    272288}
    273289
    274290//-----------------------------------------------------------------------
    275291#if defined(USE_CPU_WORK_STEALING)
    276         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {
     292        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
    277293                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
    278294
    279295                processor * const proc = kernelTLS().this_processor;
    280                 const bool external = !push_local || (!proc) || (cltr != proc->cltr);
    281 
     296                const bool external = (!proc) || (cltr != proc->cltr);
     297
     298                // Figure out the current cpu and make sure it is valid
    282299                const int cpu = __kernel_getcpu();
    283300                /* paranoid */ verify(cpu >= 0);
     
    285302                /* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count);
    286303
    287                 const cpu_map_entry_t & map = cpu_info.llc_map[cpu];
     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                }
    288315                /* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count);
    289316                /* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count);
     
    296323                        if(unlikely(external)) { r = __tls_rand(); }
    297324                        else { r = proc->rdq.its++; }
    298                         i = start + (r % READYQ_SHARD_FACTOR);
     325                        choose(hint) {
     326                                case UNPARK_LOCAL : i = start + (r % READYQ_SHARD_FACTOR);
     327                                case UNPARK_REMOTE: i = prf   + (r % READYQ_SHARD_FACTOR);
     328                        }
    299329                        // If we can't lock it retry
    300330                } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
     
    332362                processor * const proc = kernelTLS().this_processor;
    333363                const int start = map.self * READYQ_SHARD_FACTOR;
     364                const unsigned long long ctsc = rdtscl();
    334365
    335366                // Did we already have a help target
    336367                if(proc->rdq.target == -1u) {
    337                         // if We don't have a
    338                         unsigned long long min = ts(lanes.data[start]);
     368                        unsigned long long max = 0;
    339369                        for(i; READYQ_SHARD_FACTOR) {
    340                                 unsigned long long tsc = ts(lanes.data[start + i]);
    341                                 if(tsc < min) min = tsc;
    342                         }
    343                         proc->rdq.cutoff = min;
    344 
     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;
    345374                        /* paranoid */ verify(lanes.count < 65536); // The following code assumes max 65536 cores.
    346375                        /* paranoid */ verify(map.count < 65536); // The following code assumes max 65536 cores.
    347376
    348                         if(0 == (__tls_rand() % 10_000)) {
     377                        if(0 == (__tls_rand() % 100)) {
    349378                                proc->rdq.target = __tls_rand() % lanes.count;
    350379                        } else {
     
    358387                }
    359388                else {
    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;
     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;
    362395                        {
    363396                                unsigned target = proc->rdq.target;
    364397                                proc->rdq.target = -1u;
    365                                 if(lanes.tscs[target].tv < cutoff && ts(lanes.data[target]) < cutoff) {
     398                                lanes.help[target / READYQ_SHARD_FACTOR].tri++;
     399                                if(moving_average(ctsc - lanes.tscs[target].tv, lanes.tscs[target].ma) > cutoff) {
    366400                                        thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
    367401                                        proc->rdq.last = target;
    368402                                        if(t) return t;
     403                                        else proc->rdq.target = -1u;
    369404                                }
     405                                else proc->rdq.target = -1u;
    370406                        }
    371407
     
    428464        }
    429465
    430         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {
     466        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
    431467                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
    432468
    433                 const bool external = !push_local || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
     469                const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
    434470                /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count );
    435471
     
    515551#endif
    516552#if defined(USE_WORK_STEALING)
    517         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, bool push_local) with (cltr->ready_queue) {
     553        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
    518554                __cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
    519555
    520556                // #define USE_PREFERRED
    521557                #if !defined(USE_PREFERRED)
    522                 const bool external = !push_local || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
     558                const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || (cltr != kernelTLS().this_processor->cltr);
    523559                /* paranoid */ verify(external || kernelTLS().this_processor->rdq.id < lanes.count );
    524560                #else
    525561                        unsigned preferred = thrd->preferred;
    526                         const bool external = push_local || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;
     562                        const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;
    527563                        /* paranoid */ verifyf(external || preferred < lanes.count, "Invalid preferred queue %u for %u lanes", preferred, lanes.count );
    528564
     
    645681        // Actually pop the list
    646682        struct thread$ * thrd;
     683        unsigned long long tsc_before = ts(lane);
    647684        unsigned long long tsv;
    648685        [thrd, tsv] = pop(lane);
     
    658695        __STATS( stats.success++; )
    659696
    660         #if defined(USE_WORK_STEALING)
     697        #if defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
     698                unsigned long long now = rdtscl();
    661699                lanes.tscs[w].tv = tsv;
     700                lanes.tscs[w].ma = moving_average(now > tsc_before ? now - tsc_before : 0, lanes.tscs[w].ma);
    662701        #endif
    663702
    664         thrd->preferred = w;
     703        #if defined(USE_CPU_WORK_STEALING)
     704                thrd->preferred = w / READYQ_SHARD_FACTOR;
     705        #else
     706                thrd->preferred = w;
     707        #endif
    665708
    666709        // return the popped thread
     
    688731
    689732//-----------------------------------------------------------------------
     733// get preferred ready for new thread
     734unsigned 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//-----------------------------------------------------------------------
    690754// Check that all the intrusive queues in the data structure are still consistent
    691755static void check( __ready_queue_t & q ) with (q) {
     
    915979        extern void __enable_interrupts_hard();
    916980
    917         void __kernel_raw_rseq_register  (void) {
     981        static void __kernel_raw_rseq_register  (void) {
    918982                /* paranoid */ verify( __cfaabi_rseq.cpu_id == RSEQ_CPU_ID_UNINITIALIZED );
    919983
     
    933997        }
    934998
    935         void __kernel_raw_rseq_unregister(void) {
     999        static void __kernel_raw_rseq_unregister(void) {
    9361000                /* paranoid */ verify( __cfaabi_rseq.cpu_id >= 0 );
    9371001
  • libcfa/src/concurrency/ready_subqueue.hfa

    rf95634e rb7fd9daf  
    9898
    9999        // Get the relevant nodes locally
    100         unsigned long long ts = this.anchor.ts;
    101100        thread$ * node = this.anchor.next;
    102101        this.anchor.next = node->link.next;
     
    116115        /* paranoid */ verify( node->link.ts   != 0  );
    117116        /* paranoid */ verify( this.anchor.ts  != 0  );
    118         return [node, ts];
     117        return [node, this.anchor.ts];
    119118}
    120119
  • libcfa/src/concurrency/stats.cfa

    rf95634e rb7fd9daf  
    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;
    5054                #endif
    5155
     
    104108                        tally_one( &cltr->io.calls.completed  , &proc->io.calls.completed   );
    105109                        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     );
    106114                #endif
    107115        }
     
    179187                                     | " - cmp " | eng3(io.calls.drain) | "/" | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)"
    180188                                     | " - " | 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);
    181192                                sstr | nl;
    182193                        }
  • libcfa/src/concurrency/stats.hfa

    rf95634e rb7fd9daf  
    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;
    104110                };
    105111        #endif
  • libcfa/src/concurrency/thread.cfa

    rf95634e rb7fd9daf  
    2525#include "invoke.h"
    2626
     27uint64_t thread_rand();
     28
    2729//-----------------------------------------------------------------------------
    2830// Thread ctors and dtors
     
    3436        preempted = __NO_PREEMPTION;
    3537        corctx_flag = false;
    36         disable_interrupts();
    37         last_cpu = __kernel_getcpu();
    38         enable_interrupts();
    3938        curr_cor = &self_cor;
    4039        self_mon.owner = &this;
     
    4443        link.next = 0p;
    4544        link.ts   = -1llu;
    46         preferred = -1u;
     45        preferred = ready_queue_new_preferred();
    4746        last_proc = 0p;
    4847        #if defined( __CFA_WITH_VERIFY__ )
     
    141140        /* paranoid */ verify( this_thrd->context.SP );
    142141
    143         schedule_thread$( this_thrd );
     142        schedule_thread$( this_thrd, UNPARK_LOCAL );
    144143        enable_interrupts();
    145144}
  • libcfa/src/device/cpu.cfa

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

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

    rf95634e rb7fd9daf  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jul 29 22:34:10 2021
    13 // Update Count     : 454
     12// Last Modified On : Sun Oct 10 11:23:05 2021
     13// Update Count     : 512
    1414//
    1515
     
    2828#define IO_MSG "I/O error: "
    2929
    30 void ?{}( 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;
     30// private
     31void ?{}( ofstream & os, void * file ) with( os ) {
     32        file$ = file;
     33        sepDefault$ = true;
     34        sepOnOff$ = false;
     35        nlOnOff$ = true;
     36        prt$ = false;
     37        sawNL$ = false;
    3838        sepSetCur$( os, sepGet( os ) );
    3939        sepSet( os, " " );
     
    4141} // ?{}
    4242
    43 // private
    44 bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
    45 void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; }
    46 void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
    47 const char * sepGetCur$( ofstream & os ) { return os.sepCur$; }
    48 void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
    49 bool getNL$( ofstream & os ) { return os.sawNL$; }
    50 void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; }
    51 bool getANL$( ofstream & os ) { return os.nlOnOff$; }
    52 bool getPrt$( ofstream & os ) { return os.prt$; }
    53 void setPrt$( ofstream & os, bool state ) { os.prt$ = state; }
     43inline bool sepPrt$( ofstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
     44inline void sepReset$( ofstream & os ) { os.sepOnOff$ = os.sepDefault$; }
     45inline void sepReset$( ofstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
     46inline const char * sepGetCur$( ofstream & os ) { return os.sepCur$; }
     47inline void sepSetCur$( ofstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
     48inline bool getNL$( ofstream & os ) { return os.sawNL$; }
     49inline void setNL$( ofstream & os, bool state ) { os.sawNL$ = state; }
     50inline bool getANL$( ofstream & os ) { return os.nlOnOff$; }
     51inline bool getPrt$( ofstream & os ) { return os.prt$; }
     52inline void setPrt$( ofstream & os, bool state ) { os.prt$ = state; }
     53
     54inline void lock( ofstream & os ) with( os ) {  lock( os.lock$ ); }
     55inline void unlock( ofstream & os ) { unlock( os.lock$ ); }
    5456
    5557// public
    5658void ?{}( ofstream & os ) { os.file$ = 0p; }
    57 
    58 void ?{}( ofstream & os, const char name[], const char mode[] ) {
    59         open( os, name, mode );
    60 } // ?{}
    61 
    62 void ?{}( ofstream & os, const char name[] ) {
    63         open( os, name, "w" );
    64 } // ?{}
    65 
    66 void ^?{}( ofstream & os ) {
    67         close( os );
    68 } // ^?{}
     59void ?{}( ofstream & os, const char name[], const char mode[] ) { open( os, name, mode ); }
     60void ?{}( ofstream & os, const char name[] ) { open( os, name, "w" ); }
     61void ^?{}( ofstream & os ) { close( os ); }
    6962
    7063void sepOn( ofstream & os ) { os.sepOnOff$ = ! getNL$( os ); }
     
    107100        if ( &os == &exit ) exit( EXIT_FAILURE );
    108101        if ( &os == &abort ) abort();
    109         if ( os.acquired$ ) { os.acquired$ = false; release( os ); }
    110102} // ends
    111103
    112 bool fail( ofstream & os ) {
    113         return os.file$ == 0 || ferror( (FILE *)(os.file$) );
    114 } // fail
    115 
    116 void clear( ofstream & os ) {
    117         clearerr( (FILE *)(os.file$) );
    118 } // clear
    119 
    120 int flush( ofstream & os ) {
    121         return fflush( (FILE *)(os.file$) );
    122 } // flush
     104bool fail( ofstream & os ) { return os.file$ == 0 || ferror( (FILE *)(os.file$) ); }
     105void clear( ofstream & os ) { clearerr( (FILE *)(os.file$) ); }
     106int flush( ofstream & os ) { return fflush( (FILE *)(os.file$) ); }
    123107
    124108void open( ofstream & os, const char name[], const char mode[] ) {
    125         FILE * file = fopen( name, mode );
    126         #ifdef __CFA_DEBUG__
     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
    127116        if ( file == 0p ) {
    128117                throw (Open_Failure){ os };
    129118                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    130119        } // if
    131         #endif // __CFA_DEBUG__
    132         (os){ file };
     120        (os){ file };                                                                           // initialize
    133121} // open
    134122
    135 void open( ofstream & os, const char name[] ) {
    136         open( os, name, "w" );
    137 } // open
    138 
    139 void 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 ) {
     123void open( ofstream & os, const char name[] ) { open( os, name, "w" ); }
     124
     125void 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 ) {
    144137                throw (Close_Failure){ os };
    145138                // abort | IO_MSG "close output" | nl | strerror( errno );
    146139        } // if
    147         os.file$ = 0p;
     140        file$ = 0p;                                                                                     // safety after close
    148141} // close
    149142
     
    164157        va_list args;
    165158        va_start( args, format );
    166         int len = vfprintf( (FILE *)(os.file$), format, args );
     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
    167167        if ( len == EOF ) {
    168168                if ( ferror( (FILE *)(os.file$) ) ) {
     
    177177} // fmt
    178178
    179 inline void acquire( ofstream & os ) {
    180         lock( os.lock$ );
    181         if ( ! os.acquired$ ) os.acquired$ = true;
    182         else unlock( os.lock$ );
    183 } // acquire
    184 
    185 inline void release( ofstream & os ) {
    186         unlock( os.lock$ );
    187 } // release
    188 
    189 void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); }
    190 void ^?{}( osacquire & acq ) { release( acq.os ); }
    191 
    192179static ofstream soutFile = { (FILE *)stdout };
    193180ofstream & sout = soutFile, & stdout = soutFile;
     
    207194        flush( os );
    208195        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
    214196} // nl
    215197
     
    219201
    220202// private
    221 void ?{}( ifstream & is, void * file ) {
    222         is.file$ = file;
    223         is.nlOnOff$ = false;
    224         is.acquired$ = false;
    225 } // ?{}
     203void ?{}( ifstream & is, void * file ) with( is ) {
     204        file$ = file;
     205        nlOnOff$ = false;
     206} // ?{}
     207
     208bool getANL$( ifstream & os ) { return os.nlOnOff$; }
     209
     210inline void lock( ifstream & os ) with( os ) { lock( os.lock$ ); }
     211inline void unlock( ifstream & os ) { unlock( os.lock$ ); }
    226212
    227213// public
    228214void ?{}( ifstream & is ) { is.file$ = 0p; }
    229 
    230 void ?{}( ifstream & is, const char name[], const char mode[] ) {
    231         open( is, name, mode );
    232 } // ?{}
    233 
    234 void ?{}( ifstream & is, const char name[] ) {
    235         open( is, name, "r" );
    236 } // ?{}
    237 
    238 void ^?{}( ifstream & is ) {
    239         close( is );
    240 } // ^?{}
     215void ?{}( ifstream & is, const char name[], const char mode[] ) { open( is, name, mode ); }
     216void ?{}( ifstream & is, const char name[] ) { open( is, name, "r" ); }
     217void ^?{}( ifstream & is ) { close( is ); }
     218
     219bool fail( ifstream & is ) { return is.file$ == 0p || ferror( (FILE *)(is.file$) ); }
     220void clear( ifstream & is ) { clearerr( (FILE *)(is.file$) ); }
    241221
    242222void nlOn( ifstream & os ) { os.nlOnOff$ = true; }
    243223void nlOff( ifstream & os ) { os.nlOnOff$ = false; }
    244 bool getANL( ifstream & os ) { return os.nlOnOff$; }
    245 
    246 bool fail( ifstream & is ) {
    247         return is.file$ == 0p || ferror( (FILE *)(is.file$) );
    248 } // fail
    249 
    250 void clear( ifstream & is ) {
    251         clearerr( (FILE *)(is.file$) );
    252 } // clear
    253 
    254 void ends( ifstream & is ) {
    255         if ( is.acquired$ ) { is.acquired$ = false; release( is ); }
    256 } // ends
    257 
    258 bool eof( ifstream & is ) {
    259         return feof( (FILE *)(is.file$) );
    260 } // eof
     224
     225void ends( ifstream & is ) {}
     226
     227bool eof( ifstream & is ) { return feof( (FILE *)(is.file$) ) != 0; }
    261228
    262229void open( ifstream & is, const char name[], const char mode[] ) {
    263         FILE * file = fopen( name, mode );
    264         #ifdef __CFA_DEBUG__
     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
    265237        if ( file == 0p ) {
    266238                throw (Open_Failure){ is };
    267239                // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
    268240        } // if
    269         #endif // __CFA_DEBUG__
    270         is.file$ = file;
     241        (is){ file };                                                                           // initialize
    271242} // open
    272243
    273 void open( ifstream & is, const char name[] ) {
    274         open( is, name, "r" );
    275 } // open
    276 
    277 void 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 ) {
     244void open( ifstream & is, const char name[] ) { open( is, name, "r" ); }
     245
     246void 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 ) {
    282258                throw (Close_Failure){ is };
    283259                // abort | IO_MSG "close input" | nl | strerror( errno );
    284260        } // if
    285         is.file$ = 0p;
     261        file$ = 0p;                                                                                     // safety after close
    286262} // close
    287263
     
    312288int fmt( ifstream & is, const char format[], ... ) {
    313289        va_list args;
    314 
    315290        va_start( args, format );
    316         int len = vfscanf( (FILE *)(is.file$), format, args );
     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
    317298        if ( len == EOF ) {
    318299                if ( ferror( (FILE *)(is.file$) ) ) {
     
    324305} // fmt
    325306
    326 inline void acquire( ifstream & is ) {
    327         lock( is.lock$ );
    328         if ( ! is.acquired$ ) is.acquired$ = true;
    329         else unlock( is.lock$ );
    330 } // acquire
    331 
    332 inline void release( ifstream & is ) {
    333         unlock( is.lock$ );
    334 } // release
    335 
    336 void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); }
    337 void ^?{}( isacquire & acq ) { release( acq.is ); }
    338 
    339307static ifstream sinFile = { (FILE *)stdin };
    340308ifstream & sin = sinFile, & stdin = sinFile;
     
    347315
    348316// exception I/O constructors
    349 void ?{}( Open_Failure & this, ofstream & ostream ) {
    350         this.virtual_table = &Open_Failure_vt;
    351         this.ostream = &ostream;
    352         this.tag = 1;
    353 } // ?{}
    354 
    355 void ?{}( Open_Failure & this, ifstream & istream ) {
    356         this.virtual_table = &Open_Failure_vt;
    357         this.istream = &istream;
    358         this.tag = 0;
     317void ?{}( Open_Failure & ex, ofstream & ostream ) with(ex) {
     318        virtual_table = &Open_Failure_vt;
     319        ostream = &ostream;
     320        tag = 1;
     321} // ?{}
     322
     323void ?{}( Open_Failure & ex, ifstream & istream ) with(ex) {
     324        virtual_table = &Open_Failure_vt;
     325        istream = &istream;
     326        tag = 0;
    359327} // ?{}
    360328
     
    363331
    364332// exception I/O constructors
    365 void ?{}( Close_Failure & this, ofstream & ostream ) {
    366         this.virtual_table = &Close_Failure_vt;
    367         this.ostream = &ostream;
    368         this.tag = 1;
    369 } // ?{}
    370 
    371 void ?{}( Close_Failure & this, ifstream & istream ) {
    372         this.virtual_table = &Close_Failure_vt;
    373         this.istream = &istream;
    374         this.tag = 0;
     333void ?{}( Close_Failure & ex, ofstream & ostream ) with(ex) {
     334        virtual_table = &Close_Failure_vt;
     335        ostream = &ostream;
     336        tag = 1;
     337} // ?{}
     338
     339void ?{}( Close_Failure & ex, ifstream & istream ) with(ex) {
     340        virtual_table = &Close_Failure_vt;
     341        istream = &istream;
     342        tag = 0;
    375343} // ?{}
    376344
     
    379347
    380348// exception I/O constructors
    381 void ?{}( Write_Failure & this, ofstream & ostream ) {
    382         this.virtual_table = &Write_Failure_vt;
    383         this.ostream = &ostream;
    384         this.tag = 1;
    385 } // ?{}
    386 
    387 void ?{}( Write_Failure & this, ifstream & istream ) {
    388         this.virtual_table = &Write_Failure_vt;
    389         this.istream = &istream;
    390         this.tag = 0;
     349void ?{}( Write_Failure & ex, ofstream & ostream ) with(ex) {
     350        virtual_table = &Write_Failure_vt;
     351        ostream = &ostream;
     352        tag = 1;
     353} // ?{}
     354
     355void ?{}( Write_Failure & ex, ifstream & istream ) with(ex) {
     356        virtual_table = &Write_Failure_vt;
     357        istream = &istream;
     358        tag = 0;
    391359} // ?{}
    392360
     
    395363
    396364// exception I/O constructors
    397 void ?{}( Read_Failure & this, ofstream & ostream ) {
    398         this.virtual_table = &Read_Failure_vt;
    399         this.ostream = &ostream;
    400         this.tag = 1;
    401 } // ?{}
    402 
    403 void ?{}( Read_Failure & this, ifstream & istream ) {
    404         this.virtual_table = &Read_Failure_vt;
    405         this.istream = &istream;
    406         this.tag = 0;
     365void ?{}( Read_Failure & ex, ofstream & ostream ) with(ex) {
     366        virtual_table = &Read_Failure_vt;
     367        ostream = &ostream;
     368        tag = 1;
     369} // ?{}
     370
     371void ?{}( Read_Failure & ex, ifstream & istream ) with(ex) {
     372        virtual_table = &Read_Failure_vt;
     373        istream = &istream;
     374        tag = 0;
    407375} // ?{}
    408376
  • libcfa/src/fstream.hfa

    rf95634e rb7fd9daf  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jul 28 07:35:50 2021
    13 // Update Count     : 234
     12// Last Modified On : Sun Oct 10 09:37:32 2021
     13// Update Count     : 243
    1414//
    1515
     
    3636        char tupleSeparator$[ofstream_sepSize];
    3737        multiple_acquisition_lock lock$;
    38         bool acquired$;
    3938}; // ofstream
    4039
     
    5251bool getPrt$( ofstream & );
    5352void setPrt$( ofstream &, bool );
     53
     54void lock( ofstream & );
     55void unlock( ofstream & );
    5456
    5557// public
     
    7577void open( ofstream &, const char name[] );
    7678void close( ofstream & );
     79
    7780ofstream & write( ofstream &, const char data[], size_t size );
    78 
    79 void acquire( ofstream & );
    80 void release( ofstream & );
    81 
    82 struct osacquire {
    83         ofstream & os;
    84 };
    85 void ?{}( osacquire & acq, ofstream & );
    86 void ^?{}( osacquire & acq );
    8781
    8882void ?{}( ofstream & );
     
    107101        bool nlOnOff$;
    108102        multiple_acquisition_lock lock$;
    109         bool acquired$;
    110103}; // ifstream
    111104
    112105// Satisfies istream
    113106
     107// private
     108bool getANL$( ifstream & );
     109
     110void lock( ifstream & );
     111void unlock( ifstream & );
     112
    114113// public
    115114void nlOn( ifstream & );
    116115void nlOff( ifstream & );
    117 bool getANL( ifstream & );
    118116void ends( ifstream & );
    119117int fmt( ifstream &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     
    125123void open( ifstream & is, const char name[] );
    126124void close( ifstream & is );
     125
    127126ifstream & read( ifstream & is, char data[], size_t size );
    128127ifstream & ungetc( ifstream & is, char c );
    129 
    130 void acquire( ifstream & is );
    131 void release( ifstream & is );
    132 
    133 struct isacquire {
    134         ifstream & is;
    135 };
    136 void ?{}( isacquire & acq, ifstream & is );
    137 void ^?{}( isacquire & acq );
    138128
    139129void ?{}( ifstream & is );
  • libcfa/src/heap.cfa

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

    rf95634e rb7fd9daf  
    9494} __cabi_libc;
    9595
     96int cfa_main_returned;
     97
    9698extern "C" {
    9799        void __cfaabi_interpose_startup( void ) {
    98100                const char *version = 0p;
     101                cfa_main_returned = 0;
    99102
    100103                preload_libgcc();
  • libcfa/src/iostream.cfa

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

    rf95634e rb7fd9daf  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Apr 28 20:37:56 2021
    13 // Update Count     : 401
     12// Last Modified On : Sun Oct 10 10:02:07 2021
     13// Update Count     : 407
    1414//
    1515
     
    5858        void close( ostype & );
    5959        ostype & write( ostype &, const char [], size_t );
    60         void acquire( ostype & );                                                       // concurrent access
    6160}; // ostream
    6261
     
    142141        ostype & nlOn( ostype & );
    143142        ostype & nlOff( ostype & );
    144 } // distribution
    145 
    146 forall( ostype & | ostream( ostype ) ) {
    147         ostype & acquire( ostype & );
    148143} // distribution
    149144
     
    296291
    297292trait basic_istream( istype & ) {
    298         bool getANL( istype & );                                                        // get scan newline (on/off)
     293        // private
     294        bool getANL$( istype & );                                                       // get scan newline (on/off)
     295        // public
    299296        void nlOn( istype & );                                                          // read newline
    300297        void nlOff( istype & );                                                         // scan newline
    301 
    302298        void ends( istype & os );                                                       // end of output statement
    303299        int fmt( istype &, const char format[], ... ) __attribute__(( format(scanf, 2, 3) ));
     
    312308        void close( istype & is );
    313309        istype & read( istype &, char [], size_t );
    314         void acquire( istype & );                                                       // concurrent access
    315310}; // istream
    316311
     
    379374} // distribution
    380375
    381 forall( istype & | istream( istype ) ) {
    382         istype & acquire( istype & );
    383 } // distribution
    384 
    385376// *********************************** manipulators ***********************************
    386377
  • libcfa/src/memory.cfa

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

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

    rf95634e rb7fd9daf  
    1010// Created On       : Thu Apr 22 22:24:35 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Apr 27 20:59:53 2021
    13 // Update Count     : 78
     12// Last Modified On : Sun Oct 10 16:13:20 2021
     13// Update Count     : 101
    1414//
    1515
    1616#include "strstream.hfa"
     17#include "fstream.hfa"                                                                  // abort
    1718
    1819#include <stdio.h>                                                                              // vsnprintf
     
    3031
    3132// private
    32 bool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
    33 void sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; }
    34 void sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
    35 const char * sepGetCur$( ostrstream & os ) { return os.sepCur$; }
    36 void sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
    37 bool getNL$( ostrstream & os ) { return os.sawNL$; }
    38 void setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; }
    39 bool getANL$( ostrstream & os ) { return os.nlOnOff$; }
    40 bool getPrt$( ostrstream & os ) { return os.prt$; }
    41 void setPrt$( ostrstream & os, bool state ) { os.prt$ = state; }
     33inline bool sepPrt$( ostrstream & os ) { setNL$( os, false ); return os.sepOnOff$; }
     34inline void sepReset$( ostrstream & os ) { os.sepOnOff$ = os.sepDefault$; }
     35inline void sepReset$( ostrstream & os, bool reset ) { os.sepDefault$ = reset; os.sepOnOff$ = os.sepDefault$; }
     36inline const char * sepGetCur$( ostrstream & os ) { return os.sepCur$; }
     37inline void sepSetCur$( ostrstream & os, const char sepCur[] ) { os.sepCur$ = sepCur; }
     38inline bool getNL$( ostrstream & os ) { return os.sawNL$; }
     39inline void setNL$( ostrstream & os, bool state ) { os.sawNL$ = state; }
     40inline bool getANL$( ostrstream & os ) { return os.nlOnOff$; }
     41inline bool getPrt$( ostrstream & os ) { return os.prt$; }
     42inline void setPrt$( ostrstream & os, bool state ) { os.prt$ = state; }
    4243
    4344// public
     
    128129// *********************************** istrstream ***********************************
    129130
     131// private
     132bool getANL$( istrstream & is ) { return is.nlOnOff$; }
    130133
    131134// public
     
    136139} // ?{}
    137140
    138 bool getANL( istrstream & is ) { return is.nlOnOff$; }
    139141void nlOn( istrstream & is ) { is.nlOnOff$ = true; }
    140142void nlOff( istrstream & is ) { is.nlOnOff$ = false; }
    141143
    142 void ends( istrstream & is ) {
    143 } // ends
     144void ends( istrstream & is ) {}
     145bool eof( istrstream & is ) { return false; }
    144146
    145 int eof( istrstream & is ) {
    146         return 0;
    147 } // eof
     147int 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
    148164
    149165istrstream &ungetc( istrstream & is, char c ) {
     
    154170} // ungetc
    155171
    156 int 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 
    170172// Local Variables: //
    171173// tab-width: 4 //
  • libcfa/src/strstream.hfa

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

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

    rf95634e rb7fd9daf  
    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.
    350351        template< typename T >
    351         void GuardValue( T& val ) {
     352        T& GuardValue( T& val ) {
    352353                at_cleanup( [ val ]( void * newVal ) {
    353354                        * static_cast< T * >( newVal ) = val;
    354355                }, static_cast< void * >( & val ) );
     356                return val;
    355357        }
    356358
     
    394396};
    395397
     398/// Used to get a pointer to the wrapping TranslationUnit.
     399struct 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
    396408}
    397409
  • src/AST/Pass.impl.hpp

    rf95634e rb7fd9daf  
    420420template< typename core_t >
    421421inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & visitor ) {
    422         return ast::accept_all( unit.decls, 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        }
    423429}
    424430
  • src/AST/Pass.proto.hpp

    rf95634e rb7fd9daf  
    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
    428442        template<typename core_t>
    429443        static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) {
  • src/AST/Stmt.hpp

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

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

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

    rf95634e rb7fd9daf  
    2222#include <string>                  // for operator<<
    2323
     24#include "AST/Decl.hpp"
     25#include "AST/Type.hpp"
     26#include "Common/PassVisitor.h"
    2427#include "Common/SemanticError.h"  // for SemanticError
    2528#include "CodeGen/GenType.h"       // for GenType
     
    2932
    3033namespace CodeGen {
     34
     35namespace {
     36
     37struct 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
    3152        bool FixMain::replace_main = false;
    32         std::unique_ptr<FunctionDecl> FixMain::main_signature = nullptr;
    3353
    3454        template<typename container>
     
    3757        }
    3858
    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         }
     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;
    4664
    47         void FixMain::fix(std::ostream &os, const char* bootloader_filename) {
    4865                if( main_signature ) {
    4966                        os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return ";
    50                         main_signature->mangleName = SymTab::Mangler::mangle(main_signature.get());
     67                        main_signature->mangleName = SymTab::Mangler::mangle(main_signature);
    5168
    5269                        os << main_signature->get_scopedMangleName() << "(";
     
    6582                }
    6683        }
     84
     85namespace {
     86
     87ObjectDecl * signedIntObj() {
     88        return new ObjectDecl(
     89                "", Type::StorageClasses(), LinkageSpec::Cforall, 0,
     90                new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr );
     91}
     92
     93ObjectDecl * 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
     102std::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
     109std::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
     115std::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
     123bool 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
     139bool 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
     146bool 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
    67153};
  • src/CodeGen/FixMain.h

    rf95634e rb7fd9daf  
    99// Author           : Thierry Delisle
    1010// Created On       : Thr Jan 12 14:11:09 2017
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Feb 16 03:24:32 2020
    13 // Update Count     : 5
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Fri Oct 29 16:20:00 2021
     13// Update Count     : 8
    1414//
    1515
     
    1818#include <iosfwd>
    1919#include <memory>
     20#include <list>
    2021
    2122#include "SynTree/LinkageSpec.h"
    2223
     24class Declaration;
    2325class FunctionDecl;
     26namespace ast {
     27        class FunctionDecl;
     28}
    2429
    2530namespace 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                 }
    3531
    36                 static void registerMain(FunctionDecl* val);
     32class FixMain {
     33public :
     34        static inline LinkageSpec::Spec mainLinkage() {
     35                return replace_main ? LinkageSpec::Cforall : LinkageSpec::C;
     36        }
    3737
    38                 static void fix(std::ostream &os, const char* bootloader_filename);
     38        static inline void setReplaceMain(bool val) {
     39                replace_main = val;
     40        }
    3941
    40           private:
    41                 static bool replace_main;
    42                 static std::unique_ptr<FunctionDecl> main_signature;
    43         };
     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
     48private:
     49        static bool replace_main;
     50};
     51
    4452} // namespace CodeGen
  • src/CodeGen/FixNames.cc

    rf95634e rb7fd9daf  
    99// Author           : Richard C. Bilson
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Dec 13 23:39:14 2019
    13 // Update Count     : 21
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Fri Oct 29 15:49:00 2021
     13// Update Count     : 23
    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"
    2124#include "Common/PassVisitor.h"
    2225#include "Common/SemanticError.h"  // for SemanticError
     
    4649        };
    4750
    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 
    9751        void fixNames( std::list< Declaration* > & translationUnit ) {
    9852                PassVisitor<FixNames> fixer;
     
    11872                fixDWT( functionDecl );
    11973
    120                 if(is_main( SymTab::Mangler::mangle(functionDecl, true, true) )) {
     74                if ( FixMain::isMain( functionDecl ) ) {
    12175                        int nargs = functionDecl->get_functionType()->get_parameters().size();
    12276                        if( !(nargs == 0 || nargs == 2 || nargs == 3) ) {
     
    12478                        }
    12579                        functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) );
    126                         CodeGen::FixMain::registerMain( functionDecl );
    12780                }
    12881        }
     
    13285                GuardAction( [this](){ scopeLevel--; } );
    13386        }
     87
     88/// Does work with the main function and scopeLevels.
     89class 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        }
     96public:
     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
     135void fixNames( ast::TranslationUnit & translationUnit ) {
     136        ast::Pass<FixNames_new>::run( translationUnit );
     137}
     138
    134139} // namespace CodeGen
    135140
  • src/CodeGen/FixNames.h

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

    rf95634e rb7fd9daf  
    156156                /// number of counting bins for linkages
    157157                static const unsigned n_named_specs = 8;
    158                 /// map from total number of specs to bins
    159                 static const unsigned ind_for_linkage[16];
     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                }
    160171
    161172                Stats for_linkage[n_named_specs];            ///< Stores separate stats per linkage
     
    366377                        const std::string& mangleName = decl->get_mangleName().empty() ? decl->name : decl->get_mangleName();
    367378                        if ( seen_names.insert( mangleName ).second ) {
    368                                 Stats& stats = for_linkage[ ind_for_linkage[ decl->linkage ] ];
     379                                Stats& stats = for_linkage[ linkage_index( decl->linkage ) ];
    369380
    370381                                ++stats.n_decls;
     
    527538        };
    528539
    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 
    532540        void printDeclStats( std::list< Declaration * > &translationUnit ) {
    533541                PassVisitor<DeclStats> stats;
  • src/Common/module.mk

    rf95634e rb7fd9daf  
    2222      Common/CompilerError.h \
    2323      Common/Debug.h \
     24      Common/DeclStats.hpp \
     25      Common/DeclStats.cpp \
    2426      Common/ErrorObjects.h \
    2527      Common/Eval.cc \
     
    3335      Common/PassVisitor.proto.h \
    3436      Common/PersistentMap.h \
     37      Common/ResolvProtoDump.hpp \
     38      Common/ResolvProtoDump.cpp \
    3539      Common/ScopedMap.h \
    3640      Common/SemanticError.cc \
  • src/Concurrency/Keywords.cc

    rf95634e rb7fd9daf  
    9393                ObjectDecl * addField( StructDecl * );
    9494                void addRoutines( ObjectDecl *, FunctionDecl * );
     95                void addLockUnlockRoutines( StructDecl * );
    9596
    9697                virtual bool is_target( StructDecl * decl ) = 0;
     
    322323                StructDecl* dtor_guard_decl = nullptr;
    323324                StructDecl* thread_guard_decl = nullptr;
     325                StructDecl* lock_guard_decl = nullptr;
    324326
    325327                static std::unique_ptr< Type > generic_func;
     
    463465        }
    464466
    465 
    466467        void ConcurrentSueKeyword::handle( StructDecl * decl ) {
    467468                if( ! decl->body ) return;
     
    479480                FunctionDecl * func = forwardDeclare( decl );
    480481                ObjectDecl * field = addField( decl );
     482
     483                // add get_.* routine
    481484                addRoutines( field, func );
     485                // add lock/unlock routines to monitors for use by mutex stmt
     486                addLockUnlockRoutines( decl );
    482487        }
    483488
     
    612617        }
    613618
     619        // This function adds the get_.* routine body for coroutines, monitors etc
     620        //              after their corresponding struct has been made
    614621        void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) {
    615622                CompoundStmt * statement = new CompoundStmt();
     
    634641
    635642                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 );
    636751        }
    637752
     
    9371052                        assert( !thread_guard_decl );
    9381053                        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;
    9391058                }
    9401059        }
     
    10811200                                new PointerType(
    10821201                                        noQualifiers,
    1083                                         new StructInstType(
    1084                                                 noQualifiers,
    1085                                                 monitor_decl
     1202                                        //new TypeofType( noQualifiers, args.front()->clone() )
     1203                                        new TypeofType( noQualifiers, new UntypedExpr(
     1204                                                        new NameExpr( "__get_type" ),
     1205                                                        { args.front()->clone() }
     1206                                                )
    10861207                                        )
    10871208                                ),
     
    10931214                                map_range < std::list<Initializer*> > ( args, [](Expression * var ){
    10941215                                        return new SingleInit( new UntypedExpr(
    1095                                                 new NameExpr( "get_monitor" ),
    1096                                                 { var }
     1216                                                        new NameExpr( "__get_ptr" ),
     1217                                                        { var }
    10971218                                        ) );
     1219                                        //return new SingleInit( new AddressExpr( var ) );
    10981220                                })
    10991221                        )
    11001222                );
     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 ) ;
    11011234
    11021235                // in reverse order :
     
    11081241                                LinkageSpec::Cforall,
    11091242                                nullptr,
    1110                                 new StructInstType(
    1111                                         noQualifiers,
    1112                                         guard_decl
    1113                                 ),
     1243                                lock_guard_struct,
    11141244                                new ListInit(
    11151245                                        {
  • src/ControlStruct/ExceptTranslate.cc

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

    rf95634e rb7fd9daf  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // ExceptTranslate.h --
     7// ExceptTranslate.h -- Conversion of exception control flow structures.
    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 : Tus May 19 11:47:00 2020
    13 // Update Count     : 5
     12// Last Modified On : Mon Nov  8 11:43:00 2020
     13// Update Count     : 6
    1414//
    1515
     
    1919
    2020class Declaration;
     21namespace ast {
     22        class TranslationUnit;
     23}
    2124
    2225namespace ControlStruct {
    2326        void translateThrows( std::list< Declaration *> & translationUnit );
     27        void translateThrows( ast::TranslationUnit & transUnit );
    2428        /* Replaces all throw & throwResume statements with function calls.
    2529         * These still need to be resolved, so call this before the reslover.
  • src/ControlStruct/LabelGenerator.cc

    rf95634e rb7fd9daf  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Mar 11 22:23:20 2019
    13 // Update Count     : 15
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Nov  8 10:18:00 2021
     13// Update Count     : 17
    1414//
    1515
     
    1919
    2020#include "LabelGenerator.h"
     21
     22#include "AST/Attribute.hpp"
     23#include "AST/Label.hpp"
     24#include "AST/Stmt.hpp"
    2125#include "SynTree/Attribute.h"  // for Attribute
    2226#include "SynTree/Label.h"      // for Label, operator<<
     
    2428
    2529namespace ControlStruct {
    26         LabelGenerator * LabelGenerator::labelGenerator = 0;
     30
     31int LabelGenerator::current = 0;
     32LabelGenerator * LabelGenerator::labelGenerator = nullptr;
    2733
    2834        LabelGenerator * LabelGenerator::getGenerator() {
     
    4349                return l;
    4450        }
     51
     52ast::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
    4566} // namespace ControlStruct
    4667
  • src/ControlStruct/LabelGenerator.h

    rf95634e rb7fd9daf  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Jul 22 09:20:14 2017
    13 // Update Count     : 6
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Nov  8 10:16:00 2021
     13// Update Count     : 8
    1414//
    1515
     
    2121
    2222class Statement;
     23namespace ast {
     24        class Stmt;
     25        class Label;
     26}
    2327
    2428namespace ControlStruct {
    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         };
     29
     30class LabelGenerator {
     31        static int current;
     32        static LabelGenerator *labelGenerator;
     33protected:
     34        LabelGenerator() {}
     35public:
     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
    3743} // namespace ControlStruct
    3844
  • src/ControlStruct/module.mk

    rf95634e rb7fd9daf  
    1818        ControlStruct/ExceptDecl.cc \
    1919        ControlStruct/ExceptDecl.h \
     20        ControlStruct/FixLabels.cpp \
     21        ControlStruct/FixLabels.hpp \
    2022        ControlStruct/ForExprMutator.cc \
    2123        ControlStruct/ForExprMutator.h \
     
    2628        ControlStruct/MLEMutator.cc \
    2729        ControlStruct/MLEMutator.h \
     30        ControlStruct/MultiLevelExit.cpp \
     31        ControlStruct/MultiLevelExit.hpp \
    2832        ControlStruct/Mutate.cc \
    2933        ControlStruct/Mutate.h
    3034
    31 SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h
     35SRC += $(SRC_CONTROLSTRUCT) \
     36        ControlStruct/ExceptTranslateNew.cpp \
     37        ControlStruct/ExceptTranslate.cc \
     38        ControlStruct/ExceptTranslate.h
     39
    3240SRCDEMANGLE += $(SRC_CONTROLSTRUCT)
    3341
  • src/InitTweak/GenInit.cc

    rf95634e rb7fd9daf  
    99// Author           : Rob Schluntz
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Dec 13 23:15:10 2019
    13 // Update Count     : 184
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Mon Oct 25 13:53:00 2021
     13// Update Count     : 186
    1414//
    1515#include "GenInit.h"
     
    2424#include "AST/Decl.hpp"
    2525#include "AST/Init.hpp"
     26#include "AST/Pass.hpp"
    2627#include "AST/Node.hpp"
    2728#include "AST/Stmt.hpp"
     
    294295        }
    295296
     297namespace {
     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
    296426        void CtorDtor::generateCtorDtor( std::list< Declaration * > & translationUnit ) {
    297427                PassVisitor<CtorDtor> ctordtor;
  • src/InitTweak/GenInit.h

    rf95634e rb7fd9daf  
    99// Author           : Rodolfo G. Esteves
    1010// Created On       : Mon May 18 07:44:20 2015
    11 // Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Jul 22 09:31:19 2017
    13 // Update Count     : 4
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Fri Oct 22 16:08:00 2021
     13// Update Count     : 6
    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 );
    2930
    3031        /// Converts return statements into copy constructor calls on the hidden return variable
  • src/MakeLibCfa.h

    rf95634e rb7fd9daf  
    1919
    2020class Declaration;
     21namespace ast {
     22        struct TranslationUnit;
     23}
    2124
    2225namespace LibCfa {
    2326        void makeLibCfa( std::list< Declaration* > &prelude );
     27        void makeLibCfa( ast::TranslationUnit & translationUnit );
    2428} // namespace LibCfa
    2529
  • src/Makefile.am

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

    rf95634e rb7fd9daf  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Jul 20 22:03:04 2021
    13 // Update Count     : 5031
     12// Last Modified On : Fri Oct 15 09:20:17 2021
     13// Update Count     : 5163
    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         //    i.e., u"a" U"b" L"c" is disallowed.
     71        //    e.g., 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 ) );
    187188        return new ForCtrl(
    188189                distAttr( DeclarationNode::newTypeof( type, true ), DeclarationNode::newName( index )->addInitializer( new InitializerNode( start ) ) ),
     
    309310%token ATassign                                                                                 // @=
    310311
    311 %type<tok> identifier
    312 %type<tok> identifier_or_type_name  attr_name
     312%type<tok> identifier                                   identifier_at                           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                     default_initializer_opt
     328%type<en> argument_expression_list_opt  argument_expression_list        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
     562identifier_at:
     563        identifier
    560564        | '@'                                                                                           // CFA
    561565                { Token tok = { new string( DeclarationNode::anonymous.newName() ), yylval.tok.loc }; $$ = tok; }
     
    692696        // empty
    693697                { $$ = nullptr; }
    694         | argument_expression
     698        | argument_expression_list
     699        ;
     700
     701argument_expression_list:
     702        argument_expression
    695703        | argument_expression_list_opt ',' argument_expression
    696704                { $$ = (ExpressionNode *)($1->set_last( $3 )); }
     
    730738        | FLOATINGconstant fraction_constants_opt
    731739                { $$ = new ExpressionNode( build_field_name_fraction_constants( build_field_name_FLOATINGconstant( *$1 ), $2 ) ); }
    732         | identifier fraction_constants_opt
     740        | identifier_at fraction_constants_opt                          // CFA, allow anonymous fields
    733741                {
    734742                        $$ = new ExpressionNode( build_field_name_fraction_constants( build_varref( $1 ), $2 ) );
     
    10831091        comma_expression_opt ';'
    10841092                { $$ = 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; }
    10851096        ;
    10861097
     
    11811192
    11821193iteration_statement:
    1183         WHILE '(' push if_control_expression ')' statement pop
    1184                 { $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); }
    1185         | WHILE '(' ')' statement                                                       // CFA => while ( 1 )
     1194        WHILE '(' ')' statement                                                         // CFA => while ( 1 )
    11861195                { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
    1187         | DO statement WHILE '(' comma_expression ')' ';'
    1188                 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
     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; }
    11891200        | DO statement WHILE '(' ')' ';'                                        // CFA => do while( 1 )
    11901201                { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
    1191         | FOR '(' push for_control_expression_list ')' statement pop
    1192                 { $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); }
     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; }
    11931206        | FOR '(' ')' statement                                                         // CFA => for ( ;; )
    11941207                { $$ = 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; }
    11951212        ;
    11961213
     
    13381355with_statement:
    13391356        WITH '(' tuple_expression_list ')' statement
    1340                 {
    1341                         $$ = new StatementNode( build_with( $3, $5 ) );
    1342                 }
     1357                { $$ = new StatementNode( build_with( $3, $5 ) ); }
    13431358        ;
    13441359
    13451360// If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex".
    13461361mutex_statement:
    1347         MUTEX '(' argument_expression_list_opt ')' statement
     1362        MUTEX '(' argument_expression_list ')' statement
    13481363                { $$ = new StatementNode( build_mutex( $3, $5 ) ); }
    13491364        ;
     
    24452460        | simple_assignment_operator initializer        { $$ = $1 == OperKinds::Assign ? $2 : $2->set_maybeConstructed( false ); }
    24462461        | '=' VOID                                                                      { $$ = new InitializerNode( true ); }
     2462        | '{' initializer_list_opt comma_opt '}'        { $$ = new InitializerNode( $2, true ); }
    24472463        ;
    24482464
     
    24582474        | designation initializer                                       { $$ = $2->set_designators( $1 ); }
    24592475        | initializer_list_opt ',' initializer          { $$ = (InitializerNode *)( $1->set_last( $3 ) ); }
    2460         | initializer_list_opt ',' designation initializer
    2461                 { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
     2476        | initializer_list_opt ',' designation initializer { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
    24622477        ;
    24632478
     
    24742489designation:
    24752490        designator_list ':'                                                                     // C99, CFA uses ":" instead of "="
    2476         | identifier ':'                                                                        // GCC, field name
     2491        | identifier_at ':'                                                                     // GCC, field name
    24772492                { $$ = new ExpressionNode( build_varref( $1 ) ); }
    24782493        ;
     
    24862501
    24872502designator:
    2488         '.' identifier                                                                          // C99, field name
     2503        '.' identifier_at                                                                       // C99, field name
    24892504                { $$ = new ExpressionNode( build_varref( $2 ) ); }
    24902505        | '[' push assignment_expression pop ']'                        // C99, single array element
     
    29182933
    29192934paren_identifier:
    2920         identifier
     2935        identifier_at
    29212936                { $$ = DeclarationNode::newName( $1 ); }
    29222937        | '(' paren_identifier ')'                                                      // redundant parenthesis
  • src/ResolvExpr/module.mk

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

    rf95634e rb7fd9daf  
    88//
    99// Author           : Henry Xue
    10 // Created On       : Wed Aug 18 12:54:02 2021
     10// Created On       : Mon Aug 23 15:36:09 2021
    1111// Last Modified By : Henry Xue
    12 // Last Modified On : Wed Aug 18 12:54:02 2021
     12// Last Modified On : Mon Aug 23 15:36:09 2021
    1313// Update Count     : 1
    1414//
    15 
    16 // Currently not working due to unresolved issues with UniqueExpr
    1715
    1816#include "Tuples.h"
     
    2018namespace Tuples {
    2119namespace {
     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        };
    2224        struct UniqueExprExpander final : public ast::WithDeclsToAdd<> {
    2325                const ast::Expr * postvisit( const ast::UniqueExpr * unqExpr );
    2426                std::map< int, ast::ptr<ast::Expr> > decls; // not vector, because order added may not be increasing order
    2527        };
     28} // namespace
     29
     30void expandMemberTuples( ast::TranslationUnit & translationUnit ) {
     31        ast::Pass< MemberTupleExpander >::run( translationUnit );
     32}
     33
     34namespace {
     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        }
    2671} // namespace
    2772
  • src/Tuples/Tuples.h

    rf95634e rb7fd9daf  
    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 : Tue Jun 18 09:36:00 2019
    13 // Update Count     : 18
     11// Last Modified By : Henry Xue
     12// Last Modified On : Mon Aug 23 15:36:09 2021
     13// Update Count     : 19
    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 );
    4142
    4243        /// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
  • src/Tuples/module.mk

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

    rf95634e rb7fd9daf  
    99// Author           : Peter Buhr and Rob Schluntz
    1010// Created On       : Fri May 15 23:12:02 2015
    11 // Last Modified By : Henry Xue
    12 // Last Modified On : Tue Jul 20 04:27:35 2021
    13 // Update Count     : 658
     11// Last Modified By : Andrew Beach
     12// Last Modified On : Tue Nov  9 11:10:00 2021
     13// Update Count     : 657
    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
    4547#include "Common/Stats.h"
    4648#include "Common/PassVisitor.h"
     
    5153#include "ControlStruct/ExceptDecl.h"       // for translateExcept
    5254#include "ControlStruct/ExceptTranslate.h"  // for translateEHM
     55#include "ControlStruct/FixLabels.hpp"      // for fixLabels
    5356#include "ControlStruct/Mutate.h"           // for mutate
    5457#include "GenPoly/Box.h"                    // for box
     
    6265#include "Parser/TypedefTable.h"            // for TypedefTable
    6366#include "ResolvExpr/AlternativePrinter.h"  // for AlternativePrinter
     67#include "ResolvExpr/CandidatePrinter.hpp"  // for printCandidates
    6468#include "ResolvExpr/Resolver.h"            // for resolve
    6569#include "SymTab/Validate.h"                // for validate
     
    315319                // add the assignment statement after the initialization of a type parameter
    316320                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
    353321
    354322                CodeTools::fillLocations( translationUnit );
    355 
    356                 if ( resolvprotop ) {
    357                         CodeTools::dumpAsResolvProto( translationUnit );
    358                         return EXIT_SUCCESS;
    359                 } // if
    360323
    361324                if( useNewAST ) {
     
    365328                        }
    366329                        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
    367373                        PASS( "Resolve", ResolvExpr::resolve( transUnit ) );
    368374                        if ( exprp ) {
     
    385391                        translationUnit = convert( move( transUnit ) );
    386392                } 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
    387438                        PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
    388439                        if ( exprp ) {
     
    447498                PASS( "Code Gen", CodeGen::generate( translationUnit, *output, ! genproto, prettycodegenp, true, linemarks ) );
    448499
    449                 CodeGen::FixMain::fix( *output, (PreludeDirector + "/bootloader.c").c_str() );
     500                CodeGen::FixMain::fix( translationUnit, *output,
     501                                (PreludeDirector + "/bootloader.c").c_str() );
    450502                if ( output != &cout ) {
    451503                        delete output;
  • tests/.expect/declarationSpecifier.x64.txt

    rf95634e rb7fd9daf  
    11321132char **_X13cfa_args_argvPPc_1;
    11331133char **_X13cfa_args_envpPPc_1;
    1134 signed int _X17cfa_main_returnedi_1 = ((signed int )0);
     1134__attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
    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     {
    1152         ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     1151    if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
     1152        {
     1153            ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     1154        }
     1155
    11531156    }
    11541157
  • tests/.expect/declarationSpecifier.x86.txt

    rf95634e rb7fd9daf  
    11321132char **_X13cfa_args_argvPPc_1;
    11331133char **_X13cfa_args_envpPPc_1;
    1134 signed int _X17cfa_main_returnedi_1 = ((signed int )0);
     1134__attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
    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     {
    1152         ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     1151    if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
     1152        {
     1153            ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     1154        }
     1155
    11531156    }
    11541157
  • tests/.expect/gccExtensions.x64.txt

    rf95634e rb7fd9daf  
    324324char **_X13cfa_args_argvPPc_1;
    325325char **_X13cfa_args_envpPPc_1;
    326 signed int _X17cfa_main_returnedi_1 = ((signed int )0);
     326__attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
    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     {
    344         ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     343    if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
     344        {
     345            ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     346        }
     347
    345348    }
    346349
  • tests/.expect/gccExtensions.x86.txt

    rf95634e rb7fd9daf  
    302302char **_X13cfa_args_argvPPc_1;
    303303char **_X13cfa_args_envpPPc_1;
    304 signed int _X17cfa_main_returnedi_1 = ((signed int )0);
     304__attribute__ ((weak)) extern signed int _X17cfa_main_returnedi_1;
    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     {
    322         ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     321    if ( ((&_X17cfa_main_returnedi_1)!=((signed int *)0)) ) {
     322        {
     323            ((void)(_X17cfa_main_returnedi_1=((signed int )1)));
     324        }
     325
    323326    }
    324327
  • tests/Makefile.am

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

    rf95634e rb7fd9daf  
    1 #include <mutex_stmt_locks.hfa>
     1#include <mutex_stmt.hfa>
    22#include <locks.hfa>
    33
    44const unsigned int num_times = 10000;
    55
    6 owner_lock m1, m2, m3, m4, m5;
     6single_acquisition_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

    rf95634e rb7fd9daf  
    11#include <monitor.hfa>
     2#include <mutex_stmt.hfa>
    23#include <stdio.h>
    34#include <stdlib.hfa>
     
    1314bool insideFlag = false;
    1415int count = 0;
     16bool startFlag = false;
    1517
    1618void main( T_Mutex & this ) {
  • tests/concurrent/semaphore.cfa

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

    rf95634e rb7fd9daf  
    11#include <fstream.hfa>
    22#include <thread.hfa>
     3#include <mutex_stmt.hfa>
    34#include <time.hfa>
    45
     
    2930
    3031int main() {
    31         sout | acquire | "start";
     32        mutex( sout ) sout | "start";
    3233        {
    3334                slow_sleeper slow;
     
    3637                yield();
    3738        }
    38         sout | acquire | "done";
     39        mutex( sout ) sout | "done";
    3940}
    4041
  • tests/exceptions/.expect/type-check.txt

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

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

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

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

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

    rf95634e rb7fd9daf  
    44#include <exception.hfa>
    55
    6 EHM_EXCEPTION(log_message)(
     6exception 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 EHM_EXCEPTION(jump)();
     34exception jump {};
    3535void defaultTerminationHandler(jump &) {
    3636        printf("jump default handler.\n");
    3737}
    3838
    39 EHM_VIRTUAL_TABLE(jump, jump_vt);
     39vtable(jump) jump_vt;
    4040
    4141void jump_test(void) {
     
    4848}
    4949
    50 EHM_EXCEPTION(first)();
    51 EHM_VIRTUAL_TABLE(first, first_vt);
     50exception first {};
     51vtable(first) first_vt;
    5252
    53 EHM_EXCEPTION(unhandled_exception)();
    54 EHM_VIRTUAL_TABLE(unhandled_exception, unhandled_vt);
     53exception unhandled_exception {};
     54vtable(unhandled_exception) unhandled_vt;
    5555
    5656void unhandled_test(void) {
     
    6969}
    7070
    71 EHM_EXCEPTION(second)();
    72 EHM_VIRTUAL_TABLE(second, second_vt);
     71exception second {};
     72vtable(second) second_vt;
    7373
    7474void cross_test(void) {
  • tests/exceptions/finally.cfa

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

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

    rf95634e rb7fd9daf  
    11// Testing polymophic exception types.
    22
    3 #include <exception.hfa>
     3forall(T &) exception proxy {};
    44
    5 EHM_FORALL_EXCEPTION(proxy, (T&), (T))();
    6 
    7 EHM_FORALL_VIRTUAL_TABLE(proxy, (int), proxy_int);
    8 EHM_FORALL_VIRTUAL_TABLE(proxy, (char), proxy_char);
     5vtable(proxy(int)) proxy_int;
     6vtable(proxy(char)) proxy_char;
    97
    108void proxy_test(void) {
     
    3331}
    3432
    35 EHM_FORALL_EXCEPTION(cell, (T), (T))(
     33forall(T) exception cell {
    3634        T data;
    37 );
     35};
    3836
    39 EHM_FORALL_VIRTUAL_TABLE(cell, (int), int_cell);
    40 EHM_FORALL_VIRTUAL_TABLE(cell, (char), char_cell);
    41 EHM_FORALL_VIRTUAL_TABLE(cell, (bool), bool_cell);
     37vtable(cell(int)) int_cell;
     38vtable(cell(char)) char_cell;
     39vtable(cell(bool)) bool_cell;
    4240
    4341void cell_test(void) {
  • tests/exceptions/resume.cfa

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

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

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

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

    rf95634e rb7fd9daf  
    1010// Created On       : Mon Mar  1 18:40:09 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Apr 27 11:49:34 2021
    13 // Update Count     : 18
     12// Last Modified On : Wed Oct  6 18:04:58 2021
     13// Update Count     : 72
    1414//
    1515
    1616#include <fstream.hfa>
    1717#include <thread.hfa>
     18#include <mutex_stmt.hfa>
    1819
    1920thread T {};
     
    2122        // output from parallel threads should not be scrambled
    2223
    23         for ( 100 ) {                                                                           // local protection
    24                 sout | acquire | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
     24        for ( 100 ) {                                                                           // expression protection
     25                mutex(sout) sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
    2526        }
    26         {                                                                                                       // global protection (RAII)
    27                 osacquire acq = { sout };
     27        mutex( sout ) {                                                                         // statement protection
    2828                for ( 100 ) {
    2929                        sout | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
     
    3131        }
    3232        {                                                                                                       // duplicate protection demonstrating recursive lock
    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;
     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                        }
    3846                }
    3947        }
     
    4250
    4351        int 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;
     52        for ( 100 ) {                                                                           // expression protection
     53                mutex(sin) sin | a | b | c | d | e | f | g | h | i;
    4654        }
    47         {                                                                                                       // global protection (RAII)
    48                 isacquire acq = { sin };
     55        mutex( sin ) {                                                                          // statement protection
    4956                for ( 100 ) {
    5057                        sin  | a | b | c | d | e | f | g | h | i;
     
    5259        }
    5360        {                                                                                                       // duplicate protection demonstrating recursive lock
    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;
     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                        }
    5974                }
    6075        }
  • tests/linking/io-acquire.cfa

    rf95634e rb7fd9daf  
    1717#include <fstream.hfa>
    1818#include <stdlib.hfa>
     19#include <mutex_stmt.hfa>
    1920
    2021int main() {
    2122        int i;
    2223        if(threading_enabled()) {
    23                 stdout | acquire | "YES";
     24                mutex( stdout ) stdout | "YES";
    2425                stdin | i;
    2526        } else {
    26                 stdout | acquire | "NO";
     27                mutex( stdout ) stdout | "NO";
    2728                stdin | i;
    2829        }
  • tests/pybin/test_run.py

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

    rf95634e rb7fd9daf  
    257257
    258258                # update output based on current action
    259                 result_txt = TestResult.toString( retcode, duration )
     259                result_key, 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, text
     267                return retcode == TestResult.SUCCESS, result_key, text
    268268        except KeyboardInterrupt:
    269                 return False, ""
     269                return False, 'keybrd', ""
    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
    285287
    286288        # for each test to run
     
    294296                )
    295297
    296                 for i, (succ, txt) in enumerate(timed(results, timeout = settings.timeout.total), 1) :
     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
    297304                        if not succ :
    298305                                failed = True
     
    319326        # clean the workspace
    320327        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))
    321330
    322331        return failed
     
    443452                        failed = run_tests(local_tests, options.jobs)
    444453                        if failed:
    445                                 result = 1
    446454                                if not settings.continue_:
    447455                                        break
  • tools/perf/process_stat_array.py

    rf95634e rb7fd9daf  
    11#!/usr/bin/python3
    22
    3 import argparse, os, sys, re
     3import argparse, json, math, os, sys, re
     4from PIL import Image
     5import numpy as np
    46
    57def dir_path(string):
     
    1113parser = argparse.ArgumentParser()
    1214parser.add_argument('--path', type=dir_path, default=".cfadata", help= 'paste path to biog.txt file')
     15parser.add_argument('--out', type=argparse.FileType('w'), default=sys.stdout)
    1316
    1417try :
     
    2326counters = {}
    2427
     28max_cpu = 0
     29min_cpu = 1000000
     30max_tsc = 0
     31min_tsc = 18446744073709551615
     32
    2533#open the files
    2634for filename in filenames:
     
    3139                with open(os.path.join(root, filename), 'r') as file:
    3240                        for line in file:
    33                                 # data = [int(x.strip()) for x in line.split(',')]
    34                                 data = [int(line.strip())]
    35                                 data = [me, *data]
     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])
    3660                                merged.append(data)
    3761
    38         except:
     62        except Exception as e:
     63                print(e)
    3964                pass
    4065
     66
     67print({"max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc})
    4168
    4269# Sort by timestamp (the second element)
     
    4774merged.sort(key=takeSecond)
    4875
    49 # for m in merged:
    50 #       print(m)
     76json.dump({"values":merged, "max-cpu": max_cpu, "min-cpu": min_cpu, "max-tsc": max_tsc, "min-tsc": min_tsc}, args.out)
    5177
    52 single = []
    53 curr = 0
     78# vmin = merged[ 0][1]
     79# vmax = float(merged[-1][1] - vmin) / 2500000000.0
     80# # print(vmax)
    5481
    55 # merge the data
    56 # for (me, time, value) in merged:
    57 for (me, value) in merged:
    58         # check now much this changes
    59         old = counters[me]
    60         change = value - old
    61         counters[me] = value
     82# bins = []
     83# for _ in range(0, int(math.ceil(vmax * 10))):
     84#       bins.append([0] * (32 * 32))
    6285
    63         # add change to the current
    64         curr = curr + change
    65         single.append( value )
     86# # print(len(bins))
     87# bins = np.array(bins)
    6688
    67         pass
     89# rejected = 0
     90# highest  = 0
    6891
    69 print(single)
     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)
    70143
    71144# single = sorted(single)[:len(single)-100]
Note: See TracChangeset for help on using the changeset viewer.