Changes in / [949339b:4e28d2e9]


Ignore:
Files:
10 added
55 deleted
63 edited

Legend:

Unmodified
Added
Removed
  • benchmark/Cargo.toml.in

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    r949339b r4e28d2e9  
    3232
    3333    end_time = thread_time_ns()
    34     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     34    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    3535
    3636
  • doc/theses/andrew_beach_MMath/code/resume-empty.cfa

    r949339b r4e28d2e9  
    1111        if (frames) {
    1212                nounwind_empty(frames - 1);
    13                 if ( frames == -1 ) printf( "42" );                             // prevent recursion optimizations
    1413        } else {
    1514                throwResume (empty_exception){&empty_vt};
  • doc/theses/andrew_beach_MMath/code/run.sh

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

    r949339b r4e28d2e9  
    1313#   View the result from TEST in LANGUAGE stored in FILE.
    1414
    15 readonly DIR=$(dirname "$(readlink -f "$0")")
    16 cd $DIR
    17 
    18 readonly MIL=000000
    19 # Various preset values used as arguments.
    20 readonly ITERS_1M=1$MIL
    21 readonly ITERS_10M=10$MIL
    22 readonly ITERS_100M=100$MIL
    23 readonly ITERS_1000M=1000$MIL
     15readonly ITERS_1M=1000000 # 1 000 000, one million
     16readonly ITERS_10M=10000000 # 10 000 000, ten million
     17readonly ITERS_100M=100000000 # 100 000 000, hundred million
     18readonly ITERS_1000M=1000000000 # 1 000 000 000, billion
    2419readonly STACK_HEIGHT=100
    2520
     
    3530        case "$1" in
    3631        *.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}"
     32                # Requires a symbolic link.
     33                mmake "${1%.cfa}" "$1" cfa -DNDEBUG -nodebug -O3 "$1" -o "${1%.cfa}"
    4334                ;;
    4435        *.cpp)
     
    5546)
    5647
    57 if [ "-a" = "$1" ]; then
     48if [ "-a" = "$1" ]; then                        # build all
    5849        for file in *.cfa *.cpp *.java; do
    5950                build $file
    6051        done
    6152        exit 0
    62 elif [ "-b" = "$1" ]; then
     53elif [ "-b" = "$1" ]; then                      # build given
    6354        for file in "${@:2}"; do
    6455                build $file
    6556        done
    6657        exit 0
    67 elif [ "-c" = "$1" ]; then
     58elif [ "-c" = "$1" ]; then                      # clean all
    6859        rm $(basename -s ".cfa" -a *.cfa)
    6960        rm $(basename -s ".cpp" -a *.cpp)
     
    9283raise-empty)
    9384        CFAT="./throw-empty $ITERS_1M $STACK_HEIGHT"
    94         CFAR="./resume-empty $ITERS_10M $STACK_HEIGHT"
     85# see resume-fixup-empty-r      CFAR="./resume-empty $ITERS_1M $STACK_HEIGHT"
    9586        CPP="./throw-empty-cpp $ITERS_1M $STACK_HEIGHT"
    9687        JAVA="java ThrowEmpty $ITERS_1M $STACK_HEIGHT"
     
    9990raise-detor)
    10091        CFAT="./throw-detor $ITERS_1M $STACK_HEIGHT"
    101         CFAR="./resume-detor $ITERS_10M $STACK_HEIGHT"
     92# N/A   CFAR="./resume-detor $ITERS_1M $STACK_HEIGHT"
    10293        CPP="./throw-detor-cpp $ITERS_1M $STACK_HEIGHT"
    10394        JAVA=unsupported
     
    10697raise-finally)
    10798        CFAT="./throw-finally $ITERS_1M $STACK_HEIGHT"
    108         CFAR="./resume-finally $ITERS_10M $STACK_HEIGHT"
     99# N/A   CFAR="./resume-finally $ITERS_1M $STACK_HEIGHT"
    109100        CPP=unsupported
    110101        JAVA="java ThrowFinally $ITERS_1M $STACK_HEIGHT"
     
    113104raise-other)
    114105        CFAT="./throw-other $ITERS_1M $STACK_HEIGHT"
    115         CFAR="./resume-other $ITERS_10M $STACK_HEIGHT"
     106# N/A   CFAR="./resume-other $ITERS_1M $STACK_HEIGHT"
    116107        CPP="./throw-other-cpp $ITERS_1M $STACK_HEIGHT"
    117108        JAVA="java ThrowOther $ITERS_1M $STACK_HEIGHT"
     
    134125cond-match-all)
    135126        CFAT="./cond-catch $ITERS_10M 1"
    136         CFAR="./cond-fixup $ITERS_100M 1"
     127        CFAR="./cond-fixup $ITERS_10M 1"
    137128        CPP="./cond-catch-cpp $ITERS_10M 1"
    138129        JAVA="java CondCatch $ITERS_10M 1"
     
    141132cond-match-none)
    142133        CFAT="./cond-catch $ITERS_10M 0"
    143         CFAR="./cond-fixup $ITERS_100M 0"
     134        CFAR="./cond-fixup $ITERS_10M 0"
    144135        CPP="./cond-catch-cpp $ITERS_10M 0"
    145136        JAVA="java CondCatch $ITERS_10M 0"
    146137        PYTHON="./cond-catch.py $ITERS_10M 0"
    147138        ;;
    148 fixup-empty)
    149         CFAT="./fixup-empty-f $ITERS_10M $STACK_HEIGHT"
    150         CFAR="./fixup-empty-r $ITERS_10M $STACK_HEIGHT"
    151         CPP="./fixup-empty-cpp $ITERS_10M $STACK_HEIGHT"
    152         JAVA="java FixupEmpty $ITERS_10M $STACK_HEIGHT"
    153         PYTHON="./fixup-empty.py $ITERS_10M $STACK_HEIGHT"
     139raise-fixup-empty)
     140        CFAT="./resume-fixup-empty-f $ITERS_10M $STACK_HEIGHT"
     141        CFAR="./resume-fixup-empty-r $ITERS_10M $STACK_HEIGHT"
     142        CPP="./resume-fixup-empty-cpp $ITERS_10M $STACK_HEIGHT"
     143        JAVA="java ResumeFixupEmpty $ITERS_10M $STACK_HEIGHT"
     144        PYTHON="./resume-fixup-empty.py $ITERS_10M $STACK_HEIGHT"
    154145        ;;
    155 fixup-other)
    156         CFAT="./fixup-other-f $ITERS_10M $STACK_HEIGHT"
    157         CFAR="./fixup-other-r $ITERS_10M $STACK_HEIGHT"
    158         CPP="./fixup-other-cpp $ITERS_10M $STACK_HEIGHT"
    159         JAVA="java FixupOther $ITERS_10M $STACK_HEIGHT"
    160         PYTHON="./fixup-other.py $ITERS_10M $STACK_HEIGHT"
     146raise-fixup-other)
     147        CFAT="./resume-fixup-other-f $ITERS_10M $STACK_HEIGHT"
     148        CFAR="./resume-fixup-other-r $ITERS_10M $STACK_HEIGHT"
     149        CPP="./resume-fixup-other-cpp $ITERS_10M $STACK_HEIGHT"
     150        JAVA="java ResumeFixupOther $ITERS_10M $STACK_HEIGHT"
     151        PYTHON="./resume-fixup-other.py $ITERS_10M $STACK_HEIGHT"
    161152        ;;
    162153*)
     
    167158
    168159case "$TEST_LANG" in
    169 cfa-t) CALL="$CFAT";;
    170 cfa-r) CALL="$CFAR";;
    171 cpp) CALL="$CPP";;
    172 java) CALL="$JAVA";;
    173 python) CALL="$PYTHON";;
    174 *)
    175         echo "No such language: $TEST_LANG" >&2
    176         exit 2
     160        cfa-t) CALL="$CFAT";;
     161        cfa-r) CALL="$CFAR";;
     162        cpp) CALL="$CPP";;
     163        java) CALL="$JAVA";;
     164        python) CALL="$PYTHON";;
     165        *)
     166                echo "No such language: $TEST_LANG" >&2
     167                exit 2
    177168        ;;
    178169esac
     
    181172
    182173if [ -n "$VIEW_FILE" ]; then
    183         grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time.*: !!;T;p'
     174        grep -A 1 -B 0 "$CALL" "$VIEW_FILE" | sed -n -e 's!Run-Time (ns): !!;T;p'
    184175        exit
    185176fi
  • doc/theses/andrew_beach_MMath/code/throw-empty.cfa

    r949339b r4e28d2e9  
    1111        if (frames) {
    1212                unwind_empty(frames - 1);
    13                 if ( frames == -1 ) printf( "42" );                             // prevent recursion optimizations
    1413        } else {
    1514                throw (empty_exception){&empty_vt};
  • doc/theses/andrew_beach_MMath/code/throw-empty.cpp

    r949339b r4e28d2e9  
    11// Throw Across Empty Function
    22#include <chrono>
    3 #include <cstdio>
    43#include <cstdlib>
    54#include <exception>
     
    1514        if (frames) {
    1615                unwind_empty(frames - 1);
    17                 if (-1 == frames) printf("~");
    1816        } else {
    1917                throw (EmptyException){};
  • doc/theses/andrew_beach_MMath/code/throw-empty.py

    r949339b r4e28d2e9  
    3333
    3434    end_time = thread_time_ns()
    35     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     35    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    3636
    3737
  • doc/theses/andrew_beach_MMath/code/throw-finally.py

    r949339b r4e28d2e9  
    3636
    3737    end_time = thread_time_ns()
    38     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     38    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    3939
    4040
  • doc/theses/andrew_beach_MMath/code/throw-other.py

    r949339b r4e28d2e9  
    4040
    4141    end_time = thread_time_ns()
    42     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     42    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    4343
    4444
  • doc/theses/andrew_beach_MMath/code/throw-with.py

    r949339b r4e28d2e9  
    4343
    4444    end_time = thread_time_ns()
    45     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     45    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    4646
    4747
  • doc/theses/andrew_beach_MMath/code/try-catch.py

    r949339b r4e28d2e9  
    2323
    2424    end_time = thread_time_ns()
    25     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     25    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    2626
    2727
  • doc/theses/andrew_beach_MMath/code/try-finally.py

    r949339b r4e28d2e9  
    2222
    2323    end_time = thread_time_ns()
    24     print('Run-Time (s): {:.1f}'.format((end_time - start_time) / 1_000_000_000.))
     24    print('Run-Time (s) {:.1f}:'.format((end_time - start_time) / 1_000_000_000.))
    2525
    2626
  • doc/theses/andrew_beach_MMath/conclusion.tex

    r949339b r4e28d2e9  
    33% Just a little knot to tie the paper together.
    44
    5 In the previous chapters, this thesis presents the design and implementation
     5In the previous chapters this thesis presents the design and implementation
    66of \CFA's exception handling mechanism (EHM).
    77Both the design and implementation are based off of tools and
    88techniques developed for other programming languages but they were adapted to
    99better fit \CFA's feature set and add a few features that do not exist in
    10 other EHMs,
     10other EHMs;
    1111including conditional matching, default handlers for unhandled exceptions
    1212and cancellation though coroutines and threads back to the program main stack.
  • doc/theses/andrew_beach_MMath/existing.tex

    r949339b r4e28d2e9  
    66compatibility with C and its programmers.  \CFA is designed to have an
    77orthogonal feature-set based closely on the C programming paradigm
    8 (non-object-oriented), and these features can be added incrementally to an
    9 existing C code-base,
    10 allowing programmers to learn \CFA on an as-needed basis.
     8(non-object-oriented) and these features can be added incrementally to an
     9existing C code-base allowing programmers to learn \CFA on an as-needed basis.
    1110
    1211Only those \CFA features pertaining to this thesis are discussed.
     
    4645\CFA adds a reference type to C as an auto-dereferencing pointer.
    4746They work very similarly to pointers.
    48 Reference-types are written the same way as pointer-types, but each
     47Reference-types are written the same way as a pointer-type but each
    4948asterisk (@*@) is replaced with a ampersand (@&@);
    50 this includes cv-qualifiers (\snake{const} and \snake{volatile})
    51 and multiple levels of reference.
    52 
    53 Generally, references act like pointers with an implicit dereferencing
     49this includes cv-qualifiers and multiple levels of reference.
     50
     51Generally, references act like pointers with an implicate dereferencing
    5452operation added to each use of the variable.
    5553These automatic dereferences may be disabled with the address-of operator
     
    8583Mutable references may be assigned to by converting them to a pointer
    8684with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above.
     85% ???
    8786
    8887\section{Operators}
     
    9493For example,
    9594infixed multiplication is @?*?@, while prefix dereference is @*?@.
    96 This syntax makes it easy to tell the difference between prefix operations
    97 (such as @++?@) and postfix operations (@?++@).
     95This syntax make it easy to tell the difference between prefix operations
     96(such as @++?@) and post-fix operations (@?++@).
    9897
    9998As an example, here are the addition and equality operators for a point type.
     
    105104}
    106105\end{cfa}
    107 Note that this syntax works effectively as a textual transformation;
     106Note that this syntax works effectively but a textual transformation,
    108107the compiler converts all operators into functions and then resolves them
    109108normally. This means any combination of types may be used,
     
    114113%\subsection{Constructors and Destructors}
    115114In \CFA, constructors and destructors are operators, which means they are
    116 functions with special operator names, rather than type names as in \Cpp.
     115functions with special operator names rather than type names in \Cpp.
    117116Both constructors and destructors can be implicity called by the compiler,
    118117however the operator names allow explicit calls.
     
    138137@b@ because of the explicit call and @a@ implicitly.
    139138@c@ will be initalized with the second constructor.
    140 Currently, there is no general way to skip initialization.
     139Currently, there is no general way to skip initialation.
    141140% I don't use @= anywhere in the thesis.
    142141
     
    203202do_twice(i);
    204203\end{cfa}
    205 Any value with a type fulfilling the assertion may be passed as an argument to
     204Any object with a type fulfilling the assertion may be passed as an argument to
    206205a @do_twice@ call.
    207206
     
    223222function. The matched assertion function is then passed as a function pointer
    224223to @do_twice@ and called within it.
    225 The global definition of @do_once@ is ignored, however if @quadruple@ took a
     224The global definition of @do_once@ is ignored, however if quadruple took a
    226225@double@ argument, then the global definition would be used instead as it
    227226would then be a better match.\cite{Moss19}
    228227
    229228To avoid typing long lists of assertions, constraints can be collected into
    230 a convenient package called a @trait@, which can then be used in an assertion
     229convenient a package called a @trait@, which can then be used in an assertion
    231230instead of the individual constraints.
    232231\begin{cfa}
     
    242241functions and variables, and are usually used to create a shorthand for, and
    243242give descriptive names to, common groupings of assertions describing a certain
    244 functionality, like @summable@, @listable@, \etc.
     243functionality, like @sumable@, @listable@, \etc.
    245244
    246245Polymorphic structures and unions are defined by qualifying an aggregate type
    247246with @forall@. The type variables work the same except they are used in field
    248 declarations instead of parameters, returns and local variable declarations.
     247declarations instead of parameters, returns, and local variable declarations.
    249248\begin{cfa}
    250249forall(dtype T)
     
    262261
    263262\section{Control Flow}
    264 \CFA has a number of advanced control-flow features: @generator@, @coroutine@,
    265 @monitor@, @mutex@ parameters, and @thread@.
     263\CFA has a number of advanced control-flow features: @generator@, @coroutine@, @monitor@, @mutex@ parameters, and @thread@.
    266264The two features that interact with
    267265the exception system are @coroutine@ and @thread@; they and their supporting
     
    270268\subsection{Coroutine}
    271269A coroutine is a type with associated functions, where the functions are not
    272 required to finish execution when control is handed back to the caller.
    273 Instead,
     270required to finish execution when control is handed back to the caller. Instead
    274271they may suspend execution at any time and be resumed later at the point of
    275 last suspension.
    276 Coroutine
     272last suspension. (Generators are stackless and coroutines are stackful.) These
    277273types are not concurrent but share some similarities along with common
    278 underpinnings, so they are combined with the \CFA threading library.
    279 % I had mention of generators, but they don't actually matter here.
     274underpinnings, so they are combined with the \CFA threading library. Further
     275discussion in this section only refers to the coroutine because generators are
     276similar.
    280277
    281278In \CFA, a coroutine is created using the @coroutine@ keyword, which is an
     
    325322\end{cfa}
    326323
    327 When the main function returns, the coroutine halts and can no longer be
     324When the main function returns the coroutine halts and can no longer be
    328325resumed.
    329326
    330327\subsection{Monitor and Mutex Parameter}
    331 Concurrency does not guarantee ordering; without ordering, results are
     328Concurrency does not guarantee ordering; without ordering results are
    332329non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@
    333330(mutual exclusion) parameters. A monitor is another kind of aggregate, where
     
    335332@mutex@ parameters.
    336333
    337 A function that requires deterministic (ordered) execution acquires mutual
     334A function that requires deterministic (ordered) execution, acquires mutual
    338335exclusion on a monitor object by qualifying an object reference parameter with
    339 the @mutex@ qualifier.
     336@mutex@.
    340337\begin{cfa}
    341338void example(MonitorA & mutex argA, MonitorB & mutex argB);
     
    347344
    348345\subsection{Thread}
    349 Functions, generators and coroutines are sequential, so there is only a single
     346Functions, generators, and coroutines are sequential so there is only a single
    350347(but potentially sophisticated) execution path in a program. Threads introduce
    351348multiple execution paths that continue independently.
    352349
    353350For threads to work safely with objects requires mutual exclusion using
    354 monitors and mutex parameters. For threads to work safely with other threads
     351monitors and mutex parameters. For threads to work safely with other threads,
    355352also requires mutual exclusion in the form of a communication rendezvous, which
    356353also supports internal synchronization as for mutex objects. For exceptions,
  • doc/theses/andrew_beach_MMath/features.tex

    r949339b r4e28d2e9  
    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}
     
    4242
    4343The @try@ statements of \Cpp, Java and Python are common examples. All three
    44 also show another common feature of handlers: they are grouped by the guarded
     44also show another common feature of handlers, they are grouped by the guarded
    4545region.
    4646
     
    8484\paragraph{Hierarchy}
    8585A common way to organize exceptions is in a hierarchical structure.
    86 This pattern comes from object-oriented languages where the
     86This pattern comes from object-orientated languages where the
    8787exception hierarchy is a natural extension of the object hierarchy.
    8888
     
    101101between different sub-hierarchies.
    102102This design is used in \CFA even though it is not a object-orientated
    103 language, so different tools are used to create the hierarchy.
     103language; so different tools are used to create the hierarchy.
    104104
    105105% Could I cite the rational for the Python IO exception rework?
     
    118118For effective exception handling, additional information is often passed
    119119from the raise to the handler and back again.
    120 So far, only communication of the exception's identity is covered.
     120So far, only communication of the exceptions' identity is covered.
    121121A common communication method for adding information to an exception
    122122is putting fields into the exception instance
     
    129129\section{Virtuals}
    130130\label{s:virtuals}
    131 A common feature in many programming languages is a tool to pair code
    132 (behaviour) with data.
    133 In \CFA, this is done with the virtual system,
    134 which allow type information to be abstracted away, recovered and allow
    135 operations to be performed on the abstract objects.
    136 
    137131Virtual types and casts are not part of \CFA's EHM nor are they required for
    138132an EHM.
     
    158152% A type's descendants are its children and its children's descendants.
    159153
    160 For the purposes of illustration, a proposed, but unimplemented, syntax
     154For the purposes of illustration, a proposed -- but unimplemented syntax --
    161155will be used. Each virtual type is represented by a trait with an annotation
    162156that makes it a virtual type. This annotation is empty for a root type, which
     
    183177\end{minipage}
    184178
    185 Every virtual type also has a list of virtual members and a unique id.
    186 Both are stored in a virtual table.
     179Every virtual type also has a list of virtual members and a unique id,
     180both are stored in a virtual table.
    187181Every instance of a virtual type also has a pointer to a virtual table stored
    188182in it, although there is no per-type virtual table as in many other languages.
    189183
    190 The list of virtual members is accumulated from the root type down the tree.
    191 Every virtual type
     184The list of virtual members is built up down the tree. Every virtual type
    192185inherits the list of virtual members from its parent and may add more
    193186virtual members to the end of the list which are passed on to its children.
     
    205198% Consider adding a diagram, but we might be good with the explanation.
    206199
    207 As @child_type@ is a child of @root_type@, it has the virtual members of
     200As @child_type@ is a child of @root_type@ it has the virtual members of
    208201@root_type@ (@to_string@ and @size@) as well as the one it declared
    209202(@irrelevant_function@).
     
    213206The names ``size" and ``align" are reserved for the size and alignment of the
    214207virtual type, and are always automatically initialized as such.
    215 The other special case is uses of the trait's polymorphic argument
     208The other special case are uses of the trait's polymorphic argument
    216209(@T@ in the example), which are always updated to refer to the current
    217 virtual type. This allows functions that refer to the polymorphic argument
     210virtual type. This allows functions that refer to to polymorphic argument
    218211to act as traditional virtual methods (@to_string@ in the example), as the
    219212object can always be passed to a virtual method in its virtual table.
    220213
    221 Up until this point, the virtual system is similar to ones found in
    222 object-oriented languages, but this is where \CFA diverges.
     214Up until this point the virtual system is similar to ones found in
     215object-oriented languages but this is where \CFA diverges.
    223216Objects encapsulate a single set of methods in each type,
    224217universally across the entire program,
     
    230223
    231224In \CFA, types do not encapsulate any code.
    232 Whether or not a type satisfies any given assertion, and hence any trait, is
     225Whether or not satisfies any given assertion, and hence any trait, is
    233226context sensitive. Types can begin to satisfy a trait, stop satisfying it or
    234227satisfy the same trait at any lexical location in the program.
    235 In this sense, a type's implementation in the set of functions and variables
     228In this sense, an type's implementation in the set of functions and variables
    236229that allow it to satisfy a trait is ``open" and can change
    237230throughout the program.
     
    255248\end{cfa}
    256249
    257 Like any variable, they may be forward declared with the @extern@ keyword.
     250Like any variable they may be forward declared with the @extern@ keyword.
    258251Forward declaring virtual tables is relatively common.
    259252Many virtual types have an ``obvious" implementation that works in most
     
    266259Initialization is automatic.
    267260The type id and special virtual members ``size" and ``align" only depend on
    268 the virtual type, which is fixed given the type of the virtual table, and
     261the virtual type, which is fixed given the type of the virtual table and
    269262so the compiler fills in a fixed value.
    270 The other virtual members are resolved using the best match to the member's
     263The other virtual members are resolved, using the best match to the member's
    271264name and type, in the same context as the virtual table is declared using
    272265\CFA's normal resolution rules.
    273266
    274 While much of the virtual infrastructure has been created,
    275 it is currently only used
     267While much of the virtual infrastructure is created, it is currently only used
    276268internally for exception handling. The only user-level feature is the virtual
    277269cast, which is the same as the \Cpp \code{C++}{dynamic_cast}.
     
    282274Note, the syntax and semantics matches a C-cast, rather than the function-like
    283275\Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be
    284 pointers to virtual types.
     276a pointer to a virtual type.
    285277The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type
    286278of @TYPE@, and if true, returns a pointer to the
    287279@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
    288 This allows the expression to be used as both a cast and a type check.
    289280
    290281\section{Exceptions}
    291282
    292283The syntax for declaring an exception is the same as declaring a structure
    293 except the keyword:
     284except the keyword that is swapped out:
    294285\begin{cfa}
    295286exception TYPE_NAME {
     
    298289\end{cfa}
    299290
    300 Fields are filled in the same way as a structure as well. However, an extra
     291Fields are filled in the same way as a structure as well. However an extra
    301292field is added that contains the pointer to the virtual table.
    302293It must be explicitly initialized by the user when the exception is
     
    308299
    309300\begin{minipage}[t]{0.4\textwidth}
    310 Header (.hfa):
     301Header:
    311302\begin{cfa}
    312303exception Example {
     
    319310\end{minipage}
    320311\begin{minipage}[t]{0.6\textwidth}
    321 Implementation (.cfa):
     312Source:
    322313\begin{cfa}
    323314vtable(Example) example_base_vtable
     
    328319%\subsection{Exception Details}
    329320This is the only interface needed when raising and handling exceptions.
    330 However, it is actually a shorthand for a more complex
    331 trait-based interface.
     321However it is actually a short hand for a more complex
     322trait based interface.
    332323
    333324The language views exceptions through a series of traits.
     
    345336completing the virtual system). The imaginary assertions would probably come
    346337from a trait defined by the virtual system, and state that the exception type
    347 is a virtual type,
    348 that that the type is a descendant of @exception_t@ (the base exception type)
     338is a virtual type, is a descendant of @exception_t@ (the base exception type)
    349339and allow the user to find the virtual table type.
    350340
     
    365355};
    366356\end{cfa}
    367 Both traits ensure a pair of types is an exception type and
    368 its virtual table type,
     357Both traits ensure a pair of types is an exception type, its virtual table
     358type
    369359and defines one of the two default handlers. The default handlers are used
    370 as fallbacks and are discussed in detail in \autoref{s:ExceptionHandling}.
     360as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}.
    371361
    372362However, all three of these traits can be tricky to use directly.
    373363While there is a bit of repetition required,
    374364the largest issue is that the virtual table type is mangled and not in a user
    375 facing way. So, these three macros are provided to wrap these traits to
     365facing way. So these three macros are provided to wrap these traits to
    376366simplify referring to the names:
    377367@IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.
     
    381371The second (optional) argument is a parenthesized list of polymorphic
    382372arguments. This argument is only used with polymorphic exceptions and the
    383 list is passed to both types.
     373list is be passed to both types.
    384374In the current set-up, the two types always have the same polymorphic
    385 arguments, so these macros can be used without losing flexibility.
    386 
    387 For example, consider a function that is polymorphic over types that have a
     375arguments so these macros can be used without losing flexibility.
     376
     377For example consider a function that is polymorphic over types that have a
    388378defined arithmetic exception:
    389379\begin{cfa}
     
    398388These twin operations are the core of \CFA's exception handling mechanism.
    399389This section covers the general patterns shared by the two operations and
    400 then goes on to cover the details of each individual operation.
     390then goes on to cover the details each individual operation.
    401391
    402392Both operations follow the same set of steps.
     
    417407\label{s:Termination}
    418408Termination handling is the familiar kind of handling
    419 used in most programming
     409and used in most programming
    420410languages with exception handling.
    421411It is a dynamic, non-local goto. If the raised exception is matched and
     
    493483Since it is so general, a more specific handler can be defined,
    494484overriding the default behaviour for the specific exception types.
    495 
    496 For example, consider an error reading a configuration file.
    497 This is most likely a problem with the configuration file (@config_error@),
    498 but the function could have been passed the wrong file name (@arg_error@).
    499 In this case the function could raise one exception and then, if it is
    500 unhandled, raise the other.
    501 This is not usual behaviour for either exception so changing the
    502 default handler will be done locally:
    503 \begin{cfa}
    504 {
    505         void defaultTerminationHandler(config_error &) {
    506                 throw (arg_error){arg_vt};
    507         }
    508         throw (config_error){config_vt};
    509 }
    510 \end{cfa}
    511485
    512486\subsection{Resumption}
     
    568542@EXCEPTION_TYPE@$_i$ is matched and @NAME@$_i$ is bound to the exception,
    569543@HANDLER_BLOCK@$_i$ is executed right away without first unwinding the stack.
    570 After the block has finished running, control jumps to the raise site, where
     544After the block has finished running control jumps to the raise site, where
    571545the just handled exception came from, and continues executing after it,
    572546not after the try statement.
    573 
    574 For instance, a resumption used to send messages to the logger may not
    575 need to be handled at all. Putting the following default handler
    576 at the global scope can make handling that exception optional by default.
    577 \begin{cfa}
    578 void defaultResumptionHandler(log_message &) {
    579     // Nothing, it is fine not to handle logging.
    580 }
    581 // ... No change at raise sites. ...
    582 throwResume (log_message){strlit_log, "Begin event processing."}
    583 \end{cfa}
    584547
    585548\subsubsection{Resumption Marking}
     
    588551not unwind the stack. A side effect is that, when a handler is matched
    589552and run, its try block (the guarded statements) and every try statement
    590 searched before it are still on the stack. Their presence can lead to
     553searched before it are still on the stack. There presence can lead to
    591554the recursive resumption problem.\cite{Buhr00a}
    592555% Other possible citation is MacLaren77, but the form is different.
     
    622585\end{center}
    623586
    624 There are other sets of marking rules that could be used.
    625 For instance, marking just the handlers that caught the exception
     587There are other sets of marking rules that could be used,
     588for instance, marking just the handlers that caught the exception,
    626589would also prevent recursive resumption.
    627 However, the rules selected mirror what happens with termination,
     590However, the rules selected mirrors what happens with termination,
    628591so this reduces the amount of rules and patterns a programmer has to know.
    629592
     
    665628// Handle a failure relating to f2 further down the stack.
    666629\end{cfa}
    667 In this example, the file that experienced the IO error is used to decide
     630In this example the file that experienced the IO error is used to decide
    668631which handler should be run, if any at all.
    669632
     
    694657
    695658\subsection{Comparison with Reraising}
    696 In languages without conditional catch -- that is, no ability to match an
    697 exception based on something other than its type -- it can be mimicked
     659In languages without conditional catch, that is no ability to match an
     660exception based on something other than its type, it can be mimicked
    698661by matching all exceptions of the right type, checking any additional
    699662conditions inside the handler and re-raising the exception if it does not
     
    701664
    702665Here is a minimal example comparing both patterns, using @throw;@
    703 (no operand) to start a re-raise.
     666(no argument) to start a re-raise.
    704667\begin{center}
    705668\begin{tabular}{l r}
     
    729692\end{tabular}
    730693\end{center}
    731 At first glance, catch-and-reraise may appear to just be a quality-of-life
     694At first glance catch-and-reraise may appear to just be a quality of life
    732695feature, but there are some significant differences between the two
    733 strategies.
     696stratagies.
    734697
    735698A simple difference that is more important for \CFA than many other languages
    736 is that the raise site changes with a re-raise, but does not with a
     699is that the raise site changes, with a re-raise but does not with a
    737700conditional catch.
    738701This is important in \CFA because control returns to the raise site to run
    739 the per-site default handler. Because of this, only a conditional catch can
     702the per-site default handler. Because of this only a conditional catch can
    740703allow the original raise to continue.
    741704
     
    790753%   } else throw;
    791754% }
    792 In similar simple examples, translating from re-raise to conditional catch
    793 takes less code but it does not have a general, trivial solution either.
     755In similar simple examples translating from re-raise to conditional catch
     756takes less code but it does not have a general trivial solution either.
    794757
    795758So, given that the two patterns do not trivially translate into each other,
    796759it becomes a matter of which on should be encouraged and made the default.
    797 From the premise that if a handler could handle an exception then it
     760From the premise that if a handler that could handle an exception then it
    798761should, it follows that checking as many handlers as possible is preferred.
    799 So, conditional catch and checking later handlers is a good default.
     762So conditional catch and checking later handlers is a good default.
    800763
    801764\section{Finally Clauses}
    802765\label{s:FinallyClauses}
    803 Finally clauses are used to perform unconditional cleanup when leaving a
     766Finally clauses are used to preform unconditional clean-up when leaving a
    804767scope and are placed at the end of a try statement after any handler clauses:
    805768\begin{cfa}
     
    819782Execution of the finally block should always finish, meaning control runs off
    820783the end of the block. This requirement ensures control always continues as if
    821 the finally clause is not present, \ie finally is for cleanup, not changing
     784the finally clause is not present, \ie finally is for cleanup not changing
    822785control flow.
    823786Because of this requirement, local control flow out of the finally block
    824787is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or
    825788@return@ that causes control to leave the finally block. Other ways to leave
    826 the finally block, such as a @longjmp@ or termination are much harder to check,
    827 and at best require additional run-time overhead, and so are only
     789the finally block, such as a long jump or termination are much harder to check,
     790and at best requiring additional run-time overhead, and so are only
    828791discouraged.
    829792
    830 Not all languages with unwinding have finally clauses. Notably, \Cpp does
     793Not all languages with unwinding have finally clauses. Notably \Cpp does
    831794without it as destructors, and the RAII design pattern, serve a similar role.
    832795Although destructors and finally clauses can be used for the same cases,
     
    835798Destructors take more work to create, but if there is clean-up code
    836799that needs to be run every time a type is used, they are much easier
    837 to set up for each use. % It's automatic.
    838 On the other hand, finally clauses capture the local context, so are easy to
    839 use when the cleanup is not dependent on the type of a variable or requires
     800to set-up for each use. % It's automatic.
     801On the other hand finally clauses capture the local context, so is easy to
     802use when the clean-up is not dependent on the type of a variable or requires
    840803information from multiple variables.
    841804
     
    844807Cancellation is a stack-level abort, which can be thought of as as an
    845808uncatchable termination. It unwinds the entire current stack, and if
    846 possible, forwards the cancellation exception to a different stack.
     809possible forwards the cancellation exception to a different stack.
    847810
    848811Cancellation is not an exception operation like termination or resumption.
    849812There is no special statement for starting a cancellation; instead the standard
    850 library function @cancel_stack@ is called, passing an exception. Unlike a
    851 raise, this exception is not used in matching, only to pass information about
     813library function @cancel_stack@ is called passing an exception. Unlike a
     814raise, this exception is not used in matching only to pass information about
    852815the cause of the cancellation.
    853816Finally, as no handler is provided, there is no default handler.
    854817
    855 After @cancel_stack@ is called, the exception is copied into the EHM's memory
     818After @cancel_stack@ is called the exception is copied into the EHM's memory
    856819and the current stack is unwound.
    857820The behaviour after that depends on the kind of stack being cancelled.
    858821
    859822\paragraph{Main Stack}
    860 The main stack is the one used by
    861 the program's main function at the start of execution,
     823The main stack is the one used by the program main at the start of execution,
    862824and is the only stack in a sequential program.
    863 After the main stack is unwound, there is a program-level abort.
     825After the main stack is unwound there is a program-level abort.
    864826
    865827The first reason for this behaviour is for sequential programs where there
    866 is only one stack, and hence no stack to pass information to.
     828is only one stack, and hence to stack to pass information to.
    867829Second, even in concurrent programs, the main stack has no dependency
    868830on another stack and no reliable way to find another living stack.
     
    880842and an implicit join (from a destructor call). The explicit join takes the
    881843default handler (@defaultResumptionHandler@) from its calling context while
    882 the implicit join provides its own, which does a program abort if the
     844the implicit join provides its own; which does a program abort if the
    883845@ThreadCancelled@ exception cannot be handled.
    884846
     
    908870After a coroutine stack is unwound, control returns to the @resume@ function
    909871that most recently resumed it. @resume@ reports a
    910 @CoroutineCancelled@ exception, which contains a reference to the cancelled
     872@CoroutineCancelled@ exception, which contains a references to the cancelled
    911873coroutine and the exception used to cancel it.
    912874The @resume@ function also takes the \defaultResumptionHandler{} from the
  • doc/theses/andrew_beach_MMath/future.tex

    r949339b r4e28d2e9  
    33
    44The following discussion covers both possible interesting research
    5 that could follow from this work as well as simple implementation
     5that could follow from this work as long as simple implementation
    66improvements.
    77
     
    1010\CFA is a developing programming language. As such, there are partially or
    1111unimplemented features (including several broken components)
    12 that I had to work around while building the EHM largely in
     12that I had to workaround while building the EHM largely in
    1313the \CFA language (some C components). Below are a few of these issues
    1414and how implementing/fixing them would affect the EHM.
    15 In addition, there are some simple improvements that had no interesting
     15In addition there are some simple improvements that had no interesting
    1616research attached to them but would make using the language easier.
    1717\begin{itemize}
     
    2828Termination handlers cannot use local control-flow transfers, \eg by @break@,
    2929@return@, \etc. The reason is that current code generation hoists a handler
    30 into a nested function for convenience (versus assembly-code generation at the
     30into a nested function for convenience (versus assemble-code generation at the
    3131try statement). Hence, when the handler runs, it can still access local
    3232variables in the lexical scope of the try statement. Still, it does mean
    3333that seemingly local control flow is not in fact local and crosses a function
    3434boundary.
    35 Making the termination handler's code within the surrounding
     35Making the termination handlers code within the surrounding
    3636function would remove this limitation.
    3737% Try blocks are much more difficult to do practically (requires our own
    3838% assembly) and resumption handlers have some theoretical complexity.
    3939\item
    40 There is no detection of colliding unwinds. It is possible for cleanup code
    41 run during an unwind to trigger another unwind that escapes the cleanup code
    42 itself, such as a termination exception caught further down the stack or a
     40There is no detection of colliding unwinds. It is possible for clean-up code
     41run during an unwind to trigger another unwind that escapes the clean-up code
     42itself; such as a termination exception caught further down the stack or a
    4343cancellation. There do exist ways to handle this case, but currently there is
    4444no detection and the first unwind will simply be forgotten, often leaving
     
    6464Other features of the virtual system could also remove some of the
    6565special cases around exception virtual tables, such as the generation
    66 of the @msg@ function.
     66of the @msg@ function, could be removed.
    6767
    6868The full virtual system might also include other improvement like associated
     
    7474\section{Additional Raises}
    7575Several other kinds of exception raises were considered beyond termination
    76 (@throw@), resumption (@throwResume@), and re-raise.
     76(@throw@), resumption (@throwResume@), and reraise.
    7777
    7878The first is a non-local/concurrent raise providing asynchronous exceptions,
     
    110110passed on.
    111111
    112 Checked exceptions were never seriously considered for this project
     112However checked exceptions were never seriously considered for this project
    113113because they have significant trade-offs in usability and code reuse in
    114114exchange for the increased safety.
     
    116116higher-order functions from the functions the user passed into the
    117117higher-order function. There are no well known solutions to this problem
    118 that were satisfactory for \CFA (which carries some of C's
    119 flexibility-over-safety design) so additional research is needed.
     118that were satisfactory for \CFA (which carries some of C's flexibility
     119over safety design) so additional research is needed.
    120120
    121121Follow-up work might add some form of checked exceptions to \CFA,
     
    140140Zero-cost resumptions is still an open problem. First, because libunwind does
    141141not support a successful-exiting stack-search without doing an unwind.
    142 Workarounds are possible but awkward. Ideally, an extension to libunwind could
     142Workarounds are possible but awkward. Ideally an extension to libunwind could
    143143be made, but that would either require separate maintenance or gaining enough
    144144support to have it folded into the official library itself.
    145145
    146 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
    147147developed to handle the recursive resume problem and support advanced algebraic
    148148effects.
  • doc/theses/andrew_beach_MMath/implement.tex

    r949339b r4e28d2e9  
    1414\label{s:VirtualSystem}
    1515% Virtual table rules. Virtual tables, the pointer to them and the cast.
    16 While the \CFA virtual system currently has only two public features, virtual
     16While the \CFA virtual system currently has only one public features, virtual
    1717cast and virtual tables,
     18% ??? refs (see the virtual cast feature \vpageref{p:VirtualCast}),
    1819substantial structure is required to support them,
    1920and provide features for exception handling and the standard library.
     
    3435as part of field-by-field construction.
    3536
    36 \subsection{Type ID}
    37 Every virtual type has a unique ID.
     37\subsection{Type Id}
     38Every virtual type has a unique id.
    3839These are used in type equality, to check if the representation of two values
    3940are the same, and to access the type's type information.
     
    4344
    4445Our approach for program uniqueness is using a static declaration for each
    45 type ID, where the run-time storage address of that variable is guaranteed to
     46type id, where the run-time storage address of that variable is guaranteed to
    4647be unique during program execution.
    47 The type ID storage can also be used for other purposes,
     48The type id storage can also be used for other purposes,
    4849and is used for type information.
    4950
    50 The problem is that a type ID may appear in multiple TUs that compose a
    51 program (see \autoref{ss:VirtualTable}), so the initial solution would seem
    52 to be make it external in each translation unit. However, the type ID must
     51The problem is that a type id may appear in multiple TUs that compose a
     52program (see \autoref{ss:VirtualTable}); so the initial solution would seem
     53to be make it external in each translation unit. Hovever, the type id must
    5354have a declaration in (exactly) one of the TUs to create the storage.
    5455No other declaration related to the virtual type has this property, so doing
    5556this through standard C declarations would require the user to do it manually.
    5657
    57 Instead, the linker is used to handle this problem.
     58Instead the linker is used to handle this problem.
    5859% I did not base anything off of C++17; they are solving the same problem.
    5960A new feature has been added to \CFA for this purpose, the special attribute
    6061\snake{cfa_linkonce}, which uses the special section @.gnu.linkonce@.
    61 When used as a prefix (\eg @.gnu.linkonce.example@), the linker does
     62When used as a prefix (\eg @.gnu.linkonce.example@) the linker does
    6263not combine these sections, but instead discards all but one with the same
    6364full name.
    6465
    65 So, each type ID must be given a unique section name with the \snake{linkonce}
    66 prefix. Luckily, \CFA already has a way to get unique names, the name mangler.
     66So each type id must be given a unique section name with the linkonce
     67prefix. Luckily \CFA already has a way to get unique names, the name mangler.
    6768For example, this could be written directly in \CFA:
    6869\begin{cfa}
     
    7374__attribute__((section(".gnu.linkonce._X1fFv___1"))) void _X1fFv___1() {}
    7475\end{cfa}
    75 This is done internally to access the name mangler.
     76This is done internally to access the name manglers.
    7677This attribute is useful for other purposes, any other place a unique
    7778instance required, and should eventually be made part of a public and
     
    8081\subsection{Type Information}
    8182
    82 There is data stored at the type ID's declaration, the type information.
    83 The type information currently is only the parent's type ID or, if the
     83There is data stored at the type id's declaration, the type information.
     84The type information currently is only the parent's type id or, if the
    8485type has no parent, the null pointer.
    85 The ancestors of a virtual type are found by traversing type IDs through
     86The ancestors of a virtual type are found by traversing type ids through
    8687the type information.
    8788An example using helper macros looks like:
     
    104105\item
    105106Generate a new structure definition to store the type
    106 information. The layout is the same in each case, just the parent's type ID,
     107information. The layout is the same in each case, just the parent's type id,
    107108but the types used change from instance to instance.
    108109The generated name is used for both this structure and, if relevant, the
     
    114115\item
    115116The definition is generated and initialized.
    116 The parent ID is set to the null pointer or to the address of the parent's
     117The parent id is set to the null pointer or to the address of the parent's
    117118type information instance. Name resolution handles the rest.
    118119\item
     
    150151file as if it was a forward declaration, except no definition is required.
    151152
    152 This technique is used for type ID instances. A link-once definition is
     153This technique is used for type-id instances. A link-once definition is
    153154generated each time the structure is seen. This will result in multiple
    154155copies but the link-once attribute ensures all but one are removed for a
     
    167168\subsection{Virtual Table}
    168169\label{ss:VirtualTable}
    169 Each virtual type has a virtual table type that stores its type ID and
     170Each virtual type has a virtual table type that stores its type id and
    170171virtual members.
    171 An instance of a virtual type is bound to a virtual table instance,
    172 which have the values of the virtual members.
    173 Both the layout of the fields (in the virtual table type)
    174 and their value (in the virtual table instance) are decided by the rules given
     172Each virtual type instance is bound to a table instance that is filled with
     173the values of virtual members.
     174Both the layout of the fields and their value are decided by the rules given
    175175below.
    176176
    177177The layout always comes in three parts (see \autoref{f:VirtualTableLayout}).
    178 The first section is just the type ID at the head of the table. It is always
     178The first section is just the type id at the head of the table. It is always
    179179there to ensure that it can be found even when the accessing code does not
    180180know which virtual type it has.
    181 The second section is all the virtual members of the parent, in the same
     181The second section are all the virtual members of the parent, in the same
    182182order as they appear in the parent's virtual table. Note that the type may
    183183change slightly as references to the ``this" change. This is limited to
     
    199199This, combined with the fixed offset to the virtual table pointer, means that
    200200for any virtual type, it is always safe to access its virtual table and,
    201 from there, it is safe to check the type ID to identify the exact type of the
     201from there, it is safe to check the type id to identify the exact type of the
    202202underlying object, access any of the virtual members and pass the object to
    203203any of the method-like virtual members.
     
    207207the context of the declaration.
    208208
    209 The type ID is always fixed, with each virtual table type having
    210 exactly one possible type ID.
     209The type id is always fixed; with each virtual table type having
     210exactly one possible type id.
    211211The virtual members are usually filled in by type resolution.
    212212The best match for a given name and type at the declaration site is used.
    213213There are two exceptions to that rule: the @size@ field, the type's size,
    214 is set using a @sizeof@ expression, and the @align@ field, the
     214is set using a @sizeof@ expression and the @align@ field, the
    215215type's alignment, is set using an @alignof@ expression.
    216216
    217217Most of these tools are already inside the compiler. Using simple
    218 code transformations early on in compilation allows most of that work to be
     218code transformations early on in compilation, allows most of that work to be
    219219handed off to the existing tools. \autoref{f:VirtualTableTransformation}
    220 shows an example transformation; this example shows an exception virtual table.
     220shows an example transformation, this example shows an exception virtual table.
    221221It also shows the transformation on the full declaration.
    222222For a forward declaration, the @extern@ keyword is preserved and the
     
    312312        struct __cfavir_type_id * const * child );
    313313\end{cfa}
    314 The type ID for the target type of the virtual cast is passed in as
     314The type id for the target type of the virtual cast is passed in as
    315315@parent@ and
    316316the cast target is passed in as @child@.
     
    322322The virtual cast either returns the original pointer or the null pointer
    323323as the new type.
    324 The function does the parent check and returns the appropriate value.
    325 The parent check is a simple linear search of the child's ancestors using the
     324So the function does the parent check and returns the appropriate value.
     325The parent check is a simple linear search of child's ancestors using the
    326326type information.
    327327
     
    329329% The implementation of exception types.
    330330
    331 Creating exceptions can be roughly divided into two parts:
     331Creating exceptions can roughly divided into two parts,
    332332the exceptions themselves and the virtual system interactions.
    333333
     
    361361
    362362All types associated with a virtual type,
    363 the types of the virtual table and the type ID,
     363the types of the virtual table and the type id,
    364364are generated when the virtual type (the exception) is first found.
    365 The type ID (the instance) is generated with the exception, if it is
     365The type id (the instance) is generated with the exception, if it is
    366366a monomorphic type.
    367 However, if the exception is polymorphic, then a different type ID has to
     367However, if the exception is polymorphic, then a different type id has to
    368368be generated for every instance. In this case, generation is delayed
    369369until a virtual table is created.
     
    372372When a virtual table is created and initialized, two functions are created
    373373to fill in the list of virtual members.
    374 The first is the @copy@ function that adapts the exception's copy constructor
     374The first is a copy function that adapts the exception's copy constructor
    375375to work with pointers, avoiding some issues with the current copy constructor
    376376interface.
    377 Second is the @msg@ function that returns a C-string with the type's name,
     377Second is the msg function that returns a C-string with the type's name,
    378378including any polymorphic parameters.
    379379
     
    402402
    403403% Discussing multiple frame stack unwinding:
    404 Unwinding across multiple stack frames is more complex, because that
     404Unwinding across multiple stack frames is more complex because that
    405405information is no longer contained within the current function.
    406406With separate compilation,
    407407a function does not know its callers nor their frame layout.
    408408Even using the return address, that information is encoded in terms of
    409 actions in code, intermixed with the actions required to finish the function.
     409actions in code, intermixed with the actions required finish the function.
    410410Without changing the main code path it is impossible to select one of those
    411411two groups of actions at the return site.
    412412
    413 The traditional unwinding mechanism for C is implemented by saving a snapshot
    414 of a function's state with @setjmp@ and restoring that snapshot with
     413The traditional unwinding mechanism for C is implemented by saving a snap-shot
     414of a function's state with @setjmp@ and restoring that snap-shot with
    415415@longjmp@. This approach bypasses the need to know stack details by simply
    416 resetting to a snapshot of an arbitrary but existing function frame on the
    417 stack. It is up to the programmer to ensure the snapshot is valid when it is
    418 reset and that all required cleanup from the unwound stacks is performed.
    419 Because it does not automate or check any of this cleanup,
    420 it can be easy to make mistakes and always must be handled manually.
     416reseting to a snap-shot of an arbitrary but existing function frame on the
     417stack. It is up to the programmer to ensure the snap-shot is valid when it is
     418reset and that all required clean-up from the unwound stacks is performed.
     419This approach is fragile and requires extra work in the surrounding code.
    421420
    422421With respect to the extra work in the surrounding code,
    423 many languages define cleanup actions that must be taken when certain
    424 sections of the stack are removed, such as when the storage for a variable
     422many languages define clean-up actions that must be taken when certain
     423sections of the stack are removed. Such as when the storage for a variable
    425424is removed from the stack, possibly requiring a destructor call,
    426425or when a try statement with a finally clause is
    427426(conceptually) popped from the stack.
    428 None of these cases should be handled by the user -- that would contradict the
    429 intention of these features -- so they need to be handled automatically.
     427None of these cases should be handled by the user --- that would contradict the
     428intention of these features --- so they need to be handled automatically.
    430429
    431430To safely remove sections of the stack, the language must be able to find and
    432 run these cleanup actions even when removing multiple functions unknown at
     431run these clean-up actions even when removing multiple functions unknown at
    433432the beginning of the unwinding.
    434433
     
    436435library that provides tools for stack walking, handler execution, and
    437436unwinding. What follows is an overview of all the relevant features of
    438 libunwind needed for this work.
    439 Following that is the description of the \CFA code that uses libunwind
    440 to implement termination.
     437libunwind needed for this work, and how \CFA uses them to implement exception
     438handling.
    441439
    442440\subsection{libunwind Usage}
     
    531529provided storage object. It has two public fields: the @exception_class@,
    532530which is described above, and the @exception_cleanup@ function.
    533 The cleanup function is used by the EHM to clean up the exception. If it
     531The clean-up function is used by the EHM to clean-up the exception, if it
    534532should need to be freed at an unusual time, it takes an argument that says
    535533why it had to be cleaned up.
     
    553551of the most recent stack frame. It continues to call personality functions
    554552traversing the stack from newest to oldest until a function finds a handler or
    555 the end of the stack is reached. In the latter case,
    556 @_Unwind_RaiseException@ returns @_URC_END_OF_STACK@.
    557 
    558 Second, when a handler is matched, @_Unwind_RaiseException@
    559 moves to the cleanup phase and walks the stack a second time.
     553the end of the stack is reached. In the latter case, raise exception returns
     554@_URC_END_OF_STACK@.
     555
     556Second, when a handler is matched, raise exception moves to the clean-up
     557phase and walks the stack a second time.
    560558Once again, it calls the personality functions of each stack frame from newest
    561559to oldest. This pass stops at the stack frame containing the matching handler.
    562 If that personality function has not installed a handler, it is an error.
    563 
    564 If an error is encountered, @_Unwind_RaiseException@ returns either
     560If that personality function has not install a handler, it is an error.
     561
     562If an error is encountered, raise exception returns either
    565563@_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the
    566564error occurred.
     
    573571        _Unwind_Stop_Fn, void *);
    574572\end{cfa}
    575 It also unwinds the stack but it does not use the search phase. Instead,
    576 another
     573It also unwinds the stack but it does not use the search phase. Instead another
    577574function, the stop function, is used to stop searching. The exception is the
    578 same as the one passed to @_Unwind_RaiseException@.
    579 The extra arguments are the stop
     575same as the one passed to raise exception. The extra arguments are the stop
    580576function and the stop parameter. The stop function has a similar interface as a
    581577personality function, except it is also passed the stop parameter.
     
    725721one list per stack, with the
    726722list head stored in the exception context. Within each linked list, the most
    727 recently thrown exception is at the head, followed by older thrown
     723recently thrown exception is at the head followed by older thrown
    728724exceptions. This format allows exceptions to be thrown, while a different
    729725exception is being handled. The exception at the head of the list is currently
     
    736732exception into managed memory. After the exception is handled, the free
    737733function is used to clean up the exception and then the entire node is
    738 passed to @free@, returning the memory back to the heap.
     734passed to free, returning the memory back to the heap.
    739735
    740736\subsection{Try Statements and Catch Clauses}
     
    761757The three functions passed to try terminate are:
    762758\begin{description}
    763 \item[try function:] This function is the try block. It is where all the code
     759\item[try function:] This function is the try block, it is where all the code
    764760from inside the try block is placed. It takes no parameters and has no
    765761return value. This function is called during regular execution to run the try
     
    770766from the conditional part of each handler and runs each check, top to bottom,
    771767in turn, to see if the exception matches this handler.
    772 The match is performed in two steps: first, a virtual cast is used to check
     768The match is performed in two steps, first a virtual cast is used to check
    773769if the raised exception is an instance of the declared exception type or
    774770one of its descendant types, and then the condition is evaluated, if
    775771present.
    776772The match function takes a pointer to the exception and returns 0 if the
    777 exception is not handled here. Otherwise, the return value is the ID of the
     773exception is not handled here. Otherwise the return value is the id of the
    778774handler that matches the exception.
    779775
     
    786782\end{description}
    787783All three functions are created with GCC nested functions. GCC nested functions
    788 can be used to create closures;
     784can be used to create closures,
    789785in other words,
    790 functions that can refer to variables in their lexical scope even though
     786functions that can refer to variables in their lexical scope even
    791787those variables are part of a different function.
    792788This approach allows the functions to refer to all the
     
    873869At each node, the EHM checks to see if the try statement the node represents
    874870can handle the exception. If it can, then the exception is handled and
    875 the operation finishes; otherwise, the search continues to the next node.
     871the operation finishes, otherwise the search continues to the next node.
    876872If the search reaches the end of the list without finding a try statement
    877873with a handler clause
     
    883879if the exception is handled and false otherwise.
    884880The handler function checks each of its internal handlers in order,
    885 top-to-bottom, until it finds a match. If a match is found that handler is
     881top-to-bottom, until it funds a match. If a match is found that handler is
    886882run, after which the function returns true, ignoring all remaining handlers.
    887883If no match is found the function returns false.
    888 The match is performed in two steps. First a virtual cast is used to see
     884The match is performed in two steps, first a virtual cast is used to see
    889885if the raised exception is an instance of the declared exception type or one
    890 of its descendant types, if so, then the second step is to see if the
    891 exception passes the custom predicate
     886of its descendant types, if so then it is passed to the custom predicate
    892887if one is defined.
    893888% You need to make sure the type is correct before running the predicate
     
    941936% Recursive Resumption Stuff:
    942937\autoref{f:ResumptionMarking} shows search skipping
    943 (see \autoref{s:ResumptionMarking}), which ignores parts of
     938(see \vpageref{s:ResumptionMarking}), which ignores parts of
    944939the stack
    945940already examined, and is accomplished by updating the front of the list as
     
    956951This structure also supports new handlers added while the resumption is being
    957952handled. These are added to the front of the list, pointing back along the
    958 stack -- the first one points over all the checked handlers --
     953stack --- the first one points over all the checked handlers ---
    959954and the ordering is maintained.
    960955
     
    984979%\autoref{code:cleanup}
    985980A finally clause is handled by converting it into a once-off destructor.
    986 The code inside the clause is placed into a GCC nested-function
     981The code inside the clause is placed into GCC nested-function
    987982with a unique name, and no arguments or return values.
    988983This nested function is
    989984then set as the cleanup function of an empty object that is declared at the
    990985beginning of a block placed around the context of the associated try
    991 statement, as shown in \autoref{f:FinallyTransformation}.
     986statement (see \autoref{f:FinallyTransformation}).
    992987
    993988\begin{figure}
     
    10291024% Stack selections, the three internal unwind functions.
    10301025
    1031 Cancellation also uses libunwind to do its stack traversal and unwinding.
    1032 However, it uses a different primary function: @_Unwind_ForcedUnwind@. Details
    1033 of its interface can be found in Section~\vref{s:ForcedUnwind}.
     1026Cancellation also uses libunwind to do its stack traversal and unwinding,
     1027however it uses a different primary function: @_Unwind_ForcedUnwind@. Details
     1028of its interface can be found in the Section~\vref{s:ForcedUnwind}.
    10341029
    10351030The first step of cancellation is to find the cancelled stack and its type:
  • doc/theses/andrew_beach_MMath/intro.tex

    r949339b r4e28d2e9  
    2525All types of exception handling link a raise with a handler.
    2626Both operations are usually language primitives, although raises can be
    27 treated as a function that takes an exception argument.
    28 Handlers are more complex, as they are added to and removed from the stack
    29 during execution, must specify what they can handle and must give the code to
     27treated as a primitive 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 give the code to
    3030handle the exception.
    3131
     
    3939it returns control to that function.
    4040\begin{center}
    41 %\input{termination}
    42 %
    43 %\medskip
    44 \input{termhandle.pstex_t}
    45 % I hate these diagrams, but I can't access xfig to fix them and they are
    46 % better than the alternative.
     41\input{termination}
    4742\end{center}
    4843
     
    5146The handler is run on top of the existing stack, often as a new function or
    5247closure capturing the context in which the handler was defined.
    53 After the handler has finished running, it returns control to the function
     48After the handler has finished running it returns control to the function
    5449that preformed the raise, usually starting after the raise.
    5550\begin{center}
    56 %\input{resumption}
    57 %
    58 %\medskip
    59 \input{resumhandle.pstex_t}
    60 % The other one.
     51\input{resumption}
    6152\end{center}
    6253
    6354Although a powerful feature, exception handling tends to be complex to set up
    64 and expensive to use,
     55and expensive to use
    6556so it is often limited to unusual or ``exceptional" cases.
    66 The classic example is error handling; exceptions can be used to
     57The classic example is error handling, exceptions can be used to
    6758remove error handling logic from the main execution path, and pay
    6859most of the cost only when the error actually occurs.
     
    7263The \CFA EHM implements all of the common exception features (or an
    7364equivalent) found in most other EHMs and adds some features of its own.
    74 The design of all the features had to be adapted to \CFA's feature set, as
     65The design of all the features had to be adapted to \CFA's feature set as
    7566some of the underlying tools used to implement and express exception handling
    7667in other languages are absent in \CFA.
    77 Still, the resulting syntax resembles that of other languages:
     68Still the resulting syntax resembles that of other languages:
    7869\begin{cfa}
    7970try {
     
    9788covering both changes to the compiler and the run-time.
    9889In addition, a suite of test cases and performance benchmarks were created
    99 alongside the implementation.
     90along side the implementation.
    10091The implementation techniques are generally applicable in other programming
    10192languages and much of the design is as well.
     
    109100\item Implementing stack unwinding and the \CFA EHM, including updating
    110101the \CFA compiler and the run-time environment.
    111 \item Designing and implementing a prototype virtual system.
     102\item Designed and implemented a prototype virtual system.
    112103% I think the virtual system and per-call site default handlers are the only
    113104% "new" features, everything else is a matter of implementation.
    114105\item Creating tests to check the behaviour of the EHM.
    115 \item Creating benchmarks to check the performance of the EHM,
     106\item Creating benchmarks to check the performances of the EHM,
    116107as compared to other languages.
    117108\end{enumerate}
     
    119110The rest of this thesis is organized as follows.
    120111The current state of exceptions is covered in \autoref{s:background}.
    121 The existing state of \CFA is covered in \autoref{c:existing}.
     112The existing state of \CFA is also covered in \autoref{c:existing}.
    122113New EHM features are introduced in \autoref{c:features},
    123114covering their usage and design.
     
    138129message as a payload\cite{Ada12}.
    139130
    140 The modern flagship for termination exceptions -- if one exists -- is \Cpp,
     131The modern flag-ship for termination exceptions is \Cpp,
    141132which added them in its first major wave of non-object-orientated features
    142133in 1990.\cite{CppHistory}
     
    146137inheriting from
    147138\code{C++}{std::exception}.
    148 Although there is a special catch-all syntax (@catch(...)@), there are no
     139Although there is a special catch-all syntax (@catch(...)@) there are no
    149140operations that can be performed on the caught value, not even type inspection.
    150 Instead, the base exception-type \code{C++}{std::exception} defines common
     141Instead the base exception-type \code{C++}{std::exception} defines common
    151142functionality (such as
    152143the ability to describe the reason the exception was raised) and all
     
    157148
    158149Java was the next popular language to use exceptions.\cite{Java8}
    159 Its exception system largely reflects that of \Cpp, except that it requires
     150Its exception system largely reflects that of \Cpp, except that requires
    160151you throw a child type of \code{Java}{java.lang.Throwable}
    161152and it uses checked exceptions.
    162153Checked exceptions are part of a function's interface,
    163154the exception signature of the function.
    164 Every exception that could be raised from a function, either directly or
     155Every function that could be raised from a function, either directly or
    165156because it is not handled from a called function, is given.
    166157Using this information, it is possible to statically verify if any given
    167 exception is handled, and guarantee that no exception will go unhandled.
     158exception is handled and guarantee that no exception will go unhandled.
    168159Making exception information explicit improves clarity and safety,
    169160but can slow down or restrict programming.
     
    178169recovery or repair. In theory that could be good enough to properly handle
    179170the exception, but more often is used to ignore an exception that the       
    180 programmer does not feel is worth the effort of handling, for instance if
     171programmer does not feel is worth the effort of handling it, for instance if
    181172they do not believe it will ever be raised.
    182 If they are incorrect, the exception will be silenced, while in a similar
     173If they are incorrect the exception will be silenced, while in a similar
    183174situation with unchecked exceptions the exception would at least activate   
    184 the language's unhandled exception code (usually, a program abort with an
     175the language's unhandled exception code (usually program abort with an 
    185176error message).
    186177
    187178%\subsection
    188179Resumption exceptions are less popular,
    189 although resumption is as old as termination; that is, few
     180although resumption is as old as termination; hence, few
    190181programming languages have implemented them.
    191182% http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/parc/techReports/
     
    195186included in the \Cpp standard.
    196187% https://en.wikipedia.org/wiki/Exception_handling
    197 Since then, resumptions have been ignored in mainstream programming languages.
     188Since then resumptions have been ignored in main-stream programming languages.
    198189However, resumption is being revisited in the context of decades of other
    199190developments in programming languages.
    200191While rejecting resumption may have been the right decision in the past,
    201192the situation has changed since then.
    202 Some developments, such as the functional programming equivalent to resumptions,
     193Some developments, such as the function programming equivalent to resumptions,
    203194algebraic effects\cite{Zhang19}, are enjoying success.
    204 A complete reexamination of resumption is beyond this thesis,
    205 but their reemergence is enough reason to try them in \CFA.
     195A complete reexamination of resumptions is beyond this thesis,
     196but there reemergence is enough to try them in \CFA.
    206197% Especially considering how much easier they are to implement than
    207198% termination exceptions and how much Peter likes them.
     
    217208
    218209%\subsection
    219 More recently, exceptions seem to be vanishing from newer programming
     210More recently exceptions seem to be vanishing from newer programming
    220211languages, replaced by ``panic".
    221212In Rust, a panic is just a program level abort that may be implemented by
    222213unwinding the stack like in termination exception
    223214handling.\cite{RustPanicMacro}\cite{RustPanicModule}
    224 Go's panic though is very similar to a termination, except it only supports
     215Go's panic through is very similar to a termination, except it only supports
    225216a catch-all by calling \code{Go}{recover()}, simplifying the interface at
    226217the cost of flexibility.\cite{Go:2021}
    227218
    228219%\subsection
    229 As exception handling's most common use cases are in error handling,
     220While exception handling's most common use cases are in error handling,
    230221here are some other ways to handle errors with comparisons with exceptions.
    231222\begin{itemize}
     
    242233is discarded to avoid this problem.
    243234Checking error codes also bloats the main execution path,
    244 especially if the error is not handled immediately and has to be passed
     235especially if the error is not handled immediately hand has to be passed
    245236through multiple functions before it is addressed.
    246 
    247 Here is an example of the pattern in Bash, where commands can only  ``return"
    248 numbers and most output is done through streams of text.
    249 \begin{lstlisting}[language=bash,escapechar={}]
    250 # Immediately after running a command:
    251 case $? in
    252 0)
    253         # Success
    254         ;;
    255 1)
    256         # Error Code 1
    257         ;;
    258 2|3)
    259         # Error Code 2 or Error Code 3
    260         ;;
    261 # Add more cases as needed.
    262 asac
    263 \end{lstlisting}
    264237
    265238\item\emph{Special Return with Global Store}:
    266239Similar to the error codes pattern but the function itself only returns
    267 that there was an error,
    268 and stores the reason for the error in a fixed global location.
    269 For example, many routines in the C standard library will only return some
     240that there was an error
     241and store the reason for the error in a fixed global location.
     242For example many routines in the C standard library will only return some
    270243error value (such as -1 or a null pointer) and the error code is written into
    271244the standard variable @errno@.
    272245
    273246This approach avoids the multiple results issue encountered with straight
    274 error codes as only a single error value has to be returned,
    275 but otherwise has the same disadvantages and more.
     247error codes but otherwise has the same disadvantages and more.
    276248Every function that reads or writes to the global store must agree on all
    277249possible errors and managing it becomes more complex with concurrency.
    278 
    279 This example shows some of what has to be done to robustly handle a C
    280 standard library function that reports errors this way.
    281 \begin{lstlisting}[language=C]
    282 // Now a library function can set the error.
    283 int handle = open(path_name, flags);
    284 if (-1 == handle) {
    285         switch (errno) {
    286     case ENAMETOOLONG:
    287                 // path_name is a bad argument.
    288                 break;
    289         case ENFILE:
    290                 // A system resource has been exausted.
    291                 break;
    292         // And many more...
    293     }
    294 }
    295 \end{lstlisting}
    296 % cite open man page?
    297250
    298251\item\emph{Return Union}:
     
    300253Success is one tag and the errors are another.
    301254It is also possible to make each possible error its own tag and carry its own
    302 additional information, but the two-branch format is easy to make generic
     255additional information, but the two branch format is easy to make generic
    303256so that one type can be used everywhere in error handling code.
    304257
    305258This pattern is very popular in any functional or semi-functional language
    306259with primitive support for tagged unions (or algebraic data types).
    307 Return unions can also be expressed as monads (evaluation in a context)
    308 and often are in languages with special syntax for monadic evaluation,
    309 such as Haskell's \code{haskell}{do} blocks.
    310 
    311 The main advantage is that an arbitrary object can be used to represent an
    312 error, so it can include a lot more information than a simple error code.
    313 The disadvantages include that the it does have to be checked along the main
    314 execution, and if there aren't primitive tagged unions proper, usage can be
    315 hard to enforce.
    316260% We need listing Rust/rust to format code snippets from it.
    317261% Rust's \code{rust}{Result<T, E>}
    318 
    319 This is a simple example of examining the result of a failing function in
    320 Haskell, using its \code{haskell}{Either} type.
    321 Examining \code{haskell}{error} further would likely involve more matching,
    322 but the type of \code{haskell}{error} is user defined so there are no
    323 general cases.
    324 \begin{lstlisting}[language=haskell]
    325 case failingFunction argA argB of
    326     Right value -> -- Use the successful computed value.
    327     Left error -> -- Handle the produced error.
    328 \end{lstlisting}
    329 
    330 Return unions as monads will result in the same code, but can hide most
    331 of the work to propagate errors in simple cases. The code to actually handle
    332 the errors, or to interact with other monads (a common case in these
    333 languages) still has to be written by hand.
    334 
    335 If \code{haskell}{failingFunction} is implemented with two helpers that
    336 use the same error type, then it can be implemented with a \code{haskell}{do}
    337 block.
    338 \begin{lstlisting}[language=haskell,literate={}]
    339 failingFunction x y = do
    340         z <- helperOne x
    341         helperTwo y z
    342 \end{lstlisting}
     262The main advantage is that an arbitrary object can be used to represent an
     263error so it can include a lot more information than a simple error code.
     264The disadvantages include that the it does have to be checked along the main
     265execution and if there aren't primitive tagged unions proper usage can be
     266hard to enforce.
    343267
    344268\item\emph{Handler Functions}:
     
    350274variable).
    351275C++ uses this approach as its fallback system if exception handling fails,
    352 such as \snake{std::terminate} and, for a time,
    353 \snake{std::unexpected}.\footnote{\snake{std::unexpected} was part of the
    354 Dynamic Exception Specification, which has been removed from the standard
    355 as of C++20.\cite{CppExceptSpec}}
     276such as \snake{std::terminate_handler} and, for a time,
     277\snake{std::unexpected_handler}.
    356278
    357279Handler functions work a lot like resumption exceptions,
     
    361283function calls, but cheaper (constant time) to call,
    362284they are more suited to more frequent (less exceptional) situations.
    363 Although, in \Cpp and other languages that do not have checked exceptions,
    364 they can actually be enforced by the type system be more reliable.
    365 
    366 This is a more local example in \Cpp, using a function to provide
    367 a default value for a mapping.
    368 \begin{lstlisting}[language=C++]
    369 ValueT Map::key_or_default(KeyT key, ValueT(*make_default)(KeyT)) {
    370         ValueT * value = find_value(key);
    371         if (nullptr != value) {
    372                 return *value;
    373         } else {
    374                 return make_default(key);
    375         }
    376 }
    377 \end{lstlisting}
    378285\end{itemize}
    379286
     
    381288Because of their cost, exceptions are rarely used for hot paths of execution.
    382289Hence, there is an element of self-fulfilling prophecy as implementation
    383 techniques have been focused on making them cheap to set up,
     290techniques have been focused on making them cheap to set-up,
    384291happily making them expensive to use in exchange.
    385292This difference is less important in higher-level scripting languages,
    386 where using exceptions for other tasks is more common.
     293where using exception for other tasks is more common.
    387294An iconic example is Python's
    388 \code{Python}{StopIteration}\cite{PythonExceptions} exception, that
     295\code{Python}{StopIteration}\cite{PythonExceptions} exception that
    389296is thrown by an iterator to indicate that it is exhausted.
    390 When paired with Python's iterator-based for-loop, this will be thrown every
     297When paired with Python's iterator-based for-loop this will be thrown every
    391298time the end of the loop is reached.\cite{PythonForLoop}
  • doc/theses/andrew_beach_MMath/performance.tex

    r949339b r4e28d2e9  
    55Instead, 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. Hence, only a few basic performance tests were performed to
     7amount of time. Hence, a few basic performance tests were performed to
    88check this requirement.
    99
     
    1313one with termination and one with resumption.
    1414
    15 GCC C++ is the most comparable language because both it and \CFA use the same
     15C++ is the most comparable language because both it and \CFA use the same
    1616framework, libunwind.
    1717In fact, the comparison is almost entirely in quality of implementation.
     
    3737resumption exceptions. Even the older programming languages with resumption
    3838seem to be notable only for having resumption.
    39 On the other hand, the functional equivalents to resumption are too new.
    40 There does not seem to be any standard implementations in well-known
    41 languages; so far, they seem confined to extensions and research languages.
    42 % There was some maybe interesting comparison to an OCaml extension
    43 % but I'm not sure how to get that working if it is interesting.
    4439Instead, resumption is compared to its simulation in other programming
    4540languages: fixup functions that are explicitly passed into a function.
     
    5146the number used in the timing runs is given with the results per test.
    5247The Java tests run the main loop 1000 times before
    53 beginning the actual test to ``warm up" the JVM.
     48beginning the actual test to ``warm-up" the JVM.
    5449% All other languages are precompiled or interpreted.
    5550
     
    5954unhandled exceptions in \Cpp and Java as that would cause the process to
    6055terminate.
    61 Luckily, performance on the ``give up and kill the process" path is not
     56Luckily, performance on the ``give-up and kill the process" path is not
    6257critical.
    6358
     
    8176using gcc-10 10.3.0 as a backend.
    8277g++-10 10.3.0 is used for \Cpp.
    83 Java tests are complied and run with Oracle OpenJDK version 11.0.11.
    84 Python used CPython version 3.8.10.
     78Java tests are complied and run with version 11.0.11.
     79Python used version 3.8.10.
    8580The machines used to run the tests are:
    8681\begin{itemize}[nosep]
     
    9085      \lstinline{@} 2.5 GHz running Linux v5.11.0-25
    9186\end{itemize}
    92 These represent the two major families of hardware architecture.
     87Representing the two major families of hardware architecture.
    9388
    9489\section{Tests}
     
    9893
    9994\paragraph{Stack Traversal}
    100 This group of tests measures the cost of traversing the stack
     95This group measures the cost of traversing the stack,
    10196(and in termination, unwinding it).
    10297Inside the main loop is a call to a recursive function.
     
    152147This group of tests measures the cost for setting up exception handling,
    153148if it is
    154 not used because the exceptional case did not occur.
     149not used (because the exceptional case did not occur).
    155150Tests repeatedly cross (enter, execute and leave) a try statement but never
    156151perform a raise.
     
    227222for that language and the result is marked N/A.
    228223There are also cases where the feature is supported but measuring its
    229 cost is impossible. This happened with Java, which uses a JIT that optimizes
    230 away the tests and cannot be stopped.\cite{Dice21}
     224cost is impossible. This happened with Java, which uses a JIT that optimize
     225away the tests and it cannot be stopped.\cite{Dice21}
    231226These tests are marked N/C.
    232227To get results in a consistent range (1 second to 1 minute is ideal,
     
    235230results and has a value in the millions.
    236231
    237 An anomaly in some results came from \CFA's use of GCC nested functions.
     232An anomaly in some results came from \CFA's use of gcc nested functions.
    238233These nested functions are used to create closures that can access stack
    239234variables in their lexical scope.
    240 However, if they do so, then they can cause the benchmark's run time to
     235However, if they do so, then they can cause the benchmark's run-time to
    241236increase by an order of magnitude.
    242237The simplest solution is to make those values global variables instead
    243 of function-local variables.
     238of function local variables.
    244239% Do we know if editing a global inside nested function is a problem?
    245240Tests that had to be modified to avoid this problem have been marked
     
    260255                         \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c|}{Python} \\
    261256\hline
    262 Empty Traversal (1M)   & 23.0  & 9.6   & 17.6  & 23.4      & 30.6  & 13.6  & 15.5  & 14.7  \\
    263 D'tor Traversal (1M)   & 48.1  & 23.5  & N/A   & N/A       & 64.2  & 29.2  & N/A   & N/A   \\
    264 Finally Traversal (1M) & 3.2*  & N/A   & 17.6  & 29.2      & 3.9*  & N/A   & 15.5  & 19.0  \\
    265 Other Traversal (1M)   & 3.3*  & 23.9  & 17.7  & 32.8      & 3.9*  & 24.5  & 15.5  & 21.6  \\
    266 Cross Handler (1B)     & 6.5   & 0.9   & N/C   & 38.0      & 9.6   & 0.8   & N/C   & 32.1  \\
    267 Cross Finally (1B)     & 0.8   & N/A   & N/C   & 44.6      & 0.6   & N/A   & N/C   & 37.3  \\
    268 Match All (10M)        & 30.5  & 20.6  & 11.2  & 3.9       & 36.9  & 24.6  & 10.7  & 3.1   \\
    269 Match None (10M)       & 30.6  & 50.9  & 11.2  & 5.0       & 36.9  & 71.9  & 10.7  & 4.1   \\
     257Empty Traversal (1M)   & 3.4   & 2.8   & 18.3  & 23.4      & 3.7   & 3.2   & 15.5  & 14.8  \\
     258D'tor Traversal (1M)   & 48.4  & 23.6  & N/A   & N/A       & 64.2  & 29.0  & N/A   & N/A   \\
     259Finally Traversal (1M) & 3.4*  & N/A   & 17.9  & 29.0      & 4.1*  & N/A   & 15.6  & 19.0  \\
     260Other Traversal (1M)   & 3.6*  & 23.2  & 18.2  & 32.7      & 4.0*  & 24.5  & 15.5  & 21.4  \\
     261Cross Handler (100M)   & 6.0   & 0.9   & N/C   & 37.4      & 10.0  & 0.8   & N/C   & 32.2  \\
     262Cross Finally (100M)   & 0.9   & N/A   & N/C   & 44.1      & 0.8   & N/A   & N/C   & 37.3  \\
     263Match All (10M)        & 32.9  & 20.7  & 13.4  & 4.9       & 36.2  & 24.5  & 12.0  & 3.1   \\
     264Match None (10M)       & 32.7  & 50.3  & 11.0  & 5.1       & 36.3  & 71.9  & 12.3  & 4.2   \\
    270265\hline
    271266\end{tabular}
     
    281276                        & AMD     & ARM  \\
    282277\hline
    283 Empty Traversal (10M)   & 1.4     & 1.2  \\
     278Empty Traversal (10M)   & 0.2     & 0.3  \\
    284279D'tor Traversal (10M)   & 1.8     & 1.0  \\
    285 Finally Traversal (10M) & 1.8     & 1.0  \\
    286 Other Traversal (10M)   & 22.6    & 25.8 \\
    287 Cross Handler (1B)      & 9.0     & 11.9 \\
     280Finally Traversal (10M) & 1.7     & 1.0  \\
     281Other Traversal (10M)   & 22.6    & 25.9 \\
     282Cross Handler (100M)    & 8.4     & 11.9 \\
    288283Match All (100M)        & 2.3     & 3.2  \\
    289 Match None (100M)       & 3.0     & 3.8  \\
     284Match None (100M)       & 2.9     & 3.9  \\
    290285\hline
    291286\end{tabular}
     
    305300              \multicolumn{1}{c}{Raise} & \multicolumn{1}{c}{\CFA} & \multicolumn{1}{c}{\Cpp} & \multicolumn{1}{c}{Java} & \multicolumn{1}{c|}{Python} \\
    306301\hline
    307 Resume Empty (10M)  & 1.4 & 1.4 & 15.4 & 2.3 & 178.0  & 1.2 & 1.2 & 8.9 & 1.2 & 118.4 \\
     302Resume Empty (10M)  & 1.5 & 1.5 & 14.7 & 2.3 & 176.1  & 1.0 & 1.4 & 8.9 & 1.2 & 119.9 \\
    308303\hline
    309304\end{tabular}
     
    317312\CFA, \Cpp and Java.
    318313% To be exact, the Match All and Match None cases.
    319 The most likely explanation is that
    320 the generally faster languages have made ``common cases fast" at the expense
    321 of the rarer cases. Since exceptions are considered rare, they are made
    322 expensive to help speed up common actions, such as entering and leaving try
    323 statements.
    324 Python, on the other hand, while generally slower than the other languages,
    325 uses exceptions more and has not sacrificed their performance.
     314The most likely explanation is that, since exceptions
     315are rarely considered to be the common case, the more optimized languages
     316make that case expensive to improve other cases.
    326317In addition, languages with high-level representations have a much
    327318easier time scanning the stack as there is less to decode.
     
    355346Performance is similar to Empty Traversal in all languages that support finally
    356347clauses. Only Python seems to have a larger than random noise change in
    357 its run time and it is still not large.
     348its run-time and it is still not large.
    358349Despite the similarity between finally clauses and destructors,
    359 finally clauses seem to avoid the spike that run time destructors have.
     350finally clauses seem to avoid the spike that run-time destructors have.
    360351Possibly some optimization removes the cost of changing contexts.
    361352
     
    365356This results in a significant jump.
    366357
    367 Other languages experience a small increase in run time.
     358Other languages experience a small increase in run-time.
    368359The small increase likely comes from running the checks,
    369360but they could avoid the spike by not having the same kind of overhead for
     
    371362
    372363\item[Cross Handler]
    373 Here, \CFA falls behind \Cpp by a much more significant margin.
    374 This is likely due to the fact that \CFA has to insert two extra function
    375 calls, while \Cpp does not have to execute any other instructions.
     364Here \CFA falls behind \Cpp by a much more significant margin.
     365This is likely due to the fact \CFA has to insert two extra function
     366calls, while \Cpp does not have to do execute any other instructions.
    376367Python is much further behind.
    377368
     
    384375\item[Conditional Match]
    385376Both of the conditional matching tests can be considered on their own.
    386 However, for evaluating the value of conditional matching itself, the
     377However for evaluating the value of conditional matching itself, the
    387378comparison of the two sets of results is useful.
    388 Consider the massive jump in run time for \Cpp going from match all to match
     379Consider the massive jump in run-time for \Cpp going from match all to match
    389380none, which none of the other languages have.
    390 Some strange interaction is causing run time to more than double for doing
     381Some strange interaction is causing run-time to more than double for doing
    391382twice as many raises.
    392 Java and Python avoid this problem and have similar run time for both tests,
     383Java and Python avoid this problem and have similar run-time for both tests,
    393384possibly through resource reuse or their program representation.
    394 However, \CFA is built like \Cpp, and avoids the problem as well.
    395 This matches
     385However \CFA is built like \Cpp and avoids the problem as well, this matches
    396386the pattern of the conditional match, which makes the two execution paths
    397387very similar.
     
    401391\subsection{Resumption \texorpdfstring{(\autoref{t:PerformanceResumption})}{}}
    402392
    403 Moving on to resumption, there is one general note:
     393Moving on to resumption, there is one general note,
    404394resumption is \textit{fast}. The only test where it fell
    405395behind termination is Cross Handler.
    406396In every other case, the number of iterations had to be increased by a
    407 factor of 10 to get the run time in an appropriate range
     397factor of 10 to get the run-time in an appropriate range
    408398and in some cases resumption still took less time.
    409399
     
    418408
    419409\item[D'tor Traversal]
    420 Resumption does have the same spike in run time that termination has.
    421 The run time is actually very similar to Finally Traversal.
     410Resumption does have the same spike in run-time that termination has.
     411The run-time is actually very similar to Finally Traversal.
    422412As resumption does not unwind the stack, both destructors and finally
    423413clauses are run while walking down the stack during the recursive returns.
     
    426416\item[Finally Traversal]
    427417Same as D'tor Traversal,
    428 except termination did not have a spike in run time on this test case.
     418except termination did not have a spike in run-time on this test case.
    429419
    430420\item[Other Traversal]
     
    437427The only test case where resumption could not keep up with termination,
    438428although the difference is not as significant as many other cases.
    439 It is simply a matter of where the costs come from:
    440 both termination and resumption have some work to set up or tear down a
     429It is simply a matter of where the costs come from,
     430both termination and resumption have some work to set-up or tear-down a
    441431handler. It just so happens that resumption's work is slightly slower.
    442432
     
    444434Resumption shows a slight slowdown if the exception is not matched
    445435by the first handler, which follows from the fact the second handler now has
    446 to be checked. However, the difference is not large.
     436to be checked. However the difference is not large.
    447437
    448438\end{description}
     
    458448More experiments could try to tease out the exact trade-offs,
    459449but the prototype's only performance goal is to be reasonable.
    460 It is already in that range, and \CFA's fixup routine simulation is
     450It has already in that range, and \CFA's fixup routine simulation is
    461451one of the faster simulations as well.
    462 Plus, exceptions add features and remove syntactic overhead,
    463 so even at similar performance, resumptions have advantages
     452Plus exceptions add features and remove syntactic overhead,
     453so even at similar performance resumptions have advantages
    464454over fixup routines.
  • doc/theses/andrew_beach_MMath/uw-ethesis-frontpgs.tex

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

    r949339b r4e28d2e9  
    4040}
    4141
    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 
    4942@misc{RustPanicMacro,
    5043    author={The Rust Team},
    5144    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}},
     45    howpublished={\href{https://doc.rust-lang.org/std/panic/index.html}{https://\-doc.rust-lang.org/\-std/\-panic/\-index.html}},
    5346    addendum={Accessed 2021-08-31},
    5447}
  • libcfa/src/Makefile.am

    r949339b r4e28d2e9  
    4848        math.hfa \
    4949        time_t.hfa \
    50         bits/algorithm.hfa \
    5150        bits/align.hfa \
    5251        bits/containers.hfa \
     
    6059        containers/array.hfa \
    6160        concurrency/iofwd.hfa \
     61        concurrency/mutex_stmt.hfa \
    6262        containers/list.hfa \
    6363        containers/queueLockFree.hfa \
     
    7878        memory.hfa \
    7979        parseargs.hfa \
    80         parseconfig.hfa \
    8180        rational.hfa \
    8281        stdlib.hfa \
     
    8786        containers/pair.hfa \
    8887        containers/result.hfa \
    89         containers/string.hfa \
    90         containers/string_res.hfa \
    9188        containers/vector.hfa \
    9289        device/cpu.hfa
     
    9491libsrc = ${inst_headers_src} ${inst_headers_src:.hfa=.cfa} \
    9592        assert.cfa \
     93        bits/algorithm.hfa \
    9694        bits/debug.cfa \
    9795        exception.c \
     
    109107        concurrency/invoke.h \
    110108        concurrency/future.hfa \
    111         concurrency/kernel/fwd.hfa \
    112         concurrency/mutex_stmt.hfa
     109        concurrency/kernel/fwd.hfa
    113110
    114111inst_thread_headers_src = \
     
    196193        $(CFACOMPILE) -quiet -XCFA,-l ${<} -c -o ${@}
    197194
    198 concurrency/io/call.cfa: $(srcdir)/concurrency/io/call.cfa.in
    199         ${AM_V_GEN}python3 $< > $@
    200 
    201195#----------------------------------------------------------------------------------------------------------------
    202196libcfa_la_SOURCES = ${libsrc}
  • libcfa/src/concurrency/clib/cfathread.cfa

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

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

    r949339b r4e28d2e9  
    9090        static inline unsigned __flush( struct $io_context & );
    9191        static inline __u32 __release_sqes( struct $io_context & );
    92         extern void __kernel_unpark( thread$ * thrd, unpark_hint );
     92        extern void __kernel_unpark( thread$ * thrd );
    9393
    9494        bool __cfa_io_drain( processor * proc ) {
     
    118118                        __cfadbg_print_safe( io, "Kernel I/O : Syscall completed : cqe %p, result %d for %p\n", &cqe, cqe.res, future );
    119119
    120                         __kernel_unpark( fulfil( *future, cqe.res, false ), UNPARK_LOCAL );
     120                        __kernel_unpark( fulfil( *future, cqe.res, false ) );
    121121                }
    122122
  • libcfa/src/concurrency/kernel.cfa

    r949339b r4e28d2e9  
    2222#include <errno.h>
    2323#include <stdio.h>
    24 #include <string.h>
    2524#include <signal.h>
    2625#include <unistd.h>
     
    3231#include "kernel_private.hfa"
    3332#include "preemption.hfa"
    34 #include "strstream.hfa"
    35 #include "device/cpu.hfa"
    3633
    3734//Private includes
     
    234231                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    235232
    236                                 {
    237                                         eventfd_t val;
    238                                         ssize_t ret = read( this->idle, &val, sizeof(val) );
    239                                         if(ret < 0) {
    240                                                 switch((int)errno) {
    241                                                 case EAGAIN:
    242                                                 #if EAGAIN != EWOULDBLOCK
    243                                                         case EWOULDBLOCK:
    244                                                 #endif
    245                                                 case EINTR:
    246                                                         // No need to do anything special here, just assume it's a legitimate wake-up
    247                                                         break;
    248                                                 default:
    249                                                         abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    250                                                 }
    251                                         }
    252                                 }
     233                                __disable_interrupts_hard();
     234                                eventfd_t val;
     235                                eventfd_read( this->idle, &val );
     236                                __enable_interrupts_hard();
    253237
    254238                                #if !defined(__CFA_NO_STATISTICS__)
     
    341325                                }
    342326
    343                                 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
     327                                        __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
    344328                                __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle);
    345329
    346                                 {
    347                                         eventfd_t val;
    348                                         ssize_t ret = read( this->idle, &val, sizeof(val) );
    349                                         if(ret < 0) {
    350                                                 switch((int)errno) {
    351                                                 case EAGAIN:
    352                                                 #if EAGAIN != EWOULDBLOCK
    353                                                         case EWOULDBLOCK:
    354                                                 #endif
    355                                                 case EINTR:
    356                                                         // No need to do anything special here, just assume it's a legitimate wake-up
    357                                                         break;
    358                                                 default:
    359                                                         abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    360                                                 }
    361                                         }
    362                                 }
     330                                // __disable_interrupts_hard();
     331                                eventfd_t val;
     332                                eventfd_read( this->idle, &val );
     333                                // __enable_interrupts_hard();
    363334
    364335                                        __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); )
     
    422393        /* paranoid */ verifyf( thrd_dst->link.next == 0p, "Expected null got %p", thrd_dst->link.next );
    423394        __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;
    424405
    425406        __cfadbg_print_safe(runtime_core, "Kernel : core %p running thread %p (%s)\n", this, thrd_dst, thrd_dst->self_cor.name);
     
    476457                if(unlikely(thrd_dst->preempted != __NO_PREEMPTION)) {
    477458                        // The thread was preempted, reschedule it and reset the flag
    478                         schedule_thread$( thrd_dst, UNPARK_LOCAL );
     459                        schedule_thread$( thrd_dst );
    479460                        break RUNNING;
    480461                }
     
    560541// Scheduler routines
    561542// KERNEL ONLY
    562 static void __schedule_thread( thread$ * thrd, unpark_hint hint ) {
     543static void __schedule_thread( thread$ * thrd ) {
    563544        /* paranoid */ verify( ! __preemption_enabled() );
    564545        /* paranoid */ verify( ready_schedule_islocked());
     
    580561        // Dereference the thread now because once we push it, there is not guaranteed it's still valid.
    581562        struct cluster * cl = thrd->curr_cluster;
    582         __STATS(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
     563        __STATS(bool outside = thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
    583564
    584565        // push the thread to the cluster ready-queue
    585         push( cl, thrd, hint );
     566        push( cl, thrd, local );
    586567
    587568        // variable thrd is no longer safe to use
     
    608589}
    609590
    610 void schedule_thread$( thread$ * thrd, unpark_hint hint ) {
     591void schedule_thread$( thread$ * thrd ) {
    611592        ready_schedule_lock();
    612                 __schedule_thread( thrd, hint );
     593                __schedule_thread( thrd );
    613594        ready_schedule_unlock();
    614595}
     
    661642}
    662643
    663 void __kernel_unpark( thread$ * thrd, unpark_hint hint ) {
     644void __kernel_unpark( thread$ * thrd ) {
    664645        /* paranoid */ verify( ! __preemption_enabled() );
    665646        /* paranoid */ verify( ready_schedule_islocked());
     
    669650        if(__must_unpark(thrd)) {
    670651                // Wake lost the race,
    671                 __schedule_thread( thrd, hint );
     652                __schedule_thread( thrd );
    672653        }
    673654
     
    676657}
    677658
    678 void unpark( thread$ * thrd, unpark_hint hint ) {
     659void unpark( thread$ * thrd ) {
    679660        if( !thrd ) return;
    680661
     
    682663                disable_interrupts();
    683664                        // Wake lost the race,
    684                         schedule_thread$( thrd, hint );
     665                        schedule_thread$( thrd );
    685666                enable_interrupts(false);
    686667        }
  • libcfa/src/concurrency/kernel.hfa

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

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

    r949339b r4e28d2e9  
    200200        __cfadbg_print_safe(runtime_core, "Kernel : Main cluster ready\n");
    201201
    202         // Construct the processor context of the main processor
    203         void ?{}(processorCtx_t & this, processor * proc) {
    204                 (this.__cor){ "Processor" };
    205                 this.__cor.starter = 0p;
    206                 this.proc = proc;
    207         }
    208 
    209         void ?{}(processor & this) with( this ) {
    210                 ( this.terminated ){};
    211                 ( this.runner ){};
    212                 init( this, "Main Processor", *mainCluster, 0p );
    213                 kernel_thread = pthread_self();
    214 
    215                 runner{ &this };
    216                 __cfadbg_print_safe(runtime_core, "Kernel : constructed main processor context %p\n", &runner);
    217         }
    218 
    219         // Initialize the main processor and the main processor ctx
    220         // (the coroutine that contains the processing control flow)
    221         mainProcessor = (processor *)&storage_mainProcessor;
    222         (*mainProcessor){};
    223 
    224         register_tls( mainProcessor );
    225 
    226202        // Start by initializing the main thread
    227203        // SKULLDUGGERY: the mainThread steals the process main thread
     
    234210        __cfadbg_print_safe(runtime_core, "Kernel : Main thread ready\n");
    235211
     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
    236239        //initialize the global state variables
    237240        __cfaabi_tls.this_processor = mainProcessor;
     
    249252        // Add the main thread to the ready queue
    250253        // once resume is called on mainProcessor->runner the mainThread needs to be scheduled like any normal thread
    251         schedule_thread$(mainThread, UNPARK_LOCAL);
     254        schedule_thread$(mainThread);
    252255
    253256        // SKULLDUGGERY: Force a context switch to the main processor to set the main thread's context to the current UNIX
     
    483486        link.next = 0p;
    484487        link.ts   = -1llu;
    485         preferred = ready_queue_new_preferred();
     488        preferred = -1u;
    486489        last_proc = 0p;
    487490        #if defined( __CFA_WITH_VERIFY__ )
  • libcfa/src/concurrency/kernel_private.hfa

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

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

    r949339b r4e28d2e9  
    4141    }
    4242
    43     static inline L * __get_ptr( L & this ) {
    44         return &this;
     43    static inline L * __get_pointer( L & lock ) {
     44        return &lock;
    4545    }
    46 
    47     static inline L __get_type( L & this );
    48 
    49     static inline L __get_type( L * this );
    5046}
  • libcfa/src/concurrency/ready_queue.cfa

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

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

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

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

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

    r949339b r4e28d2e9  
    422422        }
    423423}
    424 
    425 cpu_info_t cpu_info;
  • libcfa/src/device/cpu.hfa

    r949339b r4e28d2e9  
    3030};
    3131
    32 extern cpu_info_t cpu_info;
     32cpu_info_t cpu_info;
  • libcfa/src/fstream.cfa

    r949339b r4e28d2e9  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Sep 21 21:51:38 2021
    13 // Update Count     : 460
     12// Last Modified On : Thu Jul 29 22:34:10 2021
     13// Update Count     : 454
    1414//
    1515
     
    2828#define IO_MSG "I/O error: "
    2929
    30 void ?{}( ofstream & os, void * file ) with(os) {
    31         file$ = file;
    32         sepDefault$ = true;
    33         sepOnOff$ = false;
    34         nlOnOff$ = true;
    35         prt$ = false;
    36         sawNL$ = false;
    37         acquired$ = false;
     30void ?{}( ofstream & os, void * file ) {
     31        os.file$ = file;
     32        os.sepDefault$ = true;
     33        os.sepOnOff$ = false;
     34        os.nlOnOff$ = true;
     35        os.prt$ = false;
     36        os.sawNL$ = false;
     37        os.acquired$ = false;
    3838        sepSetCur$( os, sepGet( os ) );
    3939        sepSet( os, " " );
     
    124124void open( ofstream & os, const char name[], const char mode[] ) {
    125125        FILE * file = fopen( name, mode );
     126        #ifdef __CFA_DEBUG__
    126127        if ( file == 0p ) {
    127128                throw (Open_Failure){ os };
    128129                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    129130        } // if
    130         (os){ file };                                                                           // initialize
     131        #endif // __CFA_DEBUG__
     132        (os){ file };
    131133} // open
    132134
     
    135137} // open
    136138
    137 void close( ofstream & os ) with(os) {
    138   if ( (FILE *)(file$) == 0p ) return;
    139   if ( (FILE *)(file$) == (FILE *)stdout || (FILE *)(file$) == (FILE *)stderr ) return;
    140 
    141         if ( fclose( (FILE *)(file$) ) == EOF ) {
     139void close( ofstream & os ) {
     140  if ( (FILE *)(os.file$) == 0p ) return;
     141  if ( (FILE *)(os.file$) == (FILE *)stdout || (FILE *)(os.file$) == (FILE *)stderr ) return;
     142
     143        if ( fclose( (FILE *)(os.file$) ) == EOF ) {
    142144                throw (Close_Failure){ os };
    143145                // abort | IO_MSG "close output" | nl | strerror( errno );
    144146        } // if
    145         file$ = 0p;
     147        os.file$ = 0p;
    146148} // close
    147149
     
    175177} // fmt
    176178
    177 inline void acquire( ofstream & os ) with(os) {
    178         lock( lock$ );                                                                          // may increase recursive lock
    179         if ( ! acquired$ ) acquired$ = true;                            // not locked ?
    180         else unlock( lock$ );                                                           // unwind recursive lock at start
     179inline void acquire( ofstream & os ) {
     180        lock( os.lock$ );
     181        if ( ! os.acquired$ ) os.acquired$ = true;
     182        else unlock( os.lock$ );
    181183} // acquire
    182184
     
    185187} // release
    186188
    187 void ?{}( osacquire & acq, ofstream & os ) { lock( os.lock$ ); &acq.os = &os; }
     189void ?{}( osacquire & acq, ofstream & os ) { &acq.os = &os; lock( os.lock$ ); }
    188190void ^?{}( osacquire & acq ) { release( acq.os ); }
    189191
     
    217219
    218220// private
    219 void ?{}( ifstream & is, void * file ) with(is) {
    220         file$ = file;
    221         nlOnOff$ = false;
    222         acquired$ = false;
     221void ?{}( ifstream & is, void * file ) {
     222        is.file$ = file;
     223        is.nlOnOff$ = false;
     224        is.acquired$ = false;
    223225} // ?{}
    224226
     
    260262void open( ifstream & is, const char name[], const char mode[] ) {
    261263        FILE * file = fopen( name, mode );
     264        #ifdef __CFA_DEBUG__
    262265        if ( file == 0p ) {
    263266                throw (Open_Failure){ is };
    264267                // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
    265268        } // if
    266         (is){ file };                                                                           // initialize
     269        #endif // __CFA_DEBUG__
     270        is.file$ = file;
    267271} // open
    268272
     
    271275} // open
    272276
    273 void close( ifstream & is ) with(is) {
    274   if ( (FILE *)(file$) == 0p ) return;
    275   if ( (FILE *)(file$) == (FILE *)stdin ) return;
    276 
    277         if ( fclose( (FILE *)(file$) ) == EOF ) {
     277void close( ifstream & is ) {
     278  if ( (FILE *)(is.file$) == 0p ) return;
     279  if ( (FILE *)(is.file$) == (FILE *)stdin ) return;
     280
     281        if ( fclose( (FILE *)(is.file$) ) == EOF ) {
    278282                throw (Close_Failure){ is };
    279283                // abort | IO_MSG "close input" | nl | strerror( errno );
    280284        } // if
    281         file$ = 0p;
     285        is.file$ = 0p;
    282286} // close
    283287
     
    320324} // fmt
    321325
    322 inline void acquire( ifstream & is ) with(is) {
    323         lock( lock$ );                                                                          // may increase recursive lock
    324         if ( ! acquired$ ) acquired$ = true;                            // not locked ?
    325         else unlock( lock$ );                                                           // unwind recursive lock at start
     326inline void acquire( ifstream & is ) {
     327        lock( is.lock$ );
     328        if ( ! is.acquired$ ) is.acquired$ = true;
     329        else unlock( is.lock$ );
    326330} // acquire
    327331
     
    330334} // release
    331335
    332 void ?{}( isacquire & acq, ifstream & is ) { lock( is.lock$ ); &acq.is = &is; }
     336void ?{}( isacquire & acq, ifstream & is ) { &acq.is = &is; lock( is.lock$ ); }
    333337void ^?{}( isacquire & acq ) { release( acq.is ); }
    334338
     
    343347
    344348// exception I/O constructors
    345 void ?{}( Open_Failure & ex, ofstream & ostream ) with(ex) {
    346         virtual_table = &Open_Failure_vt;
    347         ostream = &ostream;
    348         tag = 1;
    349 } // ?{}
    350 
    351 void ?{}( Open_Failure & ex, ifstream & istream ) with(ex) {
    352         virtual_table = &Open_Failure_vt;
    353         istream = &istream;
    354         tag = 0;
     349void ?{}( Open_Failure & this, ofstream & ostream ) {
     350        this.virtual_table = &Open_Failure_vt;
     351        this.ostream = &ostream;
     352        this.tag = 1;
     353} // ?{}
     354
     355void ?{}( Open_Failure & this, ifstream & istream ) {
     356        this.virtual_table = &Open_Failure_vt;
     357        this.istream = &istream;
     358        this.tag = 0;
    355359} // ?{}
    356360
     
    359363
    360364// exception I/O constructors
    361 void ?{}( Close_Failure & ex, ofstream & ostream ) with(ex) {
    362         virtual_table = &Close_Failure_vt;
    363         ostream = &ostream;
    364         tag = 1;
    365 } // ?{}
    366 
    367 void ?{}( Close_Failure & ex, ifstream & istream ) with(ex) {
    368         virtual_table = &Close_Failure_vt;
    369         istream = &istream;
    370         tag = 0;
     365void ?{}( Close_Failure & this, ofstream & ostream ) {
     366        this.virtual_table = &Close_Failure_vt;
     367        this.ostream = &ostream;
     368        this.tag = 1;
     369} // ?{}
     370
     371void ?{}( Close_Failure & this, ifstream & istream ) {
     372        this.virtual_table = &Close_Failure_vt;
     373        this.istream = &istream;
     374        this.tag = 0;
    371375} // ?{}
    372376
     
    375379
    376380// exception I/O constructors
    377 void ?{}( Write_Failure & ex, ofstream & ostream ) with(ex) {
    378         virtual_table = &Write_Failure_vt;
    379         ostream = &ostream;
    380         tag = 1;
    381 } // ?{}
    382 
    383 void ?{}( Write_Failure & ex, ifstream & istream ) with(ex) {
    384         virtual_table = &Write_Failure_vt;
    385         istream = &istream;
    386         tag = 0;
     381void ?{}( Write_Failure & this, ofstream & ostream ) {
     382        this.virtual_table = &Write_Failure_vt;
     383        this.ostream = &ostream;
     384        this.tag = 1;
     385} // ?{}
     386
     387void ?{}( Write_Failure & this, ifstream & istream ) {
     388        this.virtual_table = &Write_Failure_vt;
     389        this.istream = &istream;
     390        this.tag = 0;
    387391} // ?{}
    388392
     
    391395
    392396// exception I/O constructors
    393 void ?{}( Read_Failure & ex, ofstream & ostream ) with(ex) {
    394         virtual_table = &Read_Failure_vt;
    395         ostream = &ostream;
    396         tag = 1;
    397 } // ?{}
    398 
    399 void ?{}( Read_Failure & ex, ifstream & istream ) with(ex) {
    400         virtual_table = &Read_Failure_vt;
    401         istream = &istream;
    402         tag = 0;
     397void ?{}( Read_Failure & this, ofstream & ostream ) {
     398        this.virtual_table = &Read_Failure_vt;
     399        this.ostream = &ostream;
     400        this.tag = 1;
     401} // ?{}
     402
     403void ?{}( Read_Failure & this, ifstream & istream ) {
     404        this.virtual_table = &Read_Failure_vt;
     405        this.istream = &istream;
     406        this.tag = 0;
    403407} // ?{}
    404408
  • libcfa/src/fstream.hfa

    r949339b r4e28d2e9  
    8080void release( ofstream & );
    8181
    82 void lock( ofstream & );
    83 void unlock( ofstream & );
    84 
    8582struct osacquire {
    8683        ofstream & os;
  • libcfa/src/memory.cfa

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

    r949339b r4e28d2e9  
    9494
    9595forall(T &)
    96 T * release(unique_ptr(T) & this);
    97 
    98 forall(T &)
    9996int ?==?(unique_ptr(T) const & this, unique_ptr(T) const & that);
    10097forall(T &)
  • src/Concurrency/Keywords.cc

    r949339b r4e28d2e9  
    12001200                                new PointerType(
    12011201                                        noQualifiers,
    1202                                         //new TypeofType( noQualifiers, args.front()->clone() )
    1203                                         new TypeofType( noQualifiers, new UntypedExpr(
    1204                                                         new NameExpr( "__get_type" ),
    1205                                                         { args.front()->clone() }
    1206                                                 )
    1207                                         )
     1202                                        new TypeofType( noQualifiers, args.front()->clone() )
    12081203                                ),
    12091204                                new ConstantExpr( Constant::from_ulong( args.size() ) ),
     
    12141209                                map_range < std::list<Initializer*> > ( args, [](Expression * var ){
    12151210                                        return new SingleInit( new UntypedExpr(
    1216                                                         new NameExpr( "__get_ptr" ),
    1217                                                         { var }
     1211                                                new NameExpr( "__get_pointer" ),
     1212                                                { var }
    12181213                                        ) );
    1219                                         //return new SingleInit( new AddressExpr( var ) );
    12201214                                })
    12211215                        )
     
    12231217
    12241218                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                 );
     1219                TypeExpr * lock_type_expr = new TypeExpr( new TypeofType( noQualifiers, args.front()->clone() ) );
    12321220
    12331221                lock_guard_struct->parameters.push_back( lock_type_expr ) ;
  • src/Parser/parser.yy

    r949339b r4e28d2e9  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Sep 11 08:20:44 2021
    13 // Update Count     : 5040
     12// Last Modified On : Sun Aug  8 09:14:44 2021
     13// Update Count     : 5038
    1414//
    1515
     
    24462446        | simple_assignment_operator initializer        { $$ = $1 == OperKinds::Assign ? $2 : $2->set_maybeConstructed( false ); }
    24472447        | '=' VOID                                                                      { $$ = new InitializerNode( true ); }
    2448         | '{' initializer_list_opt comma_opt '}'        { $$ = new InitializerNode( $2, true ); }
    24492448        ;
    24502449
     
    24602459        | designation initializer                                       { $$ = $2->set_designators( $1 ); }
    24612460        | initializer_list_opt ',' initializer          { $$ = (InitializerNode *)( $1->set_last( $3 ) ); }
    2462         | initializer_list_opt ',' designation initializer { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
     2461        | initializer_list_opt ',' designation initializer
     2462                { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
    24632463        ;
    24642464
  • tests/Makefile.am

    r949339b r4e28d2e9  
    7575        pybin/tools.py \
    7676        long_tests.hfa \
    77         .in/parseconfig-all.txt \
    78         .in/parseconfig-errors.txt \
    79         .in/parseconfig-missing.txt \
    8077        io/.in/io.data \
    8178        io/.in/many_read.data \
     
    8582        concurrent/clib_tls.c \
    8683        exceptions/with-threads.hfa \
    87         exceptions/except-io.hfa \
    88         unified_locking/mutex_test.hfa
     84        exceptions/except-io.hfa
    8985
    9086dist-hook:
  • tests/concurrent/mutexstmt/locks.cfa

    r949339b r4e28d2e9  
    44const unsigned int num_times = 10000;
    55
    6 single_acquisition_lock m1, m2, m3, m4, m5;
     6owner_lock m1, m2, m3, m4, m5;
    77
    88thread T_Mutex {};
     
    6565        printf("Start Test: single lock mutual exclusion\n");
    6666        {
    67                 T_Mutex t[10];
     67                T_Mutex t[1];
    6868        }
    6969        printf("End Test: single lock mutual exclusion\n");
  • tools/perf/process_stat_array.py

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