Changes in / [0fe4e62:f5c3b6c]
- Files:
-
- 1 added
- 8 deleted
- 71 edited
-
Jenkinsfile (modified) (5 diffs)
-
doc/proposals/concurrency/.gitignore (modified) (1 diff)
-
doc/proposals/concurrency/Makefile (modified) (5 diffs)
-
doc/proposals/concurrency/annex/local.bib (modified) (1 diff)
-
doc/proposals/concurrency/figures/int_monitor.fig (modified) (3 diffs)
-
doc/proposals/concurrency/figures/int_monitor.fig.bak (deleted)
-
doc/proposals/concurrency/figures/monitor_structs.fig (deleted)
-
doc/proposals/concurrency/figures/system.fig (deleted)
-
doc/proposals/concurrency/style/cfa-format.tex (modified) (1 diff)
-
doc/proposals/concurrency/text/basics.tex (modified) (15 diffs)
-
doc/proposals/concurrency/text/cforall.tex (modified) (8 diffs)
-
doc/proposals/concurrency/text/concurrency.tex (modified) (40 diffs)
-
doc/proposals/concurrency/text/future.tex (modified) (1 diff)
-
doc/proposals/concurrency/text/internals.tex (modified) (5 diffs)
-
doc/proposals/concurrency/text/intro.tex (modified) (1 diff)
-
doc/proposals/concurrency/text/parallelism.tex (modified) (2 diffs)
-
doc/proposals/concurrency/text/results.tex (deleted)
-
doc/proposals/concurrency/text/together.tex (modified) (3 diffs)
-
doc/proposals/concurrency/thesis.tex (modified) (2 diffs)
-
doc/proposals/concurrency/version (modified) (1 diff)
-
src/Common/Debug.h (modified) (1 diff)
-
src/Concurrency/Keywords.cc (modified) (1 diff)
-
src/InitTweak/GenInit.cc (modified) (3 diffs)
-
src/InitTweak/InitTweak.cc (modified) (3 diffs)
-
src/ResolvExpr/AlternativeFinder.cc (modified) (8 diffs)
-
src/ResolvExpr/AlternativeFinder.h (modified) (2 diffs)
-
src/ResolvExpr/CurrentObject.cc (modified) (1 diff)
-
src/ResolvExpr/RenameVars.cc (modified) (3 diffs)
-
src/ResolvExpr/RenameVars.h (modified) (1 diff)
-
src/ResolvExpr/TypeEnvironment.cc (modified) (1 diff)
-
src/ResolvExpr/TypeEnvironment.h (modified) (1 diff)
-
src/SymTab/Mangler.cc (modified) (3 diffs)
-
src/SymTab/Mangler.h (modified) (3 diffs)
-
src/SymTab/Validate.cc (modified) (1 diff)
-
src/Tuples/TupleAssignment.cc (modified) (11 diffs)
-
src/Tuples/Tuples.h (modified) (2 diffs)
-
src/benchmark/Makefile.am (modified) (6 diffs)
-
src/benchmark/Makefile.in (modified) (4 diffs)
-
src/benchmark/SchedInt.c (added)
-
src/benchmark/compile/empty.c (deleted)
-
src/benchmark/creation/cfa_cor.c (modified) (1 diff)
-
src/benchmark/csv-data.c (modified) (9 diffs)
-
src/benchmark/schedint/cfa1.c (modified) (2 diffs)
-
src/benchmark/schedint/cfa2.c (modified) (2 diffs)
-
src/benchmark/schedint/cfa4.c (modified) (2 diffs)
-
src/libcfa/Makefile.am (modified) (1 diff)
-
src/libcfa/Makefile.in (modified) (2 diffs)
-
src/libcfa/bits/containers.h (deleted)
-
src/libcfa/bits/defs.h (deleted)
-
src/libcfa/bits/locks.h (deleted)
-
src/libcfa/concurrency/alarm.c (modified) (3 diffs)
-
src/libcfa/concurrency/coroutine.c (modified) (1 diff)
-
src/libcfa/concurrency/invoke.h (modified) (3 diffs)
-
src/libcfa/concurrency/kernel (modified) (5 diffs)
-
src/libcfa/concurrency/kernel.c (modified) (20 diffs)
-
src/libcfa/concurrency/kernel_private.h (modified) (2 diffs)
-
src/libcfa/concurrency/monitor (modified) (5 diffs)
-
src/libcfa/concurrency/monitor.c (modified) (46 diffs)
-
src/libcfa/concurrency/preemption.c (modified) (1 diff)
-
src/main.cc (modified) (1 diff)
-
src/prelude/builtins.c (modified) (1 diff)
-
src/tests/.expect/32/KRfunctions.txt (modified) (1 diff)
-
src/tests/.expect/32/attributes.txt (modified) (1 diff)
-
src/tests/.expect/32/declarationSpecifier.txt (modified) (2 diffs)
-
src/tests/.expect/32/extension.txt (modified) (1 diff)
-
src/tests/.expect/32/gccExtensions.txt (modified) (2 diffs)
-
src/tests/.expect/32/literals.txt (modified) (2 diffs)
-
src/tests/.expect/64/KRfunctions.txt (modified) (1 diff)
-
src/tests/.expect/64/attributes.txt (modified) (1 diff)
-
src/tests/.expect/64/declarationSpecifier.txt (modified) (2 diffs)
-
src/tests/.expect/64/extension.txt (modified) (1 diff)
-
src/tests/.expect/64/gccExtensions.txt (modified) (2 diffs)
-
src/tests/.expect/64/literals.txt (modified) (2 diffs)
-
src/tests/boundedBuffer.c (modified) (3 diffs)
-
src/tests/datingService.c (modified) (5 diffs)
-
src/tests/sched-int-barge.c (modified) (2 diffs)
-
src/tests/sched-int-block.c (modified) (2 diffs)
-
src/tests/sched-int-disjoint.c (modified) (3 diffs)
-
src/tests/sched-int-wait.c (modified) (6 diffs)
-
src/tests/thread.c (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
Jenkinsfile
r0fe4e62 rf5c3b6c 15 15 arch_name = '' 16 16 architecture = '' 17 17 18 18 do_alltests = false 19 19 do_benchmark = false … … 183 183 sh 'make clean > /dev/null' 184 184 sh 'make > /dev/null 2>&1' 185 } 185 } 186 186 catch (Exception caughtError) { 187 187 err = caughtError //rethrow error later … … 257 257 def build() { 258 258 build_stage('Build') { 259 259 260 260 def install_dir = pwd tmp: true 261 261 262 262 //Configure the conpilation (Output is not relevant) 263 263 //Use the current directory as the installation target so nothing … … 290 290 if( !do_benchmark ) return 291 291 292 //Write the commit id to Benchmark 293 writeFile file: 'bench.csv', text:'data=' + gitRefNewValue + ',' + arch_name + ',' 294 292 295 //Append bench results 293 sh 'make -C src/benchmark --no-print-directory jenkins githash=' + gitRefNewValue + ' arch=' + arch_name + ' | tee bench.json'296 sh 'make -C src/benchmark --no-print-directory csv-data >> bench.csv' 294 297 } 295 298 } … … 324 327 325 328 //Then publish the results 326 sh 'curl - H "Content-Type: application/json" --silent --data @bench.jsonhttp://plg2:8082/jenkins/publish > /dev/null || true'329 sh 'curl --silent --data @bench.csv http://plg2:8082/jenkins/publish > /dev/null || true' 327 330 } 328 331 } -
doc/proposals/concurrency/.gitignore
r0fe4e62 rf5c3b6c 16 16 build/*.out 17 17 build/*.ps 18 build/*.pstex19 build/*.pstex_t20 18 build/*.tex 21 19 build/*.toc -
doc/proposals/concurrency/Makefile
r0fe4e62 rf5c3b6c 13 13 annex/glossary \ 14 14 text/intro \ 15 text/cforall \ 15 16 text/basics \ 16 text/cforall \17 17 text/concurrency \ 18 18 text/internals \ 19 19 text/parallelism \ 20 text/results \21 20 text/together \ 22 21 text/future \ … … 30 29 }} 31 30 32 PICTURES = ${addprefix build/, ${addsuffix .pstex, \ 33 system \ 34 monitor_structs \ 35 }} 31 PICTURES = ${addsuffix .pstex, \ 32 } 36 33 37 34 PROGRAMS = ${addsuffix .tex, \ … … 70 67 build/*.out \ 71 68 build/*.ps \ 72 build/*.pstex \73 69 build/*.pstex_t \ 74 70 build/*.tex \ … … 84 80 dvips $< -o $@ 85 81 86 build/${basename ${DOCUMENT}}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} ${basename ${DOCUMENT}}.tex ../../LaTeXmacros/common.tex ../../LaTeXmacros/indexstyle annex/local.bib82 build/${basename ${DOCUMENT}}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} ${basename ${DOCUMENT}}.tex ../../LaTeXmacros/common.tex ../../LaTeXmacros/indexstyle 87 83 88 84 @ if [ ! -r ${basename $@}.ind ] ; then touch ${basename $@}.ind ; fi # Conditionally create an empty *.ind (index) file for inclusion until makeindex is run. … … 95 91 @ -${BibTeX} ${basename $@} 96 92 @ echo "Glossary" 97 @makeglossaries -q -s ${basename $@}.ist ${basename $@} # Make index from *.aux entries and input index at end of document93 makeglossaries -q -s ${basename $@}.ist ${basename $@} # Make index from *.aux entries and input index at end of document 98 94 @ echo ".dvi generation" 99 95 @ -build/bump_ver.sh -
doc/proposals/concurrency/annex/local.bib
r0fe4e62 rf5c3b6c 52 52 year = 2017 53 53 } 54 55 @manual{Cpp-Transactions,56 keywords = {C++, Transactional Memory},57 title = {Technical Specification for C++ Extensions for Transactional Memory},58 organization= {International Standard ISO/IEC TS 19841:2015 },59 publisher = {American National Standards Institute},60 address = {http://www.iso.org},61 year = 2015,62 }63 64 @article{BankTransfer,65 keywords = {Bank Transfer},66 title = {Bank Account Transfer Problem},67 publisher = {Wiki Wiki Web},68 address = {http://wiki.c2.com},69 year = 201070 }71 72 @misc{2FTwoHardThings,73 keywords = {Hard Problem},74 title = {TwoHardThings},75 author = {Martin Fowler},76 address = {https://martinfowler.com/bliki/TwoHardThings.html},77 year = 200978 }79 80 @article{IntrusiveData,81 title = {Intrusive Data Structures},82 author = {Jiri Soukup},83 journal = {CppReport},84 year = 1998,85 month = May,86 volume = {10/No5.},87 page = 2288 }89 90 @misc{affinityLinux,91 title = "{Linux man page - sched\_setaffinity(2)}"92 }93 94 @misc{affinityWindows,95 title = "{Windows (vs.85) - SetThreadAffinityMask function}"96 }97 98 @misc{affinityFreebsd,99 title = "{FreeBSD General Commands Manual - CPUSET(1)}"100 }101 102 @misc{affinityNetbsd,103 title = "{NetBSD Library Functions Manual - AFFINITY(3)}"104 }105 106 @misc{affinityMacosx,107 title = "{Affinity API Release Notes for OS X v10.5}"108 } -
doc/proposals/concurrency/figures/int_monitor.fig
r0fe4e62 rf5c3b6c 8 8 -2 9 9 1200 2 10 5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 675.000 2700.000 675 2400 375 2700 675 3000 11 6 4533 2866 4655 3129 12 5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 4657.017 2997.000 4655 2873 4533 2997 4655 3121 13 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 14 4655 2866 4655 3129 10 5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 600.000 2625.000 600 2325 300 2625 600 2925 11 6 3225 4500 7425 4800 12 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3375 4650 80 80 3375 4650 3455 4730 13 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4725 4650 105 105 4725 4650 4830 4755 14 1 3 0 1 -1 -1 0 0 4 0.000 1 0.0000 6225 4650 105 105 6225 4650 6330 4755 15 4 0 -1 0 0 0 12 0.0000 2 135 1035 4950 4725 blocked task\001 16 4 0 -1 0 0 0 12 0.0000 2 135 870 3525 4725 active task\001 17 4 0 -1 0 0 0 12 0.0000 2 180 930 6450 4725 routine ptrs\001 15 18 -6 16 6 4725 2866 4847 3129 17 5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 4849.017 2997.000 4847 2873 4725 2997 4847 3121 18 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 19 4847 2866 4847 3129 19 6 8445 1695 8655 1905 20 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 8550 1800 105 105 8550 1800 8655 1905 21 4 1 -1 0 0 0 10 0.0000 2 75 75 8550 1860 a\001 20 22 -6 21 6 4911 2866 5033 3129 22 5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 5035.017 2997.000 5033 2873 4911 2997 5033 3121 23 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 24 5033 2866 5033 3129 23 6 8445 1395 8655 1605 24 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 8550 1500 105 105 8550 1500 8655 1605 25 4 1 -1 0 0 0 10 0.0000 2 105 90 8550 1560 b\001 25 26 -6 26 6 9027 2866 9149 3129 27 5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 9024.983 2997.000 9027 2873 9149 2997 9027 3121 28 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 29 9027 2866 9027 3129 27 6 3945 1695 4155 1905 28 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 1800 105 105 4050 1800 4155 1905 29 4 1 -1 0 0 0 10 0.0000 2 75 75 4050 1860 a\001 30 30 -6 31 6 9253 2866 9375 3129 32 5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 9250.983 2997.000 9253 2873 9375 2997 9253 3121 33 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 34 9253 2866 9253 3129 35 -6 36 6 9478 2866 9600 3129 37 5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 9475.983 2997.000 9478 2873 9600 2997 9478 3121 38 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 39 9478 2866 9478 3129 31 6 3945 1395 4155 1605 32 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4050 1500 105 105 4050 1500 4155 1605 33 4 1 -1 0 0 0 10 0.0000 2 105 90 4050 1560 b\001 40 34 -6 41 35 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 7650 3675 80 80 7650 3675 7730 3755 42 36 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3150 3675 80 80 3150 3675 3230 3755 43 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4047 1793 125 125 4047 1793 3929 175244 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 4050 1500 125 125 4050 1500 3932 145945 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 8550 1500 125 125 8550 1500 8432 145946 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 8550 1800 125 125 8550 1800 8432 175947 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 1200 2850 125 125 1200 2850 1082 280948 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 900 2850 125 125 900 2850 782 280949 1 3 0 1 -1 -1 0 0 4 0.000 1 0.0000 6225 4650 105 105 6225 4650 6330 475550 1 3 0 1 -1 -1 0 0 20 0.000 1 0.0000 3150 4650 80 80 3150 4650 3230 473051 1 3 0 1 -1 -1 0 0 -1 0.000 1 0.0000 4575 4650 105 105 4575 4650 4680 475552 37 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 53 38 3900 1950 4200 2100 … … 77 62 3000 4050 3300 4200 78 63 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 79 6 75 3000 1425 300064 600 2925 1350 2925 80 65 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 81 6 75 2400 1425 240066 600 2325 1350 2325 82 67 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 83 1 425 2700 1500 292568 1350 2625 1425 2850 84 69 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 85 1 425 2400 1350 262570 1350 2325 1275 2550 86 71 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 87 675 2700 1425 2700 72 600 2625 1350 2625 73 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 74 1350 2775 1275 2645 1125 2645 1050 2775 1125 2905 1275 2905 75 1350 2775 76 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 77 975 2775 900 2645 750 2645 675 2775 750 2905 900 2905 78 975 2775 79 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 80 4800 3000 4725 2870 4575 2870 4500 3000 4575 3130 4725 3130 81 4800 3000 82 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 83 5100 3000 5025 2870 4875 2870 4800 3000 4875 3130 5025 3130 84 5100 3000 85 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 86 9300 3000 9225 2870 9075 2870 9000 3000 9075 3130 9225 3130 87 9300 3000 88 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 89 9600 3000 9525 2870 9375 2870 9300 3000 9375 3130 9525 3130 90 9600 3000 91 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 92 675 2775 975 2775 93 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 94 1050 2775 1350 2775 95 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 96 4875 4950 4800 4820 4650 4820 4575 4950 4650 5080 4800 5080 97 4875 4950 98 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 99 4575 4950 4875 4950 100 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7 101 3525 4970 3450 4840 3300 4840 3225 4970 3300 5100 3450 5100 102 3525 4970 88 103 4 1 -1 0 0 0 12 0.0000 2 135 315 2850 4275 exit\001 89 104 4 1 -1 0 0 0 12 0.0000 2 135 315 7350 4275 exit\001 … … 106 121 4 1 -1 0 0 0 12 0.0000 2 135 495 4050 1275 queue\001 107 122 4 1 -1 0 0 0 12 0.0000 2 165 420 4050 1050 entry\001 108 4 0 0 50 -1 0 11 0.0000 2 120 705 600 2325 Condition\001 109 4 0 -1 0 0 0 12 0.0000 2 180 930 6450 4725 routine ptrs\001 110 4 0 -1 0 0 0 12 0.0000 2 135 1050 3300 4725 active thread\001 111 4 0 -1 0 0 0 12 0.0000 2 135 1215 4725 4725 blocked thread\001 123 4 0 0 50 -1 0 11 0.0000 2 120 705 450 2250 Condition\001 124 4 0 0 50 -1 0 11 0.0000 2 165 630 3600 5025 signalled\001 125 4 0 0 50 -1 0 11 0.0000 2 165 525 4950 5025 waiting\001 -
doc/proposals/concurrency/style/cfa-format.tex
r0fe4e62 rf5c3b6c 254 254 }{} 255 255 256 \lstnewenvironment{gocode}[1][]{257 \lstset{258 language = Golang,259 style=defaultStyle,260 #1261 }262 }{}263 264 256 \newcommand{\zero}{\lstinline{zero_t}\xspace} 265 257 \newcommand{\one}{\lstinline{one_t}\xspace} -
doc/proposals/concurrency/text/basics.tex
r0fe4e62 rf5c3b6c 9 9 At its core, concurrency is based on having multiple call-stacks and scheduling among threads of execution executing on these stacks. Concurrency without parallelism only requires having multiple call stacks (or contexts) for a single thread of execution. 10 10 11 Execution with a single thread and multiple stacks where the thread is self-scheduling deterministically across the stacks is called coroutining. Execution with a single and multiple stacks but where the thread is scheduled by an oracle (non-deterministic from the thread perspective) across the stacks is called concurrency. 12 13 Therefore, a minimal concurrency system can be achieved by creating coroutines, which instead of context switching among each other, always ask an oracle where to context switch next. While coroutines can execute on the caller's stack-frame, stackfull coroutines allow full generality and are sufficient as the basis for concurrency. The aforementioned oracle is a scheduler and the whole system now follows a cooperative threading-model (a.k.a non-preemptive scheduling). The oracle/scheduler can either be a stackless or stackfull entity and correspondingly require one or two context switches to run a different coroutine. In any case, a subset of concurrency related challenges start to appear. For the complete set of concurrency challenges to occur, the only feature missing is preemption. 14 15 A scheduler introduces order of execution uncertainty, while preemption introduces uncertainty about where context-switches occur. Mutual-exclusion and synchronisation are ways of limiting non-determinism in a concurrent system. Now it is important to understand that uncertainty is desireable; uncertainty can be used by runtime systems to significantly increase performance and is often the basis of giving a user the illusion that tasks are running in parallel. Optimal performance in concurrent applications is often obtained by having as much non-determinism as correctness allows. 11 Indeed, while execution with a single thread and multiple stacks where the thread is self-scheduling deterministically across the stacks is called coroutining, execution with a single and multiple stacks but where the thread is scheduled by an oracle (non-deterministic from the thread perspective) across the stacks is called concurrency. 12 13 Therefore, a minimal concurrency system can be achieved by creating coroutines, which instead of context switching among each other, always ask an oracle where to context switch next. While coroutines can execute on the caller's stack-frame, stackfull coroutines allow full generality and are sufficient as the basis for concurrency. The aforementioned oracle is a scheduler and the whole system now follows a cooperative threading-model \cit. The oracle/scheduler can either be a stackless or stackfull entity and correspondingly require one or two context switches to run a different coroutine. In any case, a subset of concurrency related challenges start to appear. For the complete set of concurrency challenges to occur, the only feature missing is preemption. Indeed, concurrency challenges appear with non-determinism. Using mutual-exclusion or synchronisation are ways of limiting the lack of determinism in a system. A scheduler introduces order of execution uncertainty, while preemption introduces uncertainty about where context-switches occur. Now it is important to understand that uncertainty is not undesireable; uncertainty can often be used by systems to significantly increase performance and is often the basis of giving a user the illusion that tasks are running in parallel. Optimal performance in concurrent applications is often obtained by having as much non-determinism as correctness allows\cit. 16 14 17 15 \section{\protect\CFA 's Thread Building Blocks} 18 One of the important features that is missing in C is threading. On modern architectures, a lack of threading is unacceptable\cite{Sutter05, Sutter05b}, and therefore modern programming languages must have the proper tools to allow users to write performant concurrent programs to take advantage of parallelism. As an extension of C, \CFA needs to express these concepts in a way that is as natural as possible to programmers familiar with imperative languages. And being a system-level language means programmers expect to choose precisely which features they need and which cost they are willing to pay.16 One of the important features that is missing in C is threading. On modern architectures, a lack of threading is unacceptable\cite{Sutter05, Sutter05b}, and therefore modern programming languages must have the proper tools to allow users to write performant concurrent and/or parallel programs. As an extension of C, \CFA needs to express these concepts in a way that is as natural as possible to programmers familiar with imperative languages. And being a system-level language means programmers expect to choose precisely which features they need and which cost they are willing to pay. 19 17 20 18 \section{Coroutines: A stepping stone}\label{coroutine} 21 While the main focus of this proposal is concurrency and parallelism, it is important to address coroutines, which are actually a significant building block of a concurrency system. Coroutines need to deal with context-switches and other context-management operations. Therefore, this proposal includes coroutines both as an intermediate step for the implementation of threads, and a first class feature of \CFA. Furthermore, many design challenges of threads are at least partially present in designing coroutines, which makes the design effort that much more relevant. The core \acrshort{api} of coroutines revolve around two features: independent call stacks and \code{suspend}/\code{resume}. 22 19 While the main focus of this proposal is concurrency and parallelism, it is important to address coroutines, which are actually a significant building block of a concurrency system. Coroutines need to deal with context-switchs and other context-management operations. Therefore, this proposal includes coroutines both as an intermediate step for the implementation of threads, and a first class feature of \CFA. Furthermore, many design challenges of threads are at least partially present in designing coroutines, which makes the design effort that much more relevant. The core \acrshort{api} of coroutines revolve around two features: independent call stacks and \code{suspend}/\code{resume}. 20 21 A good example of a problem made easier with coroutines is genereting the fibonacci sequence. This problem comes with the challenge of decoupling how a sequence is generated and how it is used. Figure \ref{fig:fibonacci-c} shows conventional approaches to writing generators in C. All three of these approach suffer from strong coupling. The left and center approaches require that the generator have knowledge of how the sequence will be used, while the rightmost approach requires to user to hold internal state between calls on behalf of th sequence generator and makes it much harder to handle corner cases like the Fibonacci seed. 23 22 \begin{figure} 23 \label{fig:fibonacci-c} 24 24 \begin{center} 25 25 \begin{tabular}{c @{\hskip 0.025in}|@{\hskip 0.025in} c @{\hskip 0.025in}|@{\hskip 0.025in} c} … … 45 45 } 46 46 } 47 48 int main() {49 void print_fib(int n) {50 printf("%d\n", n);51 }52 53 fibonacci_func(54 10, print_fib55 );56 57 58 59 }60 47 \end{ccode}&\begin{ccode}[tabsize=2] 61 48 //Using output array … … 75 62 f2 = next; 76 63 } 77 array[i] = next; 78 } 79 } 80 81 82 int main() { 83 int a[10]; 84 85 fibonacci_func( 86 10, a 87 ); 88 89 for(int i=0;i<10;i++){ 90 printf("%d\n", a[i]); 91 } 92 64 *array = next; 65 array++; 66 } 93 67 } 94 68 \end{ccode}&\begin{ccode}[tabsize=2] … … 96 70 typedef struct { 97 71 int f1, f2; 98 } Iterator_t;72 } iterator_t; 99 73 100 74 int fibonacci_state( 101 Iterator_t * it75 iterator_t * it 102 76 ) { 103 77 int f; 104 78 f = it->f1 + it->f2; 105 79 it->f2 = it->f1; 106 it->f1 = max(f,1);80 it->f1 = f; 107 81 return f; 108 82 } … … 113 87 114 88 115 116 int main() {117 Iterator_t it={0,0};118 119 for(int i=0;i<10;i++){120 printf("%d\n",121 fibonacci_state(122 &it123 );124 );125 }126 127 }128 89 \end{ccode} 129 90 \end{tabular} 130 91 \end{center} 131 92 \caption{Different implementations of a fibonacci sequence generator in C.} 132 \label{lst:fibonacci-c}133 93 \end{figure} 134 94 135 A good example of a problem made easier with coroutines is generators, like the fibonacci sequence. This problem comes with the challenge of decoupling how a sequence is generated and how it is used. Figure \ref{lst:fibonacci-c} shows conventional approaches to writing generators in C. All three of these approach suffer from strong coupling. The left and center approaches require that the generator have knowledge of how the sequence is used, while the rightmost approach requires holding internal state between calls on behalf of the generator and makes it much harder to handle corner cases like the Fibonacci seed. 136 137 Figure \ref{lst:fibonacci-cfa} is an example of a solution to the fibonnaci problem using \CFA coroutines, where the coroutine stack holds sufficient state for the generation. This solution has the advantage of having very strong decoupling between how the sequence is generated and how it is used. Indeed, this version is as easy to use as the \code{fibonacci_state} solution, while the imlpementation is very similar to the \code{fibonacci_func} example. 95 96 Figure \ref{fig:fibonacci-cfa} is an example of a solution to the fibonnaci problem using \CFA coroutines, using the coroutine stack to hold sufficient state for the generation. This solution has the advantage of having very strong decoupling between how the sequence is generated and how it is used. Indeed, this version is a easy to use as the \code{fibonacci_state} solution, while the imlpementation is very similar to the \code{fibonacci_func} example. 138 97 139 98 \begin{figure} 99 \label{fig:fibonacci-cfa} 140 100 \begin{cfacode} 141 101 coroutine Fibonacci { … … 148 108 149 109 //main automacically called on first resume 150 void main(Fibonacci & this) with (this){110 void main(Fibonacci & this) { 151 111 int fn1, fn2; //retained between resumes 152 fn= 0;153 fn1 = fn;112 this.fn = 0; 113 fn1 = this.fn; 154 114 suspend(this); //return to last resume 155 115 156 fn= 1;116 this.fn = 1; 157 117 fn2 = fn1; 158 fn1 = fn;118 fn1 = this.fn; 159 119 suspend(this); //return to last resume 160 120 161 121 for ( ;; ) { 162 fn= fn1 + fn2;122 this.fn = fn1 + fn2; 163 123 fn2 = fn1; 164 fn1 = fn;124 fn1 = this.fn; 165 125 suspend(this); //return to last resume 166 126 } … … 180 140 \end{cfacode} 181 141 \caption{Implementation of fibonacci using coroutines} 182 \label{lst:fibonacci-cfa}183 142 \end{figure} 184 143 185 Figure \ref{lst:fmt-line} shows the \code{Format} coroutine which rearranges text in order to group characters into blocks of fixed size. The example takes advantage of resuming coroutines in the constructor to simplify the code and highlights the idea that interesting control flow can occur in the constructor. 186 144 \subsection{Construction} 145 One important design challenge for coroutines and threads (shown in section \ref{threads}) is that the runtime system needs to run code after the user-constructor runs to connect the object into the system. In the case of coroutines, this challenge is simpler since there is no non-determinism from preemption or scheduling. However, the underlying challenge remains the same for coroutines and threads. 146 147 The runtime system needs to create the coroutine's stack and more importantly prepare it for the first resumption. The timing of the creation is non-trivial since users both expect to have fully constructed objects once execution enters the coroutine main and to be able to resume the coroutine from the constructor. As regular objects, constructors can leak coroutines before they are ready. There are several solutions to this problem but the chosen options effectively forces the design of the coroutine. 148 149 Furthermore, \CFA faces an extra challenge as polymorphic routines create invisible thunks when casted to non-polymorphic routines and these thunks have function scope. For example, the following code, while looking benign, can run into undefined behaviour because of thunks: 150 151 \begin{cfacode} 152 //async: Runs function asynchronously on another thread 153 forall(otype T) 154 extern void async(void (*func)(T*), T* obj); 155 156 forall(otype T) 157 void noop(T *) {} 158 159 void bar() { 160 int a; 161 async(noop, &a); 162 } 163 \end{cfacode} 164 165 The generated C code\footnote{Code trimmed down for brevity} creates a local thunk to hold type information: 166 167 \begin{ccode} 168 extern void async(/* omitted */, void (*func)(void *), void *obj); 169 170 void noop(/* omitted */, void *obj){} 171 172 void bar(){ 173 int a; 174 void _thunk0(int *_p0){ 175 /* omitted */ 176 noop(/* omitted */, _p0); 177 } 178 /* omitted */ 179 async(/* omitted */, ((void (*)(void *))(&_thunk0)), (&a)); 180 } 181 \end{ccode} 182 The problem in this example is a storage management issue, the function pointer \code{_thunk0} is only valid until the end of the block. This extra challenge limits which solutions are viable because storing the function pointer for too long causes undefined behavior; i.e. the stack based thunk being destroyed before it was used. This challenge is an extension of challenges that come with second-class routines. Indeed, GCC nested routines also have the limitation that the routines cannot be passed outside of the scope of the functions these were declared in. The case of coroutines and threads is simply an extension of this problem to multiple call-stacks. 183 184 \subsection{Alternative: Composition} 185 One solution to this challenge is to use composition/containement, where uses add insert a coroutine field which contains the necessary information to manage the coroutine. 186 187 \begin{cfacode} 188 struct Fibonacci { 189 int fn; //used for communication 190 coroutine c; //composition 191 }; 192 193 void ?{}(Fibonacci & this) { 194 this.fn = 0; 195 (this.c){}; //Call constructor to initialize coroutine 196 } 197 \end{cfacode} 198 There are two downsides to this approach. The first, which is relatively minor, made aware of the main routine pointer. This information must either be store in the coroutine runtime data or in its static type structure. When using composition, all coroutine handles have the same static type structure which means the pointer to the main needs to be part of the runtime data. This requirement means the coroutine data must be made larger to store a value that is actually a compile time constant (address of the main routine). The second problem, which is both subtle and significant, is that now users can get the initialisation order of coroutines wrong. Indeed, every field of a \CFA struct is constructed but in declaration order, unless users explicitly write otherwise. This semantics means that users who forget to initialize the coroutine handle may resume the coroutine with an uninitilized object. For coroutines, this is unlikely to be a problem, for threads however, this is a significant problem. Figure \ref{fig:fmt-line} shows the \code{Format} coroutine which rearranges text in order to group characters into blocks of fixed size. This is a good example where the control flow is made much simpler from being able to resume the coroutine from the constructor and highlights the idea that interesting control flow can occor in the constructor. 187 199 \begin{figure} 200 \label{fig:fmt-line} 188 201 \begin{cfacode}[tabsize=3] 189 202 //format characters into blocks of 4 and groups of 5 blocks per line … … 231 244 \end{cfacode} 232 245 \caption{Formatting text into lines of 5 blocks of 4 characters.} 233 \label{lst:fmt-line}234 246 \end{figure} 235 247 236 \subsection{Construction}237 One important design challenge for coroutines and threads (shown in section \ref{threads}) is that the runtime system needs to run code after the user-constructor runs to connect the fully constructed object into the system. In the case of coroutines, this challenge is simpler since there is no non-determinism from preemption or scheduling. However, the underlying challenge remains the same for coroutines and threads.238 239 The runtime system needs to create the coroutine's stack and more importantly prepare it for the first resumption. The timing of the creation is non-trivial since users both expect to have fully constructed objects once execution enters the coroutine main and to be able to resume the coroutine from the constructor. As regular objects, constructors can leak coroutines before they are ready. There are several solutions to this problem but the chosen options effectively forces the design of the coroutine.240 241 Furthermore, \CFA faces an extra challenge as polymorphic routines create invisible thunks when casted to non-polymorphic routines and these thunks have function scope. For example, the following code, while looking benign, can run into undefined behaviour because of thunks:242 243 \begin{cfacode}244 //async: Runs function asynchronously on another thread245 forall(otype T)246 extern void async(void (*func)(T*), T* obj);247 248 forall(otype T)249 void noop(T*) {}250 251 void bar() {252 int a;253 async(noop, &a); //start thread running noop with argument a254 }255 \end{cfacode}256 257 The generated C code\footnote{Code trimmed down for brevity} creates a local thunk to hold type information:258 259 \begin{ccode}260 extern void async(/* omitted */, void (*func)(void *), void *obj);261 262 void noop(/* omitted */, void *obj){}263 264 void bar(){265 int a;266 void _thunk0(int *_p0){267 /* omitted */268 noop(/* omitted */, _p0);269 }270 /* omitted */271 async(/* omitted */, ((void (*)(void *))(&_thunk0)), (&a));272 }273 \end{ccode}274 The problem in this example is a storage management issue, the function pointer \code{_thunk0} is only valid until the end of the block, which limits the viable solutions because storing the function pointer for too long causes undefined behavior; i.e., the stack-based thunk being destroyed before it can be used. This challenge is an extension of challenges that come with second-class routines. Indeed, GCC nested routines also have the limitation that nested routine cannot be passed outside of the declaration scope. The case of coroutines and threads is simply an extension of this problem to multiple call-stacks.275 276 \subsection{Alternative: Composition}277 One solution to this challenge is to use composition/containement, where coroutine fields are added to manage the coroutine.278 279 \begin{cfacode}280 struct Fibonacci {281 int fn; //used for communication282 coroutine c; //composition283 };284 285 void FibMain(void *) {286 //...287 }288 289 void ?{}(Fibonacci & this) {290 this.fn = 0;291 //Call constructor to initialize coroutine292 (this.c){myMain};293 }294 \end{cfacode}295 The downside of this approach is that users need to correctly construct the coroutine handle before using it. Like any other objects, doing so the users carefully choose construction order to prevent usage of unconstructed objects. However, in the case of coroutines, users must also pass to the coroutine information about the coroutine main, like in the previous example. This opens the door for user errors and requires extra runtime storage to pass at runtime information that can be known statically.296 248 297 249 \subsection{Alternative: Reserved keyword} … … 303 255 }; 304 256 \end{cfacode} 305 Th e \code{coroutine} keyword means the compiler can find and inject code where needed. The downside of this approach is that it makes coroutine a special case in the language. Users wantint to extend coroutines or build their own for various reasons can only do so in ways offered by the language. Furthermore, implementing coroutines without language supports also displays the power of the programming language used. While this is ultimately the option used for idiomatic \CFA code, coroutines and threads can stillbe constructed by users without using the language support. The reserved keywords are only present to improve ease of use for the common cases.257 This mean the compiler can solve problems by injecting code where needed. The downside of this approach is that it makes coroutine a special case in the language. Users who would want to extend coroutines or build their own for various reasons can only do so in ways offered by the language. Furthermore, implementing coroutines without language supports also displays the power of the programming language used. While this is ultimately the option used for idiomatic \CFA code, coroutines and threads can both be constructed by users without using the language support. The reserved keywords are only present to improve ease of use for the common cases. 306 258 307 259 \subsection{Alternative: Lamda Objects} 308 260 309 For coroutines as for threads, many implementations are based on routine pointers or function objects\cit e{Butenhof97, ANSI14:C++, MS:VisualC++, BoostCoroutines15}. For example, Boost implements coroutines in terms of four functor object types:261 For coroutines as for threads, many implementations are based on routine pointers or function objects\cit. For example, Boost implements coroutines in terms of four functor object types: 310 262 \begin{cfacode} 311 263 asymmetric_coroutine<>::pull_type … … 316 268 Often, the canonical threading paradigm in languages is based on function pointers, pthread being one of the most well known examples. The main problem of this approach is that the thread usage is limited to a generic handle that must otherwise be wrapped in a custom type. Since the custom type is simple to write in \CFA and solves several issues, added support for routine/lambda based coroutines adds very little. 317 269 318 A variation of this would be to use a simple function pointer in the same way pthread does for threads :270 A variation of this would be to use an simple function pointer in the same way pthread does for threads : 319 271 \begin{cfacode} 320 272 void foo( coroutine_t cid, void * arg ) { … … 329 281 } 330 282 \end{cfacode} 331 This semantic s is more common for thread interfaces than coroutines worksequally well. As discussed in section \ref{threads}, this approach is superseeded by static approaches in terms of expressivity.283 This semantic is more common for thread interfaces than coroutines but would work equally well. As discussed in section \ref{threads}, this approach is superseeded by static approaches in terms of expressivity. 332 284 333 285 \subsection{Alternative: Trait-based coroutines} … … 398 350 \end{cfacode} 399 351 400 In this example, threads of type \code{foo} start execution in the \code{void main(foo &)} routine, which prints \code{"Hello World!"}. While this thesis encourages this approach to enforce strongly-typed programming, users may prefer to use the routine-based thread semantics for the sake of simplicity. With the static semantics it is trivial to write a thread type that takes a function pointer as a parameter and executes it on its stack asynchronously.352 In this example, threads of type \code{foo} start execution in the \code{void main(foo &)} routine, which prints \code{"Hello World!"}. While this thesis encourages this approach to enforce strongly-typed programming, users may prefer to use the routine-based thread semantics for the sake of simplicity. With these semantics it is trivial to write a thread type that takes a function pointer as a parameter and executes it on its stack asynchronously 401 353 \begin{cfacode} 402 354 typedef void (*voidFunc)(int); … … 409 361 void ?{}(FuncRunner & this, voidFunc inFunc, int arg) { 410 362 this.func = inFunc; 411 this.arg = arg;412 363 } 413 364 414 365 void main(FuncRunner & this) { 415 //thread starts here and runs the function416 366 this.func( this.arg ); 417 367 } 418 368 \end{cfacode} 419 369 420 A consequence of the strongly-typed approach to main is that memory layout of parameters and return values to/from a thread are now explicitly specified in the \acrshort{api}.370 An consequence of the strongly typed approach to main is that memory layout of parameters and return values to/from a thread are now explicitly specified in the \acrshort{api}. 421 371 422 372 Of course for threads to be useful, it must be possible to start and stop threads and wait for them to complete execution. While using an \acrshort{api} such as \code{fork} and \code{join} is relatively common in the literature, such an interface is unnecessary. Indeed, the simplest approach is to use \acrshort{raii} principles and have threads \code{fork} after the constructor has completed and \code{join} before the destructor runs. … … 439 389 \end{cfacode} 440 390 441 This semantic has several advantages over explicit semantics: a thread is always started and stopped exaclty once , users cannot make any progamming errors, and it naturally scales to multiple threads meaning basic synchronisation is very simple.391 This semantic has several advantages over explicit semantics: a thread is always started and stopped exaclty once and users cannot make any progamming errors and it naturally scales to multiple threads meaning basic synchronisation is very simple 442 392 443 393 \begin{cfacode} … … 461 411 \end{cfacode} 462 412 463 However, one of the drawbacks of this approach is that threads now always form a lattice, that is they are always destroyed in the opposite order of construction because of block structure. This restriction is relaxed by using dynamic allocation, so threads can outlive the scope in which they are created, much like dynamically allocating memory lets objects outlive the scope in which they are created.413 However, one of the drawbacks of this approach is that threads now always form a lattice, that is they are always destroyed in opposite order of construction because of block structure. This restriction is relaxed by using dynamic allocation, so threads can outlive the scope in which they are created, much like dynamically allocating memory lets objects outlive the scope in which they are created 464 414 465 415 \begin{cfacode} -
doc/proposals/concurrency/text/cforall.tex
r0fe4e62 rf5c3b6c 1 1 % ====================================================================== 2 2 % ====================================================================== 3 \chapter{Cforall Overview}3 \chapter{Cforall crash course} 4 4 % ====================================================================== 5 5 % ====================================================================== 6 6 7 Th e following is a quick introduction to the \CFAlanguage, specifically tailored to the features needed to support concurrency.7 This thesis presents the design for a set of concurrency features in \CFA. Since it is a new dialect of C, the following is a quick introduction to the language, specifically tailored to the features needed to support concurrency. 8 8 9 \CFA is a extension of ISO-C and therefore supports all of the same paradigms as C. It is a non-object oriented system language, meaning most of the major abstractions have either no runtime overhead or can be opt-out easily. Like C, the basics of \CFA revolve around structures and routines, which are thin abstractions over machine code. The vast majority of the code produced by the \CFA translator respects memory-layouts and calling-conventions laid out by C. Interestingly, while \CFA is not an object-oriented language, lacking the concept of a receiver (e.g., this), it does have some notion of objects\footnote{C defines the term objects as : ``region of data storage in the execution environment, the contents of which can represent 10 values''\cite[3.15]{C11}}, most importantly construction and destruction of objects. Most of the following code examples can be found on the \CFA website \cite{www-cfa} 9 \CFA is a extension of ISO-C and therefore supports all of the same paradigms as C. It is a non-object oriented system language, meaning most of the major abstractions have either no runtime overhead or can be opt-out easily. Like C, the basics of \CFA revolve around structures and routines, which are thin abstractions over machine code. The vast majority of the code produced by the \CFA translator respects memory-layouts and calling-conventions laid out by C. Interestingly, while \CFA is not an object-oriented language, lacking the concept of a received (e.g.: this), it does have some notion of objects\footnote{C defines the term objects as : [Where to I get the C11 reference manual?]}, most importantly construction and destruction of objects. Most of the following pieces of code can be found on the \CFA website \cite{www-cfa} 11 10 12 11 \section{References} 13 12 14 Like \CC, \CFA introduces re bindable references providing multiple dereferecing as an alternative to pointers. In regards to concurrency, the semantic difference between pointers and references are not particularly relevant, but since this document uses mostly references, here is a quick overview of the semantics:13 Like \CC, \CFA introduces references as an alternative to pointers. In regards to concurrency, the semantics difference between pointers and references are not particularly relevant but since this document uses mostly references here is a quick overview of the semantics : 15 14 \begin{cfacode} 16 15 int x, *p1 = &x, **p2 = &p1, ***p3 = &p2, 17 &r1 = x, &&r2 = r1, &&&r3 = r2;16 &r1 = x, &&r2 = r1, &&&r3 = r2; 18 17 ***p3 = 3; //change x 19 18 r3 = 3; //change x, ***r3 … … 26 25 sizeof(&ar[1]) == sizeof(int *); //is true, i.e., the size of a reference 27 26 \end{cfacode} 28 The important t ake away from this code example is that references offer a handle to an object, much like pointers, but which is automatically dereferenced for convinience.27 The important thing to take away from this code snippet is that references offer a handle to an object much like pointers but which is automatically derefferenced when convinient. 29 28 30 29 \section{Overloading} 31 30 32 Another important feature of \CFA is function overloading as in Java and \CC, where routine s with the same name are selected based on the numberand type of the arguments. As well, \CFA uses the return type as part of the selection criteria, as in Ada\cite{Ada}. For routines with multiple parameters and returns, the selection is complex.31 Another important feature of \CFA is function overloading as in Java and \CC, where routine with the same name are selected based on the numbers and type of the arguments. As well, \CFA uses the return type as part of the selection criteria, as in Ada\cite{Ada}. For routines with multiple parameters and returns, the selection is complex. 33 32 \begin{cfacode} 34 33 //selection based on type and number of parameters … … 46 45 double d = f(4); //select (2) 47 46 \end{cfacode} 48 This feature is particularly important for concurrency since the runtime system relies on creating different types to represent concurrency objects. Therefore, overloading is necessary to prevent the need for long prefixes and other naming conventions that prevent name clashes. As seen in chapter \ref{basics}, routine \code{main}is an example that benefits from overloading.47 This feature is particularly important for concurrency since the runtime system relies on creating different types to represent concurrency objects. Therefore, overloading is necessary to prevent the need for long prefixes and other naming conventions that prevent name clashes. As seen in chapter \ref{basics}, routines main is an example that benefits from overloading. 49 48 50 49 \section{Operators} 51 Overloading also extends to operators. The syntax for denoting operator-overloading is to name a routine with the symbol of the operator and question marks where the arguments of the operation occur, e.g.:50 Overloading also extends to operators. The syntax for denoting operator-overloading is to name a routine with the symbol of the operator and question marks where the arguments of the operation would be, like so : 52 51 \begin{cfacode} 53 52 int ++? (int op); //unary prefix increment … … 102 101 103 102 \section{Parametric Polymorphism} 104 Routines in \CFA can also be reused for multiple types. This capability is done using the \code{forall} clause which gives \CFA its name. \code{forall} clauses allow separately compiled routines to support generic usage over multiple types. For example, the following sum function works for any type that supportsconstruction from 0 and addition :103 Routines in \CFA can also be reused for multiple types. This is done using the \code{forall} clause which gives \CFA it's name. \code{forall} clauses allow seperatly compiled routines to support generic usage over multiple types. For example, the following sum function will work for any type which support construction from 0 and addition : 105 104 \begin{cfacode} 106 105 //constraint type, 0 and + … … 117 116 \end{cfacode} 118 117 119 Since writing constraints on types can become cumbersome for more constrained functions, \CFA also has the concept of traits. Traits are named collection of constraints thatcan be used both instead and in addition to regular constraints:118 Since writing constraints on types can become cumbersome for more constrained functions, \CFA also has the concept of traits. Traits are named collection of constraints which can be used both instead and in addition to regular constraints: 120 119 \begin{cfacode} 121 120 trait sumable( otype T ) { … … 131 130 132 131 \section{with Clause/Statement} 133 Since \CFA lacks the concept of a receiver, certain functions end-up needing to repeat variable names often . To remove this inconvenience, \CFA provides the \code{with} statement,which opens an aggregate scope making its fields directly accessible (like Pascal).132 Since \CFA lacks the concept of a receiver, certain functions end-up needing to repeat variable names often, to solve this \CFA offers the \code{with} statement which opens an aggregate scope making its fields directly accessible (like Pascal). 134 133 \begin{cfacode} 135 134 struct S { int i, j; }; 136 int mem(S & this) with (this)//with clause135 int mem(S & this) with this //with clause 137 136 i = 1; //this->i 138 137 j = 2; //this->j … … 141 140 struct S1 { ... } s1; 142 141 struct S2 { ... } s2; 143 with (s1)//with statement142 with s1 //with statement 144 143 { 145 144 //access fields of s1 146 145 //without qualification 147 with (s2)//nesting146 with s2 //nesting 148 147 { 149 148 //access fields of s1 and s2 … … 151 150 } 152 151 } 153 with (s1, s2)//scopes open in parallel152 with s1, s2 //scopes open in parallel 154 153 { 155 154 //access fields of s1 and s2 -
doc/proposals/concurrency/text/concurrency.tex
r0fe4e62 rf5c3b6c 4 4 % ====================================================================== 5 5 % ====================================================================== 6 Several tool can be used to solve concurrency challenges. Since many of these challenges appear with the use of mutable shared-state, some languages and libraries simply disallow mutable shared-state (Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, Akka (Scala)~\cite{Akka}). In these paradigms, interaction among concurrent objects relies on message passing~\cite{Thoth,Harmony,V-Kernel} or other paradigms closely relate to networking concepts (channels\cit e{CSP,Go}for example). However, in languages that use routine calls as their core abstraction-mechanism, these approaches force a clear distinction between concurrent and non-concurrent paradigms (i.e., message passing versus routine call). This distinction in turn means that, in order to be effective, programmers need to learn two sets of designs patterns. While this distinction can be hidden away in library code, effective use of the librairy still has to take both paradigms into account.6 Several tool can be used to solve concurrency challenges. Since many of these challenges appear with the use of mutable shared-state, some languages and libraries simply disallow mutable shared-state (Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, Akka (Scala)~\cite{Akka}). In these paradigms, interaction among concurrent objects relies on message passing~\cite{Thoth,Harmony,V-Kernel} or other paradigms closely relate to networking concepts (channels\cit for example). However, in languages that use routine calls as their core abstraction-mechanism, these approaches force a clear distinction between concurrent and non-concurrent paradigms (i.e., message passing versus routine call). This distinction in turn means that, in order to be effective, programmers need to learn two sets of designs patterns. While this distinction can be hidden away in library code, effective use of the librairy still has to take both paradigms into account. 7 7 8 8 Approaches based on shared memory are more closely related to non-concurrent paradigms since they often rely on basic constructs like routine calls and shared objects. At the lowest level, concurrent paradigms are implemented as atomic operations and locks. Many such mechanisms have been proposed, including semaphores~\cite{Dijkstra68b} and path expressions~\cite{Campbell74}. However, for productivity reasons it is desireable to have a higher-level construct be the core concurrency paradigm~\cite{HPP:Study}. 9 9 10 An approach that is worth mention ing because it is gaining in popularity is transactionnal memory~\cite{Dice10}[Check citation]. While this approach is even pursued by system languages like \CC\cite{Cpp-Transactions}, the performance and feature set is currently too restrictive to be the main concurrency paradigm for systems language, which is why it was rejected as the core paradigm for concurrency in \CFA.10 An approach that is worth mentionning because it is gaining in popularity is transactionnal memory~\cite{Dice10}[Check citation]. While this approach is even pursued by system languages like \CC\cit, the performance and feature set is currently too restrictive to be the main concurrency paradigm for systems language, which is why it was rejected as the core paradigm for concurrency in \CFA. 11 11 12 12 One of the most natural, elegant, and efficient mechanisms for synchronization and communication, especially for shared-memory systems, is the \emph{monitor}. Monitors were first proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}. Many programming languages---e.g., Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}---provide monitors as explicit language constructs. In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as semaphores or locks to simulate monitors. For these reasons, this project proposes monitors as the core concurrency-construct. … … 19 19 20 20 \subsection{Synchronization} 21 As for mutual-exclusion, low-level synchronisation primitives often offer good performance and good flexibility at the cost of ease of use. Again, higher-level mechanism often simplify usage by adding better coupling between synchronization and data, e.g.: message passing, or offering simple r solution to otherwise involved challenges. As mentioned above, synchronization can be expressed as guaranteeing that event \textit{X} always happens before \textit{Y}. Most of the time, synchronisation happens within a critical section, where threads must acquire mutual-exclusion in a certain order. However, it may also be desirable to guarantee that event \textit{Z} does not occur between \textit{X} and \textit{Y}. Not satisfying this property called barging. For example, where event \textit{X} tries to effect event \textit{Y} but another thread acquires the critical section and emits \textit{Z} before \textit{Y}. The classic exmaple is the thread that finishes using a ressource and unblocks a thread waiting to use the resource, but the unblocked thread must compete again to acquire the resource. Preventing or detecting barging is an involved challenge with low-level locks, which can be made much easier by higher-level constructs. This challenge is often split into two different methods, barging avoidance and barging prevention. Algorithms that use status flags and other flag variables to detect barging threads are said to be using barging avoidance while algorithms that baton-passing locks between threads instead of releasing the locks are said to be using barging prevention.21 As for mutual-exclusion, low-level synchronisation primitives often offer good performance and good flexibility at the cost of ease of use. Again, higher-level mechanism often simplify usage by adding better coupling between synchronization and data, e.g.: message passing, or offering simple solution to otherwise involved challenges. An example is barging. As mentioned above, synchronization can be expressed as guaranteeing that event \textit{X} always happens before \textit{Y}. Most of the time, synchronisation happens around a critical section, where threads must acquire critical sections in a certain order. However, it may also be desirable to guarantee that event \textit{Z} does not occur between \textit{X} and \textit{Y}. Not satisfying this property called barging. For example, where event \textit{X} tries to effect event \textit{Y} but another thread acquires the critical section and emits \textit{Z} before \textit{Y}. Preventing or detecting barging is an involved challenge with low-level locks, which can be made much easier by higher-level constructs. This challenge is often split into two different methods, barging avoidance and barging prevention. Algorithms that use status flags and other flag variables to detect barging threads are said to be using barging avoidance while algorithms that baton-passing locks between threads instead of releasing the locks are said to be using barging prevention. 22 22 23 23 % ====================================================================== … … 71 71 \end{tabular} 72 72 \end{center} 73 Notice how the counter is used without any explicit synchronisation and yet supports thread-safe semantics for both reading and writting , which is similar in usage to \CC \code{atomic} template.74 75 Here, the constructor(\code{?\{\}}) uses the \code{nomutex} keyword to signify that it does not acquire the monitor mutual-exclusion when constructing. This semantics is because an object not yet con \-structed should never be shared and therefore does not require mutual exclusion. The prefix increment operator uses \code{mutex} to protect the incrementing process from race conditions. Finally, there is a conversion operator from \code{counter_t} to \code{size_t}. This conversion may or may not require the \code{mutex} keyword depending on whether or not reading a \code{size_t} is an atomic operation.76 77 For maximum usability, monitors use \gls{multi-acq} semantics, which means a single thread can acquire the same monitor multiple timeswithout deadlock. For example, figure \ref{fig:search} uses recursion and \gls{multi-acq} to print values inside a binary tree.73 Notice how the counter is used without any explicit synchronisation and yet supports thread-safe semantics for both reading and writting. 74 75 Here, the constructor(\code{?\{\}}) uses the \code{nomutex} keyword to signify that it does not acquire the monitor mutual-exclusion when constructing. This semantics is because an object not yet constructed should never be shared and therefore does not require mutual exclusion. The prefix increment operator uses \code{mutex} to protect the incrementing process from race conditions. Finally, there is a conversion operator from \code{counter_t} to \code{size_t}. This conversion may or may not require the \code{mutex} keyword depending on whether or not reading a \code{size_t} is an atomic operation. 76 77 For maximum usability, monitors use \gls{multi-acq} semantics, which means a single thread can acquire multiple times the same monitor without deadlock. For example, figure \ref{fig:search} uses recursion and \gls{multi-acq} to print values inside a binary tree. 78 78 \begin{figure} 79 79 \label{fig:search} … … 95 95 \end{figure} 96 96 97 Having both \code{mutex} and \code{nomutex} keywords is redundant based on the meaning of a routine having neither of these keywords. For example, given a routine without qualifiers \code{void foo(counter_t & this)}, then it is reasonable that it should default to the safest option \code{mutex}, whereas assuming \code{nomutex} is unsafe and may cause subtle errors. In fact, \code{nomutex} is the ``normal'' parameter behaviour, with the \code{nomutex} keyword effectively stating explicitly that ``this routine is not special''. Another alternative is making exactly one of these keywords mandatory, which providesthe same semantics but without the ambiguity of supporting routines with neither keyword. Mandatory keywords would also have the added benefit of being self-documented but at the cost of extra typing. While there are several benefits to mandatory keywords, they do bring a few challenges. Mandatory keywords in \CFA would imply that the compiler must know without doubt whether or not a parameter is a monitor or not. Since \CFA relies heavily on traits as an abstraction mechanism, the distinction between a type that is a monitor and a type that looks like a monitor can become blurred. For this reason, \CFA only has the \code{mutex} keyword and uses no keyword to mean \code{nomutex}.97 Having both \code{mutex} and \code{nomutex} keywords is redundant based on the meaning of a routine having neither of these keywords. For example, given a routine without qualifiers \code{void foo(counter_t & this)}, then it is reasonable that it should default to the safest option \code{mutex}, whereas assuming \code{nomutex} is unsafe and may cause subtle errors. In fact, \code{nomutex} is the "normal" parameter behaviour, with the \code{nomutex} keyword effectively stating explicitly that "this routine is not special". Another alternative is making exactly one of these keywords mandatory, which would provide the same semantics but without the ambiguity of supporting routines with neither keyword. Mandatory keywords would also have the added benefit of being self-documented but at the cost of extra typing. While there are several benefits to mandatory keywords, they do bring a few challenges. Mandatory keywords in \CFA would imply that the compiler must know without doubt whether or not a parameter is a monitor or not. Since \CFA relies heavily on traits as an abstraction mechanism, the distinction between a type that is a monitor and a type that looks like a monitor can become blurred. For this reason, \CFA only has the \code{mutex} keyword and uses no keyword to mean \code{nomutex}. 98 98 99 99 The next semantic decision is to establish when \code{mutex} may be used as a type qualifier. Consider the following declarations: … … 113 113 int f5(monitor * mutex m []); //Not Okay : Array of unkown length 114 114 \end{cfacode} 115 Note that not all array functions are actually distinct in the type system . However, even ifthe code generation could tell the difference, the extra information is still not sufficient to extend meaningfully the monitor call semantic.116 117 Unlike object-oriented monitors, where calling a mutex member \emph{implicitly} acquires mutual-exclusion of the receiverobject, \CFA uses an explicit mechanism to acquire mutual-exclusion. A consequence of this approach is that it extends naturally to multi-monitor calls.115 Note that not all array functions are actually distinct in the type system sense. However, even the code generation could tell the difference, the extra information is still not sufficient to extend meaningfully the monitor call semantic. 116 117 Unlike object-oriented monitors, where calling a mutex member \emph{implicitly} acquires mutual-exclusion often receives an object, \CFA uses an explicit mechanism to acquire mutual-exclusion. A consequence of this approach is that it extends naturally to multi-monitor calls. 118 118 \begin{cfacode} 119 119 int f(MonitorA & mutex a, MonitorB & mutex b); … … 123 123 f(a,b); 124 124 \end{cfacode} 125 While OO monitors could be extended with a mutex qualifier for multiple-monitor calls, no example of this feature could be found. The capacity to acquire multiple locks before entering a critical section is called \emph{\gls{bulk-acq}}. In practice, writing multi-locking routines that do not lead to deadlocks is tricky. Having language support for such a feature is therefore a significant asset for \CFA. In the case presented above, \CFA guarantees that the order of aquisition is consistent across calls to different routines using the same monitors as arguments. This consistent ordering means acquiring multiple monitors in the way is safe from deadlock. However, users can stillforce the acquiring order. For example, notice which routines use \code{mutex}/\code{nomutex} and how this affects aquiring order:125 The capacity to acquire multiple locks before entering a critical section is called \emph{\gls{bulk-acq}}. In practice, writing multi-locking routines that do not lead to deadlocks is tricky. Having language support for such a feature is therefore a significant asset for \CFA. In the case presented above, \CFA guarantees that the order of aquisition is consistent across calls to routines using the same monitors as arguments. However, since \CFA monitors use \gls{multi-acq} locks, users can effectively force the acquiring order. For example, notice which routines use \code{mutex}/\code{nomutex} and how this affects aquiring order: 126 126 \begin{cfacode} 127 127 void foo(A & mutex a, B & mutex b) { //acquire a & b … … 139 139 The \gls{multi-acq} monitor lock allows a monitor lock to be acquired by both \code{bar} or \code{baz} and acquired again in \code{foo}. In the calls to \code{bar} and \code{baz} the monitors are acquired in opposite order. 140 140 141 However, such use leads to the lock acquiring order problem. In the example above, the user uses implicit ordering in the case of function \code{foo} but explicit ordering in the case of \code{bar} and \code{baz}. This subtle mistake means that calling these routines concurrently may lead to deadlock and is therefore undefined behavior. As shown \cite{Lister77}, solving this problem requires:141 However, such use leads to the lock acquiring order problem. In the example above, the user uses implicit ordering in the case of function \code{foo} but explicit ordering in the case of \code{bar} and \code{baz}. This subtle mistake means that calling these routines concurrently may lead to deadlock and is therefore undefined behavior. As shown on several occasion\cit, solving this problem requires: 142 142 \begin{enumerate} 143 143 \item Dynamically tracking of the monitor-call order. 144 144 \item Implement rollback semantics. 145 145 \end{enumerate} 146 While the first requirement is already a significant constraint on the system, implementing a general rollback semantics in a C-like language is still prohibitively complex \cite{Dice10}. In \CFA, users simply need to be carefull when acquiring multiple monitors at the same time or only use \gls{bulk-acq} of all the monitors. While \CFA provides only a partial solution, many system provide no solution and the \CFA partial solution handles many useful cases.147 148 For example, \gls{multi-acq} and \gls{bulk-acq} can be used together in interesting ways:146 While the first requirement is already a significant constraint on the system, implementing a general rollback semantics in a C-like language is prohibitively complex \cit. In \CFA, users simply need to be carefull when acquiring multiple monitors at the same time or only use \gls{bulk-acq} of all the monitors. 147 148 \Gls{multi-acq} and \gls{bulk-acq} can be used together in interesting ways, for example: 149 149 \begin{cfacode} 150 150 monitor bank { ... }; … … 157 157 } 158 158 \end{cfacode} 159 This example shows a trivial solution to the bank -account transfer-problem\cite{BankTransfer}. Without \gls{multi-acq} and \gls{bulk-acq}, the solution to this problem is much more involved and requires carefull engineering.160 161 \subs ection{\code{mutex} statement} \label{mutex-stmt}162 163 The call semantics discussed aboved have one software engineering issue, only a named routine can acquire the mutual-exclusion of a set of monitor. \CFA offers the \code{mutex} statement to workaround the need for unnecessary names, avoiding a major software engineering problem\cit e{2FTwoHardThings}. Listing \ref{lst:mutex-stmt} shows an example of the \code{mutex} statement, which introduces a new scope in which the mutual-exclusion of a set of monitor is acquired. Beyond naming, the \code{mutex} statement has no semantic difference from a routine call with \code{mutex} parameters.159 This example shows a trivial solution to the bank account transfer problem\cit. Without \gls{multi-acq} and \gls{bulk-acq}, the solution to this problem is much more involved and requires carefull engineering. 160 161 \subsubsection{\code{mutex} statement} \label{mutex-stmt} 162 163 The call semantics discussed aboved have one software engineering issue, only a named routine can acquire the mutual-exclusion of a set of monitor. \CFA offers the \code{mutex} statement to workaround the need for unnecessary names, avoiding a major software engineering problem\cit. Listing \ref{lst:mutex-stmt} shows an example of the \code{mutex} statement, which introduces a new scope in which the mutual-exclusion of a set of monitor is acquired. Beyond naming, the \code{mutex} statement has no semantic difference from a routine call with \code{mutex} parameters. 164 164 165 165 \begin{figure} … … 218 218 \end{cfacode} 219 219 220 Like threads and coroutines, monitors are defined in terms of traits with some additional language support in the form of the \code{monitor} keyword. The monitor trait is : 221 \begin{cfacode} 222 trait is_monitor(dtype T) { 223 monitor_desc * get_monitor( T & ); 224 void ^?{}( T & mutex ); 225 }; 226 \end{cfacode} 227 Note that the destructor of a monitor must be a \code{mutex} routine. This requirement ensures that the destructor has mutual-exclusion. As with any object, any call to a monitor, using \code{mutex} or otherwise, is Undefined Behaviour after the destructor has run. 228 229 % ====================================================================== 230 % ====================================================================== 231 \section{Internal scheduling} \label{intsched} 232 % ====================================================================== 233 % ====================================================================== 234 In addition to mutual exclusion, the monitors at the core of \CFA's concurrency can also be used to achieve synchronisation. With monitors, this capability is generally achieved with internal or external scheduling as in \cite{Hoare74}. Since internal scheduling within a single monitor is mostly a solved problem, this thesis concentrates on extending internal scheduling to multiple monitors. Indeed, like the \gls{bulk-acq} semantics, internal scheduling extends to multiple monitors in a way that is natural to the user but requires additional complexity on the implementation side. 220 221 % ====================================================================== 222 % ====================================================================== 223 \section{Internal scheduling} \label{insched} 224 % ====================================================================== 225 % ====================================================================== 226 In addition to mutual exclusion, the monitors at the core of \CFA's concurrency can also be used to achieve synchronisation. With monitors, this capability is generally achieved with internal or external scheduling as in\cit. Since internal scheduling within a single monitor is mostly a solved problem, this thesis concentrates on extending internal scheduling to multiple monitors. Indeed, like the \gls{bulk-acq} semantics, internal scheduling extends to multiple monitors in a way that is natural to the user but requires additional complexity on the implementation side. 235 227 236 228 First, here is a simple example of such a technique: … … 256 248 \end{cfacode} 257 249 258 There are two details to note here. First, the \code{signal} is a delayed operation, it only unblocks the waiting thread when it reaches the end of the critical section. This semantic is needed to respect mutual-exclusion. The alternative is to return immediately after the call to \code{signal}, which is significantly more restrictive. Second, in \CFA, while it is common to store a \code{condition} as a field of the monitor, a \code{condition} variable can be stored/created independently of a monitor. Here routine \code{foo} waits for the \code{signal} from \code{bar} before making further progress, effectively ensuring a basic ordering.259 260 An important aspect of the implementation is that \CFA does not allow barging, which means that once function \code{bar} releases the monitor, \code{foo}is guaranteed to resume immediately after (unless some other thread waited on the same condition). This guarantees offers the benefit of not having to loop arount waits in order to guarantee that a condition is still met. The main reason \CFA offers this guarantee is that users can easily introduce barging if it becomes a necessity but adding barging prevention or barging avoidance is more involved without language support. Supporting barging prevention as well as extending internal scheduling to multiple monitors is the main source of complexity in the design of \CFA concurrency.250 There are two details to note here. First, the \code{signal} is a delayed operation, it only unblocks the waiting thread when it reaches the end of the critical section. This semantic is needed to respect mutual-exclusion. Second, in \CFA, a \code{condition} variable can be stored/created independently of a monitor. Here routine \code{foo} waits for the \code{signal} from \code{bar} before making further progress, effectively ensuring a basic ordering. 251 252 An important aspect of the implementation is that \CFA does not allow barging, which means that once function \code{bar} releases the monitor, foo is guaranteed to resume immediately after (unless some other thread waited on the same condition). This guarantees offers the benefit of not having to loop arount waits in order to guarantee that a condition is still met. The main reason \CFA offers this guarantee is that users can easily introduce barging if it becomes a necessity but adding barging prevention or barging avoidance is more involved without language support. Supporting barging prevention as well as extending internal scheduling to multiple monitors is the main source of complexity in the design of \CFA concurrency. 261 253 262 254 % ====================================================================== … … 265 257 % ====================================================================== 266 258 % ====================================================================== 267 It is easier to understand the problem of multi-monitor scheduling using a series of pseudo-code. Note that for simplicity in the following snippets of pseudo-code, waiting and signalling is done using an implicit condition variable, like Java built-in monitors. Indeed, \code{wait} statements always use the implicit condition as paremeter and explicitly names the monitors (A and B) associated with the condition. Note that in \CFA, condition variables are tied to a set of monitors on first use (called branding) which means that using internal scheduling with distinct sets of monitors requires one condition variable per set of monitors.259 It is easier to understand the problem of multi-monitor scheduling using a series of pseudo-code. Note that for simplicity in the following snippets of pseudo-code, waiting and signalling is done using an implicit condition variable, like Java built-in monitors. Indeed, \code{wait} statements always use a single condition as paremeter and waits on the monitors associated with the condition. 268 260 269 261 \begin{multicols}{2} … … 303 295 \end{pseudo} 304 296 \end{multicols} 305 This version uses \gls{bulk-acq} (denoted using the {\sf\&}symbol), but the presence of multiple monitors does not add a particularly new meaning. Synchronization happens between the two threads in exactly the same way and order. The only difference is that mutual exclusion covers more monitors. On the implementation side, handling multiple monitors does add a degree of complexity as the next few examples demonstrate.306 307 While deadlock issues can occur when nesting monitors, these issues are only a symptom of the fact that locks, and by extension monitors, are not perfectly composable. For monitors, a well known deadlock problem is the Nested Monitor Problem \cite{Lister77}, which occurs when a \code{wait} is made by a thread that holds more than one monitor. For example, the following pseudo-code runs into the nested-monitor problem :297 This version uses \gls{bulk-acq} (denoted using the \& symbol), but the presence of multiple monitors does not add a particularly new meaning. Synchronization happens between the two threads in exactly the same way and order. The only difference is that mutual exclusion covers more monitors. On the implementation side, handling multiple monitors does add a degree of complexity as the next few examples demonstrate. 298 299 While deadlock issues can occur when nesting monitors, these issues are only a symptom of the fact that locks, and by extension monitors, are not perfectly composable. For monitors, a well known deadlock problem is the Nested Monitor Problem\cit, which occurs when a \code{wait} is made on a thread that holds more than one monitor. For example, the following pseudo-code will run into the nested monitor problem : 308 300 \begin{multicols}{2} 309 301 \begin{pseudo} … … 325 317 \end{pseudo} 326 318 \end{multicols} 327 328 The \code{wait} only releases monitor \code{B} so the signalling thread cannot acquire monitor \code{A} to get to the \code{signal}. Attempting release of all acquired monitors at the \code{wait} results in another set of problems such as releasing monitor \code{C}, which has nothing to do with the \code{signal}.329 330 319 However, for monitors as for locks, it is possible to write a program using nesting without encountering any problems if nesting is done correctly. For example, the next pseudo-code snippet acquires monitors {\sf A} then {\sf B} before waiting, while only acquiring {\sf B} when signalling, effectively avoiding the nested monitor problem. 331 320 … … 350 339 \end{multicols} 351 340 352 % ====================================================================== 353 % ====================================================================== 354 \subsection{Internal Scheduling - in depth} 355 % ====================================================================== 356 % ====================================================================== 357 358 A larger example is presented to show complex issuesfor \gls{bulk-acq} and all the implementation options are analyzed. Listing \ref{lst:int-bulk-pseudo} shows an example where \gls{bulk-acq} adds a significant layer of complexity to the internal signalling semantics, and listing \ref{lst:int-bulk-cfa} shows the corresponding \CFA code which implements the pseudo-code in listing \ref{lst:int-bulk-pseudo}. For the purpose of translating the given pseudo-code into \CFA-code any method of introducing monitor into context, other than a \code{mutex} parameter, is acceptable, e.g., global variables, pointer parameters or using locals with the \code{mutex}-statement. 341 Listing \ref{lst:int-bulk-pseudo} shows an example where \gls{bulk-acq} adds a significant layer of complexity to the internal signalling semantics. Listing \ref{lst:int-bulk-cfa} shows the corresponding \CFA code which implements the pseudo-code in listing \ref{lst:int-bulk-pseudo}. Note that listing \ref{lst:int-bulk-cfa} uses non-\code{mutex} parameter to introduce monitor \code{b} into context. However, for the purpose of translating the given pseudo-code into \CFA-code any method of introducing new monitors into context, other than a \code{mutex} parameter, is acceptable, e.g. global variables, pointer parameters or using locals with the \code{mutex}-statement. 359 342 360 343 \begin{figure}[!b] … … 393 376 394 377 \begin{figure}[!b] 395 \begin{center}396 \begin{cfacode}[xleftmargin=.4\textwidth]397 monitor A a;398 monitor B b;399 condition c;400 \end{cfacode}401 \end{center}402 378 \begin{multicols}{2} 403 379 Waiting thread 404 380 \begin{cfacode} 405 mutex(a) { 381 monitor A; 382 monitor B; 383 extern condition c; 384 void foo(A & mutex a, B & b) { 406 385 //Code Section 1 407 386 mutex(a, b) { … … 418 397 Signalling thread 419 398 \begin{cfacode} 420 mutex(a) { 399 monitor A; 400 monitor B; 401 extern condition c; 402 void foo(A & mutex a, B & b) { 421 403 //Code Section 5 422 404 mutex(a, b) { … … 433 415 \end{figure} 434 416 435 The complexity begins at code sections 4 and 8, which are where the existing semantics of internal scheduling need to be extended for multiple monitors. The root of the problem is that \gls{bulk-acq} is used in a context where one of the monitors is already acquired and is why it is important to define the behaviour of the previous pseudo-code. When the signaller thread reaches the location where it should ``release \code{A & B}'' (line 16), it must actually transfer ownership of monitor \code{B} to the waiting thread. This ownership trasnfer is required in order to prevent barging. Since the signalling thread still needs monitor \code{A}, simply waking up the waiting thread is not an option because it violatesmutual exclusion. There are three options.417 It is particularly important to pay attention to code sections 4 and 8, which are where the existing semantics of internal scheduling need to be extended for multiple monitors. The root of the problem is that \gls{bulk-acq} is used in a context where one of the monitors is already acquired and is why it is important to define the behaviour of the previous pseudo-code. When the signaller thread reaches the location where it should "release A \& B" (line 16), it must actually transfer ownership of monitor B to the waiting thread. This ownership trasnfer is required in order to prevent barging. Since the signalling thread still needs monitor A, simply waking up the waiting thread is not an option because it would violate mutual exclusion. There are three options. 436 418 437 419 \subsubsection{Delaying signals} 438 The obvious solution to solve the problem of multi-monitor scheduling is to keep ownership of all locks until the last lock is ready to be transferred. It can be argued that that moment is when the last lock is no longer needed because this semantics fits most closely to the behaviour of single-monitor scheduling. This solution has the main benefit of transferring ownership of groups of monitors, which simplifies the semantics from mutiple objects to a single group of objects, effectively making the existing single-monitor semantic viable by simply changing monitors to monitor groups.420 The first more obvious solution to solve the problem of multi-monitor scheduling is to keep ownership of all locks until the last lock is ready to be transferred. It can be argued that that moment is the correct time to transfer ownership when the last lock is no longer needed because this semantics fits most closely to the behaviour of single monitor scheduling. This solution has the main benefit of transferring ownership of groups of monitors, which simplifies the semantics from mutiple objects to a single group of objects, effectively making the existing single monitor semantic viable by simply changing monitors to monitor groups. 439 421 \begin{multicols}{2} 440 422 Waiter … … 461 443 \end{multicols} 462 444 However, this solution can become much more complicated depending on what is executed while secretly holding B (at line 10). Indeed, nothing prevents signalling monitor A on a different condition variable: 463 \begin{figure} 464 \begin{multicols}{3} 465 Thread $\alpha$ 445 \begin{multicols}{2} 446 Thread 1 466 447 \begin{pseudo}[numbers=left, firstnumber=1] 467 448 acquire A … … 472 453 \end{pseudo} 473 454 474 \columnbreak 475 476 Thread $\gamma$ 477 \begin{pseudo}[numbers=left, firstnumber=1] 455 Thread 2 456 \begin{pseudo}[numbers=left, firstnumber=6] 457 acquire A 458 wait A 459 release A 460 \end{pseudo} 461 462 \columnbreak 463 464 Thread 3 465 \begin{pseudo}[numbers=left, firstnumber=9] 478 466 acquire A 479 467 acquire A & B 480 468 signal A & B 481 469 release A & B 470 //Secretly keep B here 482 471 signal A 483 472 release A 484 \end{pseudo} 485 486 \columnbreak 487 488 Thread $\beta$ 489 \begin{pseudo}[numbers=left, firstnumber=1] 490 acquire A 491 wait A 492 release A 493 \end{pseudo} 494 495 \end{multicols} 496 \caption{Dependency graph} 497 \label{lst:dependency} 498 \end{figure} 473 //Wakeup thread 1 or 2? 474 //Who wakes up the other thread? 475 \end{pseudo} 476 \end{multicols} 499 477 500 478 The goal in this solution is to avoid the need to transfer ownership of a subset of the condition monitors. However, this goal is unreacheable in the previous example. Depending on the order of signals (line 12 and 15) two cases can happen. … … 506 484 Note that ordering is not determined by a race condition but by whether signalled threads are enqueued in FIFO or FILO order. However, regardless of the answer, users can move line 15 before line 11 and get the reverse effect. 507 485 508 In both cases, the threads need to be able to distinguish, on a per monitor basis, which ones need to be released and which ones need to be transferred, which means monitors cannot be handled as a single homogenous group and therefore effectively precludesthis approach.486 In both cases, the threads need to be able to distinguish, on a per monitor basis, which ones need to be released and which ones need to be transferred, which means monitors cannot be handled as a single homogenous group and therefore invalidates the main benefit of this approach. 509 487 510 488 \subsubsection{Dependency graphs} 511 In the listing \ref{lst:int-bulk-pseudo} pseudo-code, there is a solution which statisfies both barging prevention and mutual exclusion. If ownership of both monitors is transferred to the waiter when the signaller releases \code{A & B} and then the waiter transfers back ownership of \code{A} when it releases it, then the problem is solved (\code{B} is no longer in use at this point). Dynamically finding the correct order is therefore the second possible solution. The problem it encounters is that it effectively boils down to resolving a dependency graph of ownership requirements. Here even the simplest of code snippets requires two transfers and it seems to increase in a manner closer to polynomial. For example, the following code, which is just a direct extension to three monitors, requires at least three ownership transfer and has multiple solutions:489 In the Listing 1 pseudo-code, there is a solution which statisfies both barging prevention and mutual exclusion. If ownership of both monitors is transferred to the waiter when the signaller releases A and then the waiter transfers back ownership of A when it releases it, then the problem is solved. Dynamically finding the correct order is therefore the second possible solution. The problem it encounters is that it effectively boils down to resolving a dependency graph of ownership requirements. Here even the simplest of code snippets requires two transfers and it seems to increase in a manner closer to polynomial. For example, the following code, which is just a direct extension to three monitors, requires at least three ownership transfer and has multiple solutions: 512 490 513 491 \begin{multicols}{2} … … 536 514 537 515 \begin{figure} 516 \begin{multicols}{3} 517 Thread $\alpha$ 518 \begin{pseudo}[numbers=left, firstnumber=1] 519 acquire A 520 acquire A & B 521 wait A & B 522 release A & B 523 release A 524 \end{pseudo} 525 526 \columnbreak 527 528 Thread $\gamma$ 529 \begin{pseudo}[numbers=left, firstnumber=1] 530 acquire A 531 acquire A & B 532 signal A & B 533 release A & B 534 signal A 535 release A 536 \end{pseudo} 537 538 \columnbreak 539 540 Thread $\beta$ 541 \begin{pseudo}[numbers=left, firstnumber=1] 542 acquire A 543 wait A 544 release A 545 \end{pseudo} 546 547 \end{multicols} 548 \caption{Dependency graph} 549 \label{lst:dependency} 550 \end{figure} 551 552 \begin{figure} 538 553 \begin{center} 539 554 \input{dependency} 540 555 \end{center} 556 \label{fig:dependency} 541 557 \caption{Dependency graph of the statements in listing \ref{lst:dependency}} 542 \label{fig:dependency}543 558 \end{figure} 544 559 545 Listing \ref{lst:dependency} is the three thread example rewritten for dependency graphs . Figure \ref{fig:dependency} shows the corresponding dependency graph that results, where every node is a statement of one of the three threads, and the arrows the dependency of that statement (e.g., $\alpha1$ must happen before $\alpha2$). The extra challenge is that this dependency graph is effectively post-mortem, but the runtime system needs to be able to build and solve these graphs as the dependency unfolds. Resolving dependency graph being a complex and expensive endeavour, this solution is not the preffered one.560 Listing \ref{lst:dependency} is the three thread example rewritten for dependency graphs as well as the corresponding dependency graph. Figure \ref{fig:dependency} shows the corresponding dependency graph that results, where every node is a statement of one of the three threads, and the arrows the dependency of that statement. The extra challenge is that this dependency graph is effectively post-mortem, but the run time system needs to be able to build and solve these graphs as the dependency unfolds. Resolving dependency graph being a complex and expensive endeavour, this solution is not the preffered one. 546 561 547 562 \subsubsection{Partial signalling} \label{partial-sig} 548 Finally, the solution that is chosen for \CFA is to use partial signalling. Again using listing \ref{lst:int-bulk-pseudo}, the partial signalling solution transfers ownership of monitor B at lines 10 but does not wake the waiting thread since it is still using monitor A. Only when it reaches line 11 does it actually wakeup the waiting thread. This solution has the benefit that complexity is encapsulated into only two actions, passing monitors to the next owner when they should be release and conditionally waking threads if all conditions are met. This solution has a much simpler implementation than a dependency graph solving algorithm which is why it was chosen. Furthermore, after being fully implemented, this solution does not appear to have any downsides worth mentionning. 563 Finally, the solution that is chosen for \CFA is to use partial signalling. Consider the following case: 564 565 \begin{multicols}{2} 566 \begin{pseudo}[numbers=left] 567 acquire A 568 acquire A & B 569 wait A & B 570 release A & B 571 release A 572 \end{pseudo} 573 574 \columnbreak 575 576 \begin{pseudo}[numbers=left, firstnumber=6] 577 acquire A 578 acquire A & B 579 signal A & B 580 release A & B 581 //... More code 582 release A 583 \end{pseudo} 584 \end{multicols} 585 The partial signalling solution transfers ownership of monitor B at lines 10 but does not wake the waiting thread since it is still using monitor A. Only when it reaches line 11 does it actually wakeup the waiting thread. This solution has the benefit that complexity is encapsulated into only two actions, passing monitors to the next owner when they should be release and conditionally waking threads if all conditions are met. This solution has a much simpler implementation than a dependency graph solving algorithm which is why it was chosen. 549 586 550 587 % ====================================================================== … … 553 590 % ====================================================================== 554 591 % ====================================================================== 592 An important note is that, until now, signalling a monitor was a delayed operation. The ownership of the monitor is transferred only when the monitor would have otherwise been released, not at the point of the \code{signal} statement. However, in some cases, it may be more convenient for users to immediately transfer ownership to the thread that is waiting for cooperation, which is achieved using the \code{signal_block} routine\footnote{name to be discussed}. 593 594 The example in listing \ref{lst:datingservice} highlights the difference in behaviour. As mentioned, \code{signal} only transfers ownership once the current critical section exits, this behaviour cause the need for additional synchronisation when a two-way handshake is needed. To avoid this extraneous synchronisation, the \code{condition} type offers the \code{signal_block} routine which handle two-way handshakes as shown in the example. This removes the need for a second condition variables and simplifies programming. Like every other monitor semantic, \code{signal_block} uses barging prevention which means mutual-exclusion is baton-passed both on the frond-end and the back-end of the call to \code{signal_block}, meaning no other thread can acquire the monitor neither before nor after the call. 555 595 \begin{figure} 556 596 \begin{tabular}{|c|c|} … … 582 622 girlPhoneNo = phoneNo; 583 623 584 //wake boy fro mchair624 //wake boy fron chair 585 625 signal(exchange); 586 626 } … … 629 669 girlPhoneNo = phoneNo; 630 670 631 //wake boy fro mchair671 //wake boy fron chair 632 672 signal(exchange); 633 673 } … … 656 696 \label{lst:datingservice} 657 697 \end{figure} 658 An important note is that, until now, signalling a monitor was a delayed operation. The ownership of the monitor is transferred only when the monitor would have otherwise been released, not at the point of the \code{signal} statement. However, in some cases, it may be more convenient for users to immediately transfer ownership to the thread that is waiting for cooperation, which is achieved using the \code{signal_block} routine\footnote{name to be discussed}.659 660 The example in listing \ref{lst:datingservice} highlights the difference in behaviour. As mentioned, \code{signal} only transfers ownership once the current critical section exits, this behaviour requires additional synchronisation when a two-way handshake is needed. To avoid this extraneous synchronisation, the \code{condition} type offers the \code{signal_block} routine, which handles the two-way handshake as shown in the example. This removes the need for a second condition variables and simplifies programming. Like every other monitor semantic, \code{signal_block} uses barging prevention, which means mutual-exclusion is baton-passed both on the frond-end and the back-end of the call to \code{signal_block}, meaning no other thread can acquire the monitor neither before nor after the call.661 698 662 699 % ====================================================================== … … 665 702 % ====================================================================== 666 703 % ====================================================================== 667 An alternative to internal scheduling is external scheduling, e.g., in \uC.704 An alternative to internal scheduling is to use external scheduling. 668 705 \begin{center} 669 \begin{tabular}{|c|c| c|}670 Internal Scheduling & External Scheduling & Go\\706 \begin{tabular}{|c|c|} 707 Internal Scheduling & External Scheduling \\ 671 708 \hline 672 \begin{ucppcode} [tabsize=3]709 \begin{ucppcode} 673 710 _Monitor Semaphore { 674 711 condition c; … … 676 713 public: 677 714 void P() { 678 if(inUse) 679 wait(c); 715 if(inUse) wait(c); 680 716 inUse = true; 681 717 } … … 685 721 } 686 722 } 687 \end{ucppcode}&\begin{ucppcode} [tabsize=3]723 \end{ucppcode}&\begin{ucppcode} 688 724 _Monitor Semaphore { 689 725 … … 691 727 public: 692 728 void P() { 693 if(inUse) 694 _Accept(V); 729 if(inUse) _Accept(V); 695 730 inUse = true; 696 731 } … … 700 735 } 701 736 } 702 \end{ucppcode}&\begin{gocode}[tabsize=3] 703 type MySem struct { 704 inUse bool 705 c chan bool 706 } 707 708 // acquire 709 func (s MySem) P() { 710 if s.inUse { 711 select { 712 case <-s.c: 713 } 714 } 715 s.inUse = true 716 } 717 718 // release 719 func (s MySem) V() { 720 s.inUse = false 721 722 //This actually deadlocks 723 //when single thread 724 s.c <- false 725 } 726 \end{gocode} 737 \end{ucppcode} 727 738 \end{tabular} 728 739 \end{center} 729 This method is more constrained and explicit, which helps users tone down the undeterministic nature of concurrency. Indeed, as the following examples demonstrates, external scheduling allows users to wait for events from other threads without the concern of unrelated events occuring. External scheduling can generally be done either in terms of control flow (e.g., \uC with \code{_Accept}) or in terms of data (e.g. ,Go with channels). Of course, both of these paradigms have their own strenghts and weaknesses but for this project control-flow semantics were chosen to stay consistent with the rest of the languages semantics. Two challenges specific to \CFA arise when trying to add external scheduling with loose object definitions and multi-monitor routines. The previous example shows a simple use \code{_Accept} versus \code{wait}/\code{signal} and its advantages. Note that while other languages often use \code{accept}/\code{select} as the core external scheduling keyword, \CFA uses \code{waitfor} to prevent name collisions with existing socket \acrshort{api}s.730 731 For the \code{P} member above using internal scheduling, the call to \code{wait} only guarantees that \code{V} is the last routine to access the monitor, allowing a third routine, say \code{isInUse()}, acquire mutual exclusion several times while routine \code{P} is waiting. On the other hand, external scheduling guarantees that while routine \code{P} is waiting, no routine other than \code{V} canacquire the monitor.740 This method is more constrained and explicit, which helps users tone down the undeterministic nature of concurrency. Indeed, as the following examples demonstrates, external scheduling allows users to wait for events from other threads without the concern of unrelated events occuring. External scheduling can generally be done either in terms of control flow (e.g., \uC with \code{_Accept}) or in terms of data (e.g. Go with channels). Of course, both of these paradigms have their own strenghts and weaknesses but for this project control-flow semantics were chosen to stay consistent with the rest of the languages semantics. Two challenges specific to \CFA arise when trying to add external scheduling with loose object definitions and multi-monitor routines. The previous example shows a simple use \code{_Accept} versus \code{wait}/\code{signal} and its advantages. Note that while other languages often use \code{accept}/\code{select} as the core external scheduling keyword, \CFA uses \code{waitfor} to prevent name collisions with existing socket \acrshort{api}s. 741 742 In the case of internal scheduling, the call to \code{wait} only guarantees that \code{V} is the last routine to access the monitor. This entails that a third routine, say \code{isInUse()}, may have acquired mutual exclusion several times while routine \code{P} was waiting. On the other hand, external scheduling guarantees that while routine \code{P} was waiting, no routine other than \code{V} could acquire the monitor. 732 743 733 744 % ====================================================================== … … 736 747 % ====================================================================== 737 748 % ====================================================================== 738 In \uC, monitor declarations include an exhaustive list of monitor operations. Since \CFA is not object oriented , monitors become both more difficult to implement and less clear for auser:749 In \uC, monitor declarations include an exhaustive list of monitor operations. Since \CFA is not object oriented it becomes both more difficult to implement but also less clear for the user: 739 750 740 751 \begin{cfacode} … … 771 782 For the first two conditions, it is easy to implement a check that can evaluate the condition in a few instruction. However, a fast check for \pscode{monitor accepts me} is much harder to implement depending on the constraints put on the monitors. Indeed, monitors are often expressed as an entry queue and some acceptor queue as in the following figure: 772 783 773 \begin{figure}[H]774 784 \begin{center} 775 785 {\resizebox{0.4\textwidth}{!}{\input{monitor}}} 776 786 \end{center} 777 \label{fig:monitor} 778 \end{figure} 779 780 There are other alternatives to these pictures, but in the case of this picture, implementing a fast accept check is relatively easy. Restricted to a fixed number of mutex members, N, the accept check reduces to updating a bitmask when the acceptor queue changes, a check that executes in a single instruction even with a fairly large number (e.g., 128) of mutex members. This technique cannot be used in \CFA because it relies on the fact that the monitor type enumerates (declares) all the acceptable routines. For OO languages this does not compromise much since monitors already have an exhaustive list of member routines. However, for \CFA this is not the case; routines can be added to a type anywhere after its declaration. It is important to note that the bitmask approach does not actually require an exhaustive list of routines, but it requires a dense unique ordering of routines with an upper-bound and that ordering must be consistent across translation units. 781 The alternative is to alter the implementeation like this: 787 788 There are other alternatives to these pictures, but in the case of this picture, implementing a fast accept check is relatively easy. Indeed simply updating a bitmask when the acceptor queue changes is enough to have a check that executes in a single instruction, even with a fairly large number (e.g. 128) of mutex members. This technique cannot be used in \CFA because it relies on the fact that the monitor type declares all the acceptable routines. For OO languages this does not compromise much since monitors already have an exhaustive list of member routines. However, for \CFA this is not the case; routines can be added to a type anywhere after its declaration. Its important to note that the bitmask approach does not actually require an exhaustive list of routines, but it requires a dense unique ordering of routines with an upper-bound and that ordering must be consistent across translation units. 789 The alternative is to have a picture like this one: 782 790 783 791 \begin{center} … … 785 793 \end{center} 786 794 787 Generating a mask dynamically means that the storage for the mask information can vary between calls to \code{waitfor}, allowing for more flexibility and extensions. Storing an array of accepted function-pointers replaces the single instruction bitmask compare with dereferencing a pointer followed by a linear search. Furthermore, supporting nested external scheduling (e.g., listing \ref{lst:nest-ext}) may now require additionnal searches on calls to \code{waitfor} statement to check if a routine is already queued in. 788 789 \begin{figure} 795 Not storing the mask inside the monitor means that the storage for the mask information can vary between calls to \code{waitfor}, allowing for more flexibility and extensions. Storing an array of function-pointers would solve the issue of uniquely identifying acceptable routines. However, the single instruction bitmask compare has been replaced by dereferencing a pointer followed by a linear search. Furthermore, supporting nested external scheduling may now require additionnal searches on calls to waitfor to check if a routine is already queued in. 796 797 Note that in the second picture, tasks need to always keep track of through which routine they are attempting to acquire the monitor and the routine mask needs to have both a function pointer and a set of monitors, as will be discussed in the next section. These details where omitted from the picture for the sake of simplifying the representation. 798 799 At this point we must make a decision between flexibility and performance. Many design decisions in \CFA achieve both flexibility and performance, for example polymorphic routines add significant flexibility but inlining them means the optimizer can easily remove any runtime cost. Here however, the cost of flexibility cannot be trivially removed. In the end, the most flexible approach has been chosen since it allows users to write programs that would otherwise be prohibitively hard to write. This decision is based on the assumption that writing fast but inflexible locks is closer to a solved problems than writing locks that are as flexible as external scheduling in \CFA. 800 801 % ====================================================================== 802 % ====================================================================== 803 \subsection{Multi-monitor scheduling} 804 % ====================================================================== 805 % ====================================================================== 806 807 External scheduling, like internal scheduling, becomes significantly more complex when introducing multi-monitor syntax. Even in the simplest possible case, some new semantics need to be established: 790 808 \begin{cfacode} 791 809 monitor M {}; 792 void foo( M & mutex a ) {}793 void bar( M & mutex b ) {794 //Nested in the waitfor(bar, c) call795 waitfor(foo, b);796 }797 void baz( M & mutex c ) {798 waitfor(bar, c);799 }800 801 \end{cfacode}802 \caption{Example of nested external scheduling}803 \label{lst:nest-ext}804 \end{figure}805 806 Note that in the second picture, tasks need to always keep track of which routine they are attempting to acquire the monitor and the routine mask needs to have both a function pointer and a set of monitors, as will be discussed in the next section. These details where omitted from the picture for the sake of simplifying the representation.807 808 At this point, a decision must be made between flexibility and performance. Many design decisions in \CFA achieve both flexibility and performance, for example polymorphic routines add significant flexibility but inlining them means the optimizer can easily remove any runtime cost. Here however, the cost of flexibility cannot be trivially removed. In the end, the most flexible approach has been chosen since it allows users to write programs that would otherwise be prohibitively hard to write. This decision is based on the assumption that writing fast but inflexible locks is closer to a solved problems than writing locks that are as flexible as external scheduling in \CFA.809 810 % ======================================================================811 % ======================================================================812 \subsection{Multi-monitor scheduling}813 % ======================================================================814 % ======================================================================815 816 External scheduling, like internal scheduling, becomes significantly more complex when introducing multi-monitor syntax. Even in the simplest possible case, some new semantics need to be established:817 \begin{cfacode}818 monitor M {};819 810 820 811 void f(M & mutex a); 821 812 822 void g(M & mutex b, M & mutex c) {823 waitfor(f); // two monitors M => unkown which to pass to f(M & mutex)813 void g(M & mutex a, M & mutex b) { 814 waitfor(f); //ambiguous, keep a pass b or other way around? 824 815 } 825 816 \end{cfacode} … … 837 828 \end{cfacode} 838 829 839 This syntax is unambiguous. Both locks are acquired and kept by \code{g}. When routine \code{f} is called, the lock for monitor \code{b} is temporarily transferred from \code{g} to \code{f} (while \code{g} still holds lock \code{a}). This behavior can be extended to multi-monitor \code{waitfor}statement as follows.830 This syntax is unambiguous. Both locks are acquired and kept. When routine \code{f} is called, the lock for monitor \code{b} is temporarily transferred from \code{g} to \code{f} (while \code{g} still holds lock \code{a}). This behavior can be extended to multi-monitor waitfor statement as follows. 840 831 841 832 \begin{cfacode} … … 851 842 Note that the set of monitors passed to the \code{waitfor} statement must be entirely contained in the set of monitors already acquired in the routine. \code{waitfor} used in any other context is Undefined Behaviour. 852 843 853 An important behavior to note is when a set of monitors only match partially :844 An important behavior to note is that what happens when a set of monitors only match partially : 854 845 855 846 \begin{cfacode} … … 874 865 \end{cfacode} 875 866 876 While the equivalent can happen when using internal scheduling, the fact that conditions are specific to a set of monitors means that users have to use two different condition variables. In both cases, partially matching monitor sets does not wake-up the waiting thread. It is also important to note that in the case of external scheduling, as for routine calls, the order of parameters is i rrelevant; \code{waitfor(f,a,b)} and \code{waitfor(f,b,a)} are indistinguishablewaiting condition.867 While the equivalent can happen when using internal scheduling, the fact that conditions are specific to a set of monitors means that users have to use two different condition variables. In both cases, partially matching monitor sets does not wake-up the waiting thread. It is also important to note that in the case of external scheduling, as for routine calls, the order of parameters is important; \code{waitfor(f,a,b)} and \code{waitfor(f,b,a)} are to distinct waiting condition. 877 868 878 869 % ====================================================================== … … 882 873 % ====================================================================== 883 874 884 Syntactically, the \code{waitfor} statement takes a function identifier and a set of monitors. While the set of monitors can be any list of expression, the function name is more restricted because the compiler validates at compile time the validity of the function type and the parameters used with the \code{waitfor}statement. It checks that the set of monitor passed in matches the requirements for a function call. Listing \ref{lst:waitfor} shows various usage of the waitfor statement and which are acceptable. The choice of the function type is made ignoring any non-\code{mutex} parameter. One limitation of the current implementation is that it does not handle overloading.875 Syntactically, the \code{waitfor} statement takes a function identifier and a set of monitors. While the set of monitors can be any list of expression, the function name is more restricted. This is because the compiler validates at compile time the validity of the waitfor statement. It checks that the set of monitor passed in matches the requirements for a function call. Listing \ref{lst:waitfor} shows various usage of the waitfor statement and which are acceptable. The choice of the function type is made ignoring any non-\code{mutex} parameter. One limitation of the current implementation is that it does not handle overloading. 885 876 \begin{figure} 886 877 \begin{cfacode} … … 907 898 waitfor(f2, a1, a2); //Incorrect : Mutex arguments don't match 908 899 waitfor(f1, 1); //Incorrect : 1 not a mutex argument 909 waitfor(f 9, a1); //Incorrect : f9 function does not exist910 waitfor(*fp, a1 ); //Incorrect : fp not a nidentifier900 waitfor(f4, a1); //Incorrect : f9 not a function 901 waitfor(*fp, a1 ); //Incorrect : fp not a identifier 911 902 waitfor(f4, a1); //Incorrect : f4 ambiguous 912 903 … … 918 909 \end{figure} 919 910 920 Finally, for added flexibility, \CFA supports constructing complex \code{waitfor} mask using the \code{or}, \code{timeout} and \code{else}. Indeed, multiple \code{waitfor} can be chained together using \code{or}; this chain forms a single statement that uses baton-pass to any one function that fits one of the function+monitor set passed in. To eanble users to tell which accepted function is accepted, \code{waitfor}s are followed by a statement (including the null statement \code{;}) or a compound statement. When multiple \code{waitfor} are chained together, only the statement corresponding to the accepted function is executed. A \code{waitfor} chain can also be followed by a \code{timeout}, to signify an upper bound on the wait, or an \code{else}, to signify that the call should be non-blocking, that is only check of a matching function callalready arrived and return immediately otherwise. Any and all of these clauses can be preceded by a \code{when} condition to dynamically construct the mask based on some current state. Listing \ref{lst:waitfor2}, demonstrates several complex masks and some incorrect ones.911 Finally, for added flexibility, \CFA supports constructing complex waitfor mask using the \code{or}, \code{timeout} and \code{else}. Indeed, multiple \code{waitfor} can be chained together using \code{or}; this chain will form a single statement which will baton-pass to any one function that fits one of the function+monitor set which was passed in. To eanble users to tell which was the accepted function, \code{waitfor}s are followed by a statement (including the null statement \code{;}) or a compound statement. When multiple \code{waitfor} are chained together, only the statement corresponding to the accepted function is executed. A \code{waitfor} chain can also be followed by a \code{timeout}, to signify an upper bound on the wait, or an \code{else}, to signify that the call should be non-blocking, that is only check of a matching function already arrived and return immediately otherwise. Any and all of these clauses can be preceded by a \code{when} condition to dynamically construct the mask based on some current state. Listing \ref{lst:waitfor2}, demonstrates several complex masks and some incorrect ones. 921 912 922 913 \begin{figure} … … 982 973 \label{lst:waitfor2} 983 974 \end{figure} 984 985 % ======================================================================986 % ======================================================================987 \subsection{Waiting for the destructor}988 % ======================================================================989 % ======================================================================990 An interesting use for the \code{waitfor} statement is destructor semantics. Indeed, the \code{waitfor} statement can accept any \code{mutex} routine, which includes the destructor (see section \ref{data}). However, with the semantics discussed until now, waiting for the destructor does not make any sense since using an object after its destructor is called is undefined behaviour. The simplest approach is to disallow \code{waitfor} on a destructor. However, a more expressive approach is to flip execution ordering when waiting for the destructor, meaning that waiting for the destructor allows the destructor to run after the current \code{mutex} routine, similarly to how a condition is signalled.991 \begin{figure}992 \begin{cfacode}993 monitor Executer {};994 struct Action;995 996 void ^?{} (Executer & mutex this);997 void execute(Executer & mutex this, const Action & );998 void run (Executer & mutex this) {999 while(true) {1000 waitfor(execute, this);1001 or waitfor(^?{} , this) {1002 break;1003 }1004 }1005 }1006 \end{cfacode}1007 \caption{Example of an executor which executes action in series until the destructor is called.}1008 \label{lst:dtor-order}1009 \end{figure}1010 For example, listing \ref{lst:dtor-order} shows an example of an executor with an infinite loop, which waits for the destructor to break out of this loop. Switching the semantic meaning introduces an idiomatic way to terminate a task and/or wait for its termination via destruction. -
doc/proposals/concurrency/text/future.tex
r0fe4e62 rf5c3b6c 5 5 % ====================================================================== 6 6 7 \section{Flexible Scheduling} \label{futur:sched} 8 An important part of concurrency is scheduling. Different scheduling algorithm can affact peformance (both in terms of average and variation). However, no single scheduler is optimal for all workloads and therefore there is value in being able to change the scheduler for given programs. One solution is to offer various tweaking options to users, allowing the scheduler to be adjusted the to requirements of the workload. However, in order to be truly flexible, it would be interesting to allow users to add arbitrary data and arbirary scheduling algorithms to the scheduler. For example, a web server could attach Type-of-Service information to threads and have a ``ToS aware'' scheduling algorithm tailored to this specific web server. This path of flexible schedulers will be explored for \CFA. 9 10 \section{Non-Blocking IO} \label{futur:nbio} 11 While most of the parallelism tools 12 However, many modern workloads are not bound on computation but on IO operations, an common case being webservers and XaaS (anything as a service). These type of workloads often require significant engineering around amortising costs of blocking IO operations. While improving throughtput of these operations is outside what \CFA can do as a language, it can help users to make better use of the CPU time otherwise spent waiting on IO operations. The current trend is to use asynchronous programming using tools like callbacks and/or futurs and promises\cite. However, while these are valid solutions, they lead to code that is harder to read and maintain because it is much less linear 13 14 \section{Other concurrency tools} \label{futur:tools} 15 While monitors offer a flexible and powerful concurent core for \CFA, other concurrency tools are also necessary for a complete multi-paradigm concurrency package. Example of such tools can include simple locks and condition variables, futures and promises\cite{promises}, and executors. These additional features are useful when monitors offer a level of abstraction which is indaquate for certain tasks. 16 17 \section{Implicit threading} \label{futur:implcit} 18 Simpler applications can benefit greatly from having implicit parallelism. That is, parallelism that does not rely on the user to write concurrency. This type of parallelism can be achieved both at the language level and at the library level. The cannonical example of implcit parallelism is parallel for loops, which are the simplest example of a divide and conquer algorithm\cite{uC++book}. Listing \ref{lst:parfor} shows three different code examples that accomplish pointwise sums of large arrays. Note that none of these example explicitly declare any concurrency or parallelism objects. 19 20 \begin{figure} 21 \begin{center} 22 \begin{tabular}[t]{|c|c|c|} 23 Sequential & Library Parallel & Language Parallel \\ 24 \begin{cfacode}[tabsize=3] 25 void big_sum( 26 int* a, int* b, 27 int* o, 28 size_t len) 29 { 30 for( 31 int i = 0; 32 i < len; 33 ++i ) 34 { 35 o[i]=a[i]+b[i]; 36 } 37 } 7 Concurrency and parallelism is still a very active field that strongly benefits from hardware advances. As such certain features that aren't necessarily mature enough in their current state could become relevant in the lifetime of \CFA. 8 \section{Non-Blocking IO} 38 9 39 10 11 \section{Other concurrency tools} 40 12 41 13 42 43 int* a[10000]; 44 int* b[10000]; 45 int* c[10000]; 46 //... fill in a & b 47 big_sum(a,b,c,10000); 48 \end{cfacode} &\begin{cfacode}[tabsize=3] 49 void big_sum( 50 int* a, int* b, 51 int* o, 52 size_t len) 53 { 54 range ar(a, a+len); 55 range br(b, b+len); 56 range or(o, o+len); 57 parfor( ai, bi, oi, 58 []( int* ai, 59 int* bi, 60 int* oi) 61 { 62 oi=ai+bi; 63 }); 64 } 14 \section{Implicit threading} 15 % Finally, simpler applications can benefit greatly from having implicit parallelism. That is, parallelism that does not rely on the user to write concurrency. This type of parallelism can be achieved both at the language level and at the system level. 16 % 17 % \begin{center} 18 % \begin{tabular}[t]{|c|c|c|} 19 % Sequential & System Parallel & Language Parallel \\ 20 % \begin{lstlisting} 21 % void big_sum(int* a, int* b, 22 % int* out, 23 % size_t length) 24 % { 25 % for(int i = 0; i < length; ++i ) { 26 % out[i] = a[i] + b[i]; 27 % } 28 % } 29 % 30 % 31 % 32 % 33 % 34 % int* a[10000]; 35 % int* b[10000]; 36 % int* c[10000]; 37 % //... fill in a and b ... 38 % big_sum(a, b, c, 10000); 39 % \end{lstlisting} &\begin{lstlisting} 40 % void big_sum(int* a, int* b, 41 % int* out, 42 % size_t length) 43 % { 44 % range ar(a, a + length); 45 % range br(b, b + length); 46 % range or(out, out + length); 47 % parfor( ai, bi, oi, 48 % [](int* ai, int* bi, int* oi) { 49 % oi = ai + bi; 50 % }); 51 % } 52 % 53 % int* a[10000]; 54 % int* b[10000]; 55 % int* c[10000]; 56 % //... fill in a and b ... 57 % big_sum(a, b, c, 10000); 58 % \end{lstlisting}&\begin{lstlisting} 59 % void big_sum(int* a, int* b, 60 % int* out, 61 % size_t length) 62 % { 63 % for (ai, bi, oi) in (a, b, out) { 64 % oi = ai + bi; 65 % } 66 % } 67 % 68 % 69 % 70 % 71 % 72 % int* a[10000]; 73 % int* b[10000]; 74 % int* c[10000]; 75 % //... fill in a and b ... 76 % big_sum(a, b, c, 10000); 77 % \end{lstlisting} 78 % \end{tabular} 79 % \end{center} 80 % 65 81 66 82 67 int* a[10000]; 68 int* b[10000]; 69 int* c[10000]; 70 //... fill in a & b 71 big_sum(a,b,c,10000); 72 \end{cfacode}&\begin{cfacode}[tabsize=3] 73 void big_sum( 74 int* a, int* b, 75 int* o, 76 size_t len) 77 { 78 parfor (ai,bi,oi) 79 in (a, b, o ) 80 { 81 oi = ai + bi; 82 } 83 } 83 \section{Multiple Paradigms} 84 84 85 85 86 87 88 89 90 91 int* a[10000]; 92 int* b[10000]; 93 int* c[10000]; 94 //... fill in a & b 95 big_sum(a,b,c,10000); 96 \end{cfacode} 97 \end{tabular} 98 \end{center} 99 \caption{For loop to sum numbers: Sequential, using library parallelism and language parallelism.} 100 \label{lst:parfor} 101 \end{figure} 102 103 Implicit parallelism is a general solution and therefore has its limitations. However, it is a quick and simple approach to parallelism which may very well be sufficient for smaller applications and reduces the amount of boiler-plate that is needed to start benefiting from parallelism in modern CPUs. 104 105 86 \section{Transactions} -
doc/proposals/concurrency/text/internals.tex
r0fe4e62 rf5c3b6c 1 1 2 2 \chapter{Behind the scene} 3 There are several challenges specific to \CFA when implementing concurrency. These challenges are a direct result of \gls{bulk-acq} and loose object-definitions. These two constraints are the root cause of most design decisions in the implementation. Furthermore, to avoid contention from dynamically allocating memory in a concurrent environment, the internal-scheduling design is (almost) entirely free of mallocs. This is to avoid the chicken and egg problem \cite{Chicken} of having a memory allocator that relies on the threading system and a threading system that relies on the runtime. This extra goal, means that memory management is a constant concern in the design of the system.4 3 5 The main memory concern for concurrency is queues. All blocking operations are made by parking threads onto queues. The queue design needs to be intrusive\cite{IntrusiveData} to avoid the need for memory allocation, which entails that all the nodes need specific fields to keep track of all needed information. Since many concurrency operations can use an unbound amount of memory (depending on \gls{bulk-acq}), statically defining information in the intrusive fields of threads is insufficient. The only variable sized container that does not require memory allocation is the callstack, which is heavily used in the implementation of internal scheduling. Particularly variable length arrays, which are used extensively.6 7 Since stack allocation is based around scope, the first step of the implementation is to identify the scopes that are available to store the information, and which of these can have a variable length. The threads and the condition both allow a fixed amount of memory to be stored, while mutex-routines and the actual blocking call allow for an unbound amount (though the later is preferable in terms of performance).8 9 Note that since the major contributions of this thesis are extending monitor semantics to \gls{bulk-acq} and loose object definitions, any challenges that are not resulting of these characteristiques of \CFA are considered as solved problems and therefore not discussed further.10 4 11 5 % ====================================================================== 12 6 % ====================================================================== 13 \section{ Mutex routines}7 \section{Implementation Details: Interaction with polymorphism} 14 8 % ====================================================================== 15 9 % ====================================================================== 10 Depending on the choice of semantics for when monitor locks are acquired, interaction between monitors and \CFA's concept of polymorphism can be complex to support. However, it is shown that entry-point locking solves most of the issues. 16 11 17 The first step towards the monitor implementation is simple mutex-routines using monitors. In the single monitor case, this is done using the entry/exit procedure highlighted in listing \ref{lst:entry1}. This entry/exit procedure does not actually have to be extended to support multiple monitors, indeed it is sufficient to enter/leave monitors one-by-one as long as the order is correct to prevent deadlocks\cite{Havender68}. In \CFA, ordering of monitor relies on memory ordering, this is sufficient because all objects are guaranteed to have distinct non-overlaping memory layouts and mutual-exclusion for a monitor is only defined for its lifetime, meaning that destroying a monitor while it is acquired is undefined behavior. When a mutex call is made, the concerned monitors are agregated into a variable-length pointer array and sorted based on pointer values. This array presists for the entire duration of the mutual-exclusion and its ordering reused extensively. 12 First of all, interaction between \code{otype} polymorphism and monitors is impossible since monitors do not support copying. Therefore, the main question is how to support \code{dtype} polymorphism. Since a monitor's main purpose is to ensure mutual exclusion when accessing shared data, this implies that mutual exclusion is only required for routines that do in fact access shared data. However, since \code{dtype} polymorphism always handles incomplete types (by definition), no \code{dtype} polymorphic routine can access shared data since the data requires knowledge about the type. Therefore, the only concern when combining \code{dtype} polymorphism and monitors is to protect access to routines. 13 14 Before looking into complex control-flow, it is important to present the difference between the two acquiring options : callsite and entry-point locking, i.e. acquiring the monitors before making a mutex routine call or as the first operation of the mutex routine-call. For example: 18 15 \begin{figure} 19 \begin{multicols}{2} 20 Entry 21 \begin{pseudo} 22 if monitor is free 23 enter 24 elif already own the monitor 25 continue 26 else 27 block 28 increment recursions 29 \end{pseudo} 30 \columnbreak 31 Exit 32 \begin{pseudo} 33 decrement recursion 34 if recursion == 0 35 if entry queue not empty 36 wake-up thread 37 \end{pseudo} 38 \end{multicols} 39 \caption{Initial entry and exit routine for monitors} 40 \label{lst:entry1} 41 \end{figure} 42 43 \subsection{ Details: Interaction with polymorphism} 44 Depending on the choice of semantics for when monitor locks are acquired, interaction between monitors and \CFA's concept of polymorphism can be more complex to support. However, it is shown that entry-point locking solves most of the issues. 45 46 First of all, interaction between \code{otype} polymorphism and monitors is impossible since monitors do not support copying. Therefore, the main question is how to support \code{dtype} polymorphism. It is important to present the difference between the two acquiring options : callsite and entry-point locking, i.e. acquiring the monitors before making a mutex routine call or as the first operation of the mutex routine-call. For example: 47 \begin{figure}[H] 16 \label{fig:locking-site} 48 17 \begin{center} 18 \setlength\tabcolsep{1.5pt} 49 19 \begin{tabular}{|c|c|c|} 50 20 Mutex & \gls{callsite-locking} & \gls{entry-point-locking} \\ … … 96 66 \end{tabular} 97 67 \end{center} 98 \caption{Call-site vs entry-point locking for mutex calls} 99 \label{fig:locking-site} 68 \caption{Callsite vs entry-point locking for mutex calls} 100 69 \end{figure} 101 70 102 Note the \code{mutex} keyword relies on the type system, which means that in cases where a generic monitor routine is desired, writing the mutex routine is possible with the proper trait, for example: 71 72 Note the \code{mutex} keyword relies on the type system, which means that in cases where a generic monitor routine is actually desired, writing a mutex routine is possible with the proper trait, which is possible because monitors are designed in terms a trait. For example: 103 73 \begin{cfacode} 104 //Incorrect: T may not bemonitor74 //Incorrect: T is not a monitor 105 75 forall(dtype T) 106 76 void foo(T * mutex t); … … 111 81 \end{cfacode} 112 82 113 Both entry-point and callsite locking are feasible implementations. The current \CFA implementations uses entry-point locking because it requires less work when using \gls{raii}, effectively transferring the burden of implementation to object construction/destruction. The same could be said of callsite locking, the difference being that the later does not necessarily have an existing scope that matches exactly the scope of the mutual exclusion, i.e.: the function body. Furthermore, entry-point locking requires less code generation since any useful routine is called at least as often as it is define, there can be only one entry-point but many callsites.114 83 115 84 % ====================================================================== 116 85 % ====================================================================== 117 \section{ Threading} \label{impl:thread}86 \section{Internal scheduling: Implementation} \label{inschedimpl} 118 87 % ====================================================================== 119 88 % ====================================================================== 89 There are several challenges specific to \CFA when implementing internal scheduling. These challenges are direct results of \gls{bulk-acq} and loose object definitions. These two constraints are to root cause of most design decisions in the implementation of internal scheduling. Furthermore, to avoid the head-aches of dynamically allocating memory in a concurrent environment, the internal-scheduling design is entirely free of mallocs and other dynamic memory allocation scheme. This is to avoid the chicken and egg problem \cite{Chicken} of having a memory allocator that relies on the threading system and a threading system that relies on the runtime. This extra goal, means that memory management is a constant concern in the design of the system. 120 90 121 Figure \ref{fig:system1} shows a high-level picture if the \CFA runtime system in regards to concurrency. Each component of the picture is explained in details in the fllowing sections.91 The main memory concern for concurrency is queues. All blocking operations are made by parking threads onto queues. These queues need to be intrinsic\cit to avoid the need memory allocation. This entails that all the fields needed to keep track of all needed information. Since internal scheduling can use an unbound amount of memory (depending on \gls{bulk-acq}) statically defining information information in the intrusive fields of threads is insufficient. The only variable sized container that does not require memory allocation is the callstack, which is heavily used in the implementation of internal scheduling. Particularly the GCC extension variable length arrays which is used extensively. 122 92 123 \begin{figure} 124 \begin{center} 125 {\resizebox{\textwidth}{!}{\input{system.pstex_t}}} 126 \end{center} 127 \caption{Overview of the entire system} 128 \label{fig:system1} 129 \end{figure} 93 Since stack allocation is based around scope, the first step of the implementation is to identify the scopes that are available to store the information, and which of these can have a variable length. In the case of external scheduling, the threads and the condition both allow a fixed amount of memory to be stored, while mutex-routines and the actual blocking call allow for an unbound amount (though adding too much to the mutex routine stack size can become expansive faster). 130 94 131 \subsection{Context Switching} 132 As mentionned in section \ref{coroutine}, coroutines are a stepping stone for implementing threading. This is because they share the same mechanism for context-switching between different stacks. To improve performance and simplicity, context-switching is implemented using the following assumption: all context-switches happen inside a specific function call. This assumption means that the context-switch only has to copy the callee-saved registers onto the stack and then switch the stack registers with the ones of the target coroutine/thread. Note that the instruction pointer can be left untouched since the context-switch is always inside the same function. Threads however do not context-switch between each other directly. They context-switch to the scheduler. This method is called a 2-step context-switch and has the advantage of having a clear distinction between user code and the kernel where scheduling and other system operation happen. Obiously, this has the cost of doubling the context-switch cost because threads must context-switch to an intermediate stack. However, the performance of the 2-step context-switch is still superior to a \code{pthread_yield}(see section \ref{results}). additionally, for users in need for optimal performance, it is important to note that having a 2-step context-switch as the default does not prevent \CFA from offering a 1-step context-switch to use manually (or as part of monitors). This option is not currently present in \CFA but the changes required to add it are strictly additive. 95 The following figure is the traditionnal illustration of a monitor : 133 96 134 \subsection{Processors}135 Parallelism in \CFA is built around using processors to specify how much parallelism is desired. \CFA processors are object wrappers around kernel threads, specifically pthreads in the current implementation of \CFA. Indeed, any parallelism must go through operating-system librairies. However, \glspl{uthread} are still the main source of concurrency, processors are simply the underlying source of parallelism. Indeed, processor \glspl{kthread} simply fetch a \glspl{uthread} from the scheduler and run, they are effectively executers for user-threads. The main benefit of this approach is that it offers a well defined boundary between kernel code and user code, for example, kernel thread quiescing, scheduling and interrupt handling. Processors internally use coroutines to take advantage of the existing context-switching semantics.136 137 \subsection{Stack management}138 One of the challenges of this system is to reduce the footprint as much as possible. Specifically, all pthreads created also have a stack created with them, which should be used as much as possible. Normally, coroutines also create there own stack to run on, however, in the case of the coroutines used for processors, these coroutines run directly on the kernel thread stack, effectively stealing the processor stack. The exception to this rule is the Main Processor, i.e. the initial kernel thread that is given to any program. In order to respect user expectations, the stack of the initial kernel thread, the main stack of the program, is used by the main user thread rather than the main processor.139 140 \subsection{Preemption} \label{preemption}141 Finally, an important aspect for any complete threading system is preemption. As mentionned in chapter \ref{basics}, preemption introduces an extra degree of uncertainty, which enables users to have multiple threads interleave transparently, rather than having to cooperate among threads for proper scheduling and CPU distribution. Indeed, preemption is desireable because it adds a degree of isolation among threads. In a fully cooperative system, any thread that runs into a long loop can starve other threads, while in a preemptive system starvation can still occur but it does not rely on every thread having to yield or block on a regular basis, which reduces significantly a programmer burden. Obviously, preemption is not optimal for every workload, however any preemptive system can become a cooperative system by making the time-slices extremely large. Which is why \CFA uses a preemptive threading system.142 143 Preemption in \CFA is based on kernel timers, which are used to run a discrete-event simulation. Every processor keeps track of the current time and registers an expiration time with the preemption system. When the preemption system receives a change in preemption, it sorts these expiration times in a list and sets a kernel timer for the closest one, effectively stepping between preemption events on each signals sent by the timer. These timers use the linux signal {\tt SIGALRM}, which is delivered to the process rather than the kernel-thread. This results in an implementation problem,because when delivering signals to a process, the kernel documentation states that the signal can be delivered to any kernel thread for which the signal is not blocked i.e. :144 \begin{quote}145 A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.146 SIGNAL(7) - Linux Programmer's Manual147 \end{quote}148 For the sake of simplicity and in order to prevent the case of having two threads receiving alarms simultaneously, \CFA programs block the {\tt SIGALRM} signal on every thread except one. Now because of how involontary context-switches are handled, the kernel thread handling {\tt SIGALRM} cannot also be a processor thread.149 150 Involuntary context-switching is done by sending signal {\tt SIGUSER1} to the corresponding processor and having the thread yield from inside the signal handler. Effectively context-switching away from the signal-handler back to the kernel and the signal-handler frame is eventually unwound when the thread is scheduled again. This approach means that a signal-handler can start on one kernel thread and terminate on a second kernel thread (but the same user thread). It is important to note that signal-handlers save and restore signal masks because user-thread migration can cause signal mask to migrate from one kernel thread to another. This behaviour is only a problem if all kernel threads among which a user thread can migrate differ in terms of signal masks\footnote{Sadly, official POSIX documentation is silent on what distiguishes ``async-signal-safe'' functions from other functions}. However, since the kernel thread hanlding preemption requires a different signal mask, executing user threads on the kernel alarm thread can cause deadlocks. For this reason, the alarm thread is on a tight loop around a system call to \code{sigwaitinfo}, requiring very little CPU time for preemption. One final detail about the alarm thread is how to wake it when additional communication is required (e.g., on thread termination). This unblocking is also done using {\tt SIGALRM}, but sent throught the \code{pthread_sigqueue}. Indeed, \code{sigwait} can differentiate signals sent from \code{pthread_sigqueue} from signals sent from alarms or the kernel.151 152 \subsection{Scheduler}153 Finally, an aspect that was not mentionned yet is the scheduling algorithm. Currently, the \CFA scheduler uses a single ready queue for all processors, which is the simplest approach to scheduling. Further discussion on scheduling is present in section \label{futur:sched}.154 155 % ======================================================================156 % ======================================================================157 \section{Internal scheduling} \label{impl:intsched}158 % ======================================================================159 % ======================================================================160 The following figure is the traditional illustration of a monitor (repeated from page~\pageref{fig:monitor} for convenience) :161 162 \begin{figure}[H]163 97 \begin{center} 164 98 {\resizebox{0.4\textwidth}{!}{\input{monitor}}} 165 99 \end{center} 166 \caption{Traditional illustration of a monitor}167 \label{fig:monitor}168 \end{figure}169 100 170 This picture has several components, the two most important being the entry-queue and the AS-stack. The entry-queue is an (almost) FIFO list where threads waiting to enter are parked, while the acceptor-signalor (AS) stack is a FILO list used for threads that have been signalled or otherwise marked as running next. 101 For \CFA, the previous picture does not have support for blocking multiple monitors on a single condition. To support \gls{bulk-acq} two changes to this picture are required. First, it doesn't make sense to tie the condition to a single monitor since blocking two monitors as one would require arbitrarily picking a monitor to hold the condition. Secondly, the object waiting on the conditions and AS-stack cannot simply contain the waiting thread since a single thread can potentially wait on multiple monitors. As mentionned in section \ref{inschedimpl}, the handling in multiple monitors is done by partially passing, which entails that each concerned monitor needs to have a node object. However, for waiting on the condition, since all threads need to wait together, a single object needs to be queued in the condition. Moving out the condition and updating the node types yields : 171 102 172 For \CFA, this picture does not have support for blocking multiple monitors on a single condition. To support \gls{bulk-acq} two changes to this picture are required. First, it is non longer helpful to attach the condition to a single monitor. Secondly, the thread waiting on the conditions has to be seperated multiple monitors, which yields :173 174 \begin{figure}[H]175 103 \begin{center} 176 104 {\resizebox{0.8\textwidth}{!}{\input{int_monitor}}} 177 105 \end{center} 178 \caption{Illustration of \CFA monitor}179 \label{fig:monitor_cfa}180 \end{figure}181 106 182 This picture and the proper entry and leave algorithms is the fundamental implementation of internal scheduling (see listing \ref{lst:entry2}). Note that when threads are moved from the condition to the AS-stack, it splits the thread into to pieces. The thread is woken up when all the pieces have moved from the AS-stacks to the active thread seat. In this picture, the threads are split into halves but this is only because there are two monitors in this picture. For a specific signaling operation every monitor needs a piece of thread on its AS-stack. 107 \newpage 183 108 184 \begin{figure}[b] 109 This picture and the proper entry and leave algorithms is the fundamental implementation of internal scheduling. 110 185 111 \begin{multicols}{2} 186 112 Entry 187 \begin{pseudo} 113 \begin{pseudo}[numbers=left] 188 114 if monitor is free 189 115 enter 190 elif already own the monitor116 elif I already own the monitor 191 117 continue 192 118 else … … 197 123 \columnbreak 198 124 Exit 199 \begin{pseudo} 125 \begin{pseudo}[numbers=left, firstnumber=8] 200 126 decrement recursion 201 127 if recursion == 0 … … 209 135 \end{pseudo} 210 136 \end{multicols} 211 \caption{Entry and exit routine for monitors with internal scheduling}212 \label{lst:entry2}213 \end{figure}214 137 215 Some important things to notice about the exit routine. The solution discussed in \ref{intsched} can be seen in the exit routine of listing \ref{lst:entry2}. Basically, the solution boils down to having a seperate data structure for the condition queue and the AS-stack, and unconditionally transferring ownership of the monitors but only unblocking the thread when the last monitor has transferred ownership. This solution is deadlock safe as well as preventing any potential barging. The data structure used for the AS-stack are reused extensively for external scheduling, but in the case of internal scheduling, the data is allocated using variable-length arrays on the callstack of the \code{wait} and \code{signal_block} routines. 216 217 \begin{figure}[H] 218 \begin{center} 219 {\resizebox{0.8\textwidth}{!}{\input{monitor_structs.pstex_t}}} 220 \end{center} 221 \caption{Data structures involved in internal/external scheduling} 222 \label{fig:structs} 223 \end{figure} 224 225 Figure \ref{fig:structs} shows a high level representation of these data-structures. The main idea behind them is that, while figure \ref{fig:monitor_cfa} is a nice illustration in theory, in practice breaking a threads into multiple pieces to put unto intrusive stacks does not make sense. The \code{condition node} is the data structure that is queued into a condition variable and, when signaled, the condition queue is popped and each \code{condition criterion} are moved to the AS-stack. Once all the criterion have be popped from their respective AS-stacks, the thread is woken-up, which is what is shown in listing \ref{lst:entry2}. 138 Some important things to notice about the exit routine. The solution discussed in \ref{inschedimpl} can be seen on line 11 of the previous pseudo code. Basically, the solution boils down to having a seperate data structure for the condition queue and the AS-stack, and unconditionally transferring ownership of the monitors but only unblocking the thread when the last monitor has trasnferred ownership. This solution is safe as well as preventing any potential barging. 226 139 227 140 % ====================================================================== 228 141 % ====================================================================== 229 \section{ External scheduling}142 \section{Implementation Details: External scheduling queues} 230 143 % ====================================================================== 231 144 % ====================================================================== 232 Similarly to internal scheduling, external scheduling for multiple monitors relies on the idea that waiting-thread queues are no longer specific to a single monitor, as mentionned in section \ref{extsched}. For internal scheduling, these queues are part of condition variables which are still unique for a given scheduling operation (e.g., no single statment uses multiple conditions). However, in the case of external scheduling, there is no equivalent object which is associated with \code{waitfor} statements. This absence means the queues holding the waiting threads must be stored inside at least one of the monitors that is acquired. The monitors being the only objects that have sufficient lifetime and are available on both sides of the \code{waitfor} statment. This requires an algorithm to choose which monitor holds the relevant queue. It is also important that said algorithm be independent of the order in which users list parameters. The proposed algorithm is to fall back on monitor lock ordering and specify that the monitor that is acquired first is the one with the relevant wainting queue. This assumes that the lock acquiring order is static for the lifetime of all concerned objects but that is a reasonable constraint.145 To support multi-monitor external scheduling means that some kind of entry-queues must be used that is aware of both monitors. However, acceptable routines must be aware of the entry queues which means they must be stored inside at least one of the monitors that will be acquired. This in turn adds the requirement a systematic algorithm of disambiguating which queue is relavant regardless of user ordering. The proposed algorithm is to fall back on monitors lock ordering and specify that the monitor that is acquired first is the lock with the relevant entry queue. This assumes that the lock acquiring order is static for the lifetime of all concerned objects but that is a reasonable constraint. This algorithm choice has two consequences, the entry queue of the highest priority monitor is no longer a true FIFO queue and the queue of the lowest priority monitor is both required and probably unused. The queue can no longer be a FIFO queue because instead of simply containing the waiting threads in order arrival, they also contain the second mutex. Therefore, another thread with the same highest priority monitor but a different lowest priority monitor may arrive first but enter the critical section after a thread with the correct pairing. Secondly, since it may not be known at compile time which monitor will be the lowest priority monitor, every monitor needs to have the correct queues even though it is probable that half the multi-monitor queues will go unused for the entire duration of the program. 233 146 234 This algorithm choice has two consequences :235 \begin{itemize}236 \item The queue of the highest priority monitor is no longer a true FIFO queue because threads can be moved to the front of the queue. These queues need to contain a set of monitors for each of the waiting threads. Therefore, another thread whose set contains the same highest priority monitor but different lower priority monitors may arrive first but enter the critical section after a thread with the correct pairing.237 \item The queue of the lowest priority monitor is both required and potentially unused. Indeed, since it is not known at compile time which monitor will be the lowest priority monitor, every monitor needs to have the correct queues even though it is possible that some queues will go unused for the entire duration of the program, for example if a monitor is only used in a specific pair.238 \end{itemize}239 147 240 Therefore, the following modifications need to be made to support external scheduling : 241 \begin{itemize} 242 \item The threads waiting on the entry-queue need to keep track of which routine is trying to enter, and using which set of monitors. The \code{mutex} routine already has all the required information on its stack so the thread only needs to keep a pointer to that information. 243 \item The monitors need to keep a mask of acceptable routines. This mask contains for each acceptable routine, a routine pointer and an array of monitors to go with it. It also needs storage to keep track of which routine was accepted. Since this information is not specific to any monitor, the monitors actually contain a pointer to an integer on the stack of the waiting thread. Note that the complete mask can be pushed to any owned monitors, regardless of \code{when} statements, the \code{waitfor} statement is used in a context where the thread already has full ownership of (at least) every concerned monitor and therefore monitors will refuse all calls no matter what. 244 \item The entry/exit routine need to be updated as shown in listing \ref{lst:entry3}. 245 \end{itemize} 246 247 \subsection{External scheduling - destructors} 248 Finally, to support the ordering inversion of destructors, the code generation needs to be modified to use a special entry routine. This routine is needed because of the storage requirements of the call order inversion. Indeed, when waiting for the destructors, storage is need for the waiting context and the lifetime of said storage needs to outlive the waiting operation it is needed for. For regular \code{waitfor} statements, the callstack of the routine itself matches this requirement but it is no longer the case when waiting for the destructor since it is pushed on to the AS-stack for later. The waitfor semantics can then be adjusted correspondingly, as seen in listing \ref{lst:entry-dtor} 249 250 \begin{figure} 251 \begin{multicols}{2} 252 Entry 253 \begin{pseudo} 254 if monitor is free 255 enter 256 elif already own the monitor 257 continue 258 elif matches waitfor mask 259 push criterions to AS-stack 260 continue 261 else 262 block 263 increment recursion 264 \end{pseudo} 265 \columnbreak 266 Exit 267 \begin{pseudo} 268 decrement recursion 269 if recursion == 0 270 if signal_stack not empty 271 set_owner to thread 272 if all monitors ready 273 wake-up thread 274 endif 275 endif 276 277 if entry queue not empty 278 wake-up thread 279 endif 280 \end{pseudo} 281 \end{multicols} 282 \caption{Entry and exit routine for monitors with internal scheduling and external scheduling} 283 \label{lst:entry3} 284 \end{figure} 285 286 \begin{figure} 287 \begin{multicols}{2} 288 Destructor Entry 289 \begin{pseudo} 290 if monitor is free 291 enter 292 elif already own the monitor 293 increment recursion 294 return 295 create wait context 296 if matches waitfor mask 297 reset mask 298 push self to AS-stack 299 baton pass 300 else 301 wait 302 increment recursion 303 \end{pseudo} 304 \columnbreak 305 Waitfor 306 \begin{pseudo} 307 if matching thread is already there 308 if found destructor 309 push destructor to AS-stack 310 unlock all monitors 311 else 312 push self to AS-stack 313 baton pass 314 endif 315 return 316 endif 317 if non-blocking 318 Unlock all monitors 319 Return 320 endif 321 322 push self to AS-stack 323 set waitfor mask 324 block 325 return 326 \end{pseudo} 327 \end{multicols} 328 \caption{Pseudo code for the \code{waitfor} routine and the \code{mutex} entry routine for destructors} 329 \label{lst:entry-dtor} 330 \end{figure} 148 \section{Internals} 149 The complete mask can be pushed to any one, we are in a context where we already have full ownership of (at least) every concerned monitor and therefore monitors will refuse all calls no matter what. -
doc/proposals/concurrency/text/intro.tex
r0fe4e62 rf5c3b6c 3 3 % ====================================================================== 4 4 5 This thesis provides a minimal concurrency \acrshort{api} that is simple, efficient and can be reused to build higher-level features. The simplest possible concurrency system is a thread and a lock but this low-level approach is hard to master. An easier approach for users is to support higher-level constructs as the basis of concurrency. Indeed, for highly productive concurrent programming, high-level approaches are much more popular~\cite{HPP:Study}. Examples are task based, message passing and implicit threading. The high-level approach and its minimal \acrshort{api} are tested in a dialect of C, call \CFA. Furthermore, the proposed \acrshort{api} doubles as an early definition of the \CFA language and library. This thesis also comes with an implementation of the concurrency library for \CFA as well as all the required language features added to the source-to-source translator.5 This thesis provides a minimal concurrency \acrshort{api} that is simple, efficient and can be reused to build higher-level features. The simplest possible concurrency system is a thread and a lock but this low-level approach is hard to master. An easier approach for users is to support higher-level constructs as the basis of concurrency. Indeed, for highly productive concurrent programming, high-level approaches are much more popular~\cite{HPP:Study}. Examples are task based, message passing and implicit threading. The high-level approach and its minimal \acrshort{api} are tested in a dialect of C, call \CFA. [Is there value to say that this thesis is also an early definition of the \CFA language and library in regards to concurrency?] 6 6 7 7 There are actually two problems that need to be solved in the design of concurrency for a programming language: which concurrency and which parallelism tools are available to the programmer. While these two concepts are often combined, they are in fact distinct, requiring different tools~\cite{Buhr05a}. Concurrency tools need to handle mutual exclusion and synchronization, while parallelism tools are about performance, cost and resource utilization. -
doc/proposals/concurrency/text/parallelism.tex
r0fe4e62 rf5c3b6c 15 15 Examples of languages that support \glspl{uthread} are Erlang~\cite{Erlang} and \uC~\cite{uC++book}. 16 16 17 \subsection{Fibers : user-level threads without preemption} \label{fibers}18 A popular varient of \glspl{uthread} is what is often refered to as \glspl{fiber}. However, \glspl{fiber} do not present meaningful semantical differences with \glspl{uthread}. The significant difference between \glspl{uthread} and \glspl{fiber} is the lack of \gls{preemption} in the later one. Advocates of \glspl{fiber} list their high performance and ease of implementation as majors strenghts of \glspl{fiber} but the performance difference between \glspl{uthread} and \glspl{fiber} is controversial, and the ease of implementation, while true, is a weak argument in the context of language design. Therefore this proposal largely ignoresfibers.17 \subsection{Fibers : user-level threads without preemption} 18 A popular varient of \glspl{uthread} is what is often refered to as \glspl{fiber}. However, \glspl{fiber} do not present meaningful semantical differences with \glspl{uthread}. Advocates of \glspl{fiber} list their high performance and ease of implementation as majors strenghts of \glspl{fiber} but the performance difference between \glspl{uthread} and \glspl{fiber} is controversial, and the ease of implementation, while true, is a weak argument in the context of language design. Therefore this proposal largely ignore fibers. 19 19 20 20 An example of a language that uses fibers is Go~\cite{Go} … … 26 26 27 27 \subsection{Paradigm performance} 28 While the choice between the three paradigms listed above may have significant performance implication, it is difficult to pindown the performance implications of chosing a model at the language level. Indeed, in many situations one of these paradigms may show better performance but it all strongly depends on the workload. Having a large amount of mostly independent units of work to execute almost guarantess that the \gls{pool} based system has the best performance thanks to the lower memory overhead (i.e., no thread stack per job). However, interactions among jobs can easily exacerbate contention. User-level threads allow fine-grain context switching, which results in better resource utilisation, but a context switch is more expensive and the extra control means users need to tweak more variables to get the desired performance. Finally, if the units of uninterrupted work are large enough the paradigm choice is largely amortised by the actual work done. 28 While the choice between the three paradigms listed above may have significant performance implication, it is difficult to pindown the performance implications of chosing a model at the language level. Indeed, in many situations one of these paradigms may show better performance but it all strongly depends on the workload. Having a large amount of mostly independent units of work to execute almost guarantess that the \gls{pool} based system has the best performance thanks to the lower memory overhead (i.e., not thread stack per job). However, interactions among jobs can easily exacerbate contention. User-level threads allow fine-grain context switching, which results in better resource utilisation, but a context switch is more expensive and the extra control means users need to tweak more variables to get the desired performance. Finally, if the units of uninterrupted work are large enough the paradigm choice is largely amortised by the actual work done. 29 30 \TODO 29 31 30 32 \section{The \protect\CFA\ Kernel : Processors, Clusters and Threads}\label{kernel} 31 33 32 \Glspl{cfacluster} have not been fully implmented in the context of this thesis, currently \CFA only supports one \gls{cfacluster}, the initial one. The objective of \gls{cfacluster} is to group \gls{kthread} with identical settings together. \Glspl{uthread} can be scheduled on a \glspl{kthread} of a given \gls{cfacluster}, allowing organization between \glspl{kthread} and \glspl{uthread}. It is important that \glspl{kthread} belonging to a same \glspl{cfacluster} have homogenous settings, otherwise migrating a \gls{uthread} from one \gls{kthread} to the other can cause issues.33 34 34 35 \subsection{Future Work: Machine setup}\label{machine} 35 While this was not done in the context of this thesis, another important aspect of clusters is affinity. While many common desktop and laptop PCs have homogeneous CPUs, other devices often have more heteregenous setups. For example, system using \acrshort{numa} configurations may benefit from users being able to tie clusters and \/or kernel threads to certains CPU cores. OS support for CPU affinity is now common \cite{affinityLinux, affinityWindows, affinityFreebsd, affinityNetbsd, affinityMacosx}which means it is both possible and desirable for \CFA to offer an abstraction mechanism for portable CPU affinity.36 While this was not done in the context of this thesis, another important aspect of clusters is affinity. While many common desktop and laptop PCs have homogeneous CPUs, other devices often have more heteregenous setups. For example, system using \acrshort{numa} configurations may benefit from users being able to tie clusters and/or kernel threads to certains CPU cores. OS support for CPU affinity is now common \cit, which means it is both possible and desirable for \CFA to offer an abstraction mechanism for portable CPU affinity. 36 37 37 %\subsection{Paradigms}\label{cfaparadigms}38 % Given these building blocks, it is possible to reproduce all three of the popular paradigms. Indeed, \glspl{uthread} is the default paradigm in \CFA. However, disabling \gls{preemption} on the \gls{cfacluster} means \glspl{cfathread} effectively become \glspl{fiber}. Since several \glspl{cfacluster} with different scheduling policy can coexist in the same application, this allows \glspl{fiber} and \glspl{uthread} to coexist in the runtime of an application. Finally, it is possible to build executors for thread pools from \glspl{uthread} or \glspl{fiber}.38 \subsection{Paradigms}\label{cfaparadigms} 39 Given these building blocks, it is possible to reproduce all three of the popular paradigms. Indeed, \glspl{uthread} is the default paradigm in \CFA. However, disabling \glspl{preemption} on the \gls{cfacluster} means \glspl{cfathread} effectively become \glspl{fiber}. Since several \glspl{cfacluster} with different scheduling policy can coexist in the same application, this allows \glspl{fiber} and \glspl{uthread} to coexist in the runtime of an application. Finally, it is possible to build executors for thread pools from \glspl{uthread} or \glspl{fiber}. -
doc/proposals/concurrency/text/together.tex
r0fe4e62 rf5c3b6c 7 7 8 8 \section{Threads as monitors} 9 As it was subtely alluded in section \ref{threads}, \code{threads} in \CFA are in fact monitors , which means that all monitorfeatures are available when using threads. For example, here is a very simple two thread pipeline that could be used for a simulator of a game engine :9 As it was subtely alluded in section \ref{threads}, \code{threads} in \CFA are in fact monitors. This means that all the monitors features are available when using threads. For example, here is a very simple two thread pipeline that could be used for a simulator of a game engine : 10 10 \begin{cfacode} 11 11 // Visualization declaration … … 36 36 } 37 37 \end{cfacode} 38 One of the obvious complaints of the previous code snippet (other than its toy-like simplicity) is that it does not handle exit conditions and just goes on for ever. Luckily, the monitor semantics can also be used to clearly enforce a shutdown order in a concise manner :38 One of the obvious complaints of the previous code snippet (other than its toy-like simplicity) is that it does not handle exit conditions and just goes on for ever. Luckily, the monitor semantics can also be used to clearly enforce a shutdown order in a concise manner : 39 39 \begin{cfacode} 40 40 // Visualization declaration … … 72 72 } 73 73 } 74 75 // Call destructor for simulator once simulator finishes76 // Call destructor for renderer to signify shutdown77 74 \end{cfacode} 78 75 79 76 \section{Fibers \& Threads} 80 As mentionned in section \ref{preemption}, \CFA uses preemptive threads by default but can use fibers on demand. Currently, using fibers is done by adding the following line of code to the program~:81 \begin{cfacode}82 unsigned int default_preemption() {83 return 0;84 }85 \end{cfacode}86 This function is called by the kernel to fetch the default preemption rate, where 0 signifies an infinite time-slice i.e. no preemption. However, once clusters are fully implemented, it will be possible to create fibers and uthreads in on the same system :87 \begin{figure}88 \begin{cfacode}89 //Cluster forward declaration90 struct cluster;91 92 //Processor forward declaration93 struct processor;94 95 //Construct clusters with a preemption rate96 void ?{}(cluster& this, unsigned int rate);97 //Construct processor and add it to cluster98 void ?{}(processor& this, cluster& cluster);99 //Construct thread and schedule it on cluster100 void ?{}(thread& this, cluster& cluster);101 102 //Declare two clusters103 cluster thread_cluster = { 10`ms }; //Preempt every 10 ms104 cluster fibers_cluster = { 0 }; //Never preempt105 106 //Construct 4 processors107 processor processors[4] = {108 //2 for the thread cluster109 thread_cluster;110 thread_cluster;111 //2 for the fibers cluster112 fibers_cluster;113 fibers_cluster;114 };115 116 //Declares thread117 thread UThread {};118 void ?{}(UThread& this) {119 //Construct underlying thread to automatically120 //be scheduled on the thread cluster121 (this){ thread_cluster }122 }123 124 void main(UThread & this);125 126 //Declares fibers127 thread Fiber {};128 void ?{}(Fiber& this) {129 //Construct underlying thread to automatically130 //be scheduled on the fiber cluster131 (this.__thread){ fibers_cluster }132 }133 134 void main(Fiber & this);135 \end{cfacode}136 \end{figure} -
doc/proposals/concurrency/thesis.tex
r0fe4e62 rf5c3b6c 35 35 \usepackage[pagewise]{lineno} 36 36 \usepackage{fancyhdr} 37 \usepackage{float}38 37 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 39 \usepackage{siunitx}40 \sisetup{ binary-units=true }41 38 \input{style} % bespoke macros used in the document 42 39 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} … … 110 107 \input{together} 111 108 112 \input{results}113 114 109 \input{future} 115 110 -
doc/proposals/concurrency/version
r0fe4e62 rf5c3b6c 1 0.1 1.1291 0.10.212 -
src/Common/Debug.h
r0fe4e62 rf5c3b6c 24 24 #include "SynTree/Declaration.h" 25 25 26 #define DEBUG 26 /// debug codegen a translation unit 27 static inline void debugCodeGen( const std::list< Declaration * > & translationUnit, const std::string & label ) { 28 std::list< Declaration * > decls; 27 29 28 namespace Debug { 29 /// debug codegen a translation unit 30 static inline void codeGen( __attribute__((unused)) const std::list< Declaration * > & translationUnit, __attribute__((unused)) const std::string & label ) { 31 #ifdef DEBUG 32 std::list< Declaration * > decls; 30 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), []( Declaration * decl ) { 31 return ! LinkageSpec::isBuiltin( decl->get_linkage() ); 32 }); 33 33 34 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), []( Declaration * decl ) { 35 return ! LinkageSpec::isBuiltin( decl->get_linkage() ); 36 }); 37 38 std::cerr << "======" << label << "======" << std::endl; 39 CodeGen::generate( decls, std::cerr, false, true ); 40 #endif 41 } // dump 42 43 static inline void treeDump( __attribute__((unused)) const std::list< Declaration * > & translationUnit, __attribute__((unused)) const std::string & label ) { 44 #ifdef DEBUG 45 std::list< Declaration * > decls; 46 47 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), []( Declaration * decl ) { 48 return ! LinkageSpec::isBuiltin( decl->get_linkage() ); 49 }); 50 51 std::cerr << "======" << label << "======" << std::endl; 52 printAll( decls, std::cerr ); 53 #endif 54 } // dump 55 } 34 std::cerr << "======" << label << "======" << std::endl; 35 CodeGen::generate( decls, std::cerr, false, true ); 36 } // dump 56 37 57 38 // Local Variables: // -
src/Concurrency/Keywords.cc
r0fe4e62 rf5c3b6c 553 553 ), 554 554 new ListInit( 555 map_range < std::list<Initializer*> > ( args, [ ](DeclarationWithType * var ){555 map_range < std::list<Initializer*> > ( args, [this](DeclarationWithType * var ){ 556 556 Type * type = var->get_type()->clone(); 557 557 type->set_mutex( false ); -
src/InitTweak/GenInit.cc
r0fe4e62 rf5c3b6c 214 214 } 215 215 // a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable) 216 return managedTypes.find( SymTab::Mangler::mangle Concrete( type ) ) != managedTypes.end() || GenPoly::isPolyType( type );216 return managedTypes.find( SymTab::Mangler::mangle( type ) ) != managedTypes.end() || GenPoly::isPolyType( type ); 217 217 } 218 218 … … 232 232 Type * type = InitTweak::getPointerBase( params.front()->get_type() ); 233 233 assert( type ); 234 managedTypes.insert( SymTab::Mangler::mangle Concrete( type ) );234 managedTypes.insert( SymTab::Mangler::mangle( type ) ); 235 235 } 236 236 } … … 242 242 if ( ObjectDecl * field = dynamic_cast< ObjectDecl * >( member ) ) { 243 243 if ( isManaged( field ) ) { 244 // generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that245 // polymorphic constructors make generic types managed types246 244 StructInstType inst( Type::Qualifiers(), aggregateDecl ); 247 managedTypes.insert( SymTab::Mangler::mangle Concrete( &inst ) );245 managedTypes.insert( SymTab::Mangler::mangle( &inst ) ); 248 246 break; 249 247 } -
src/InitTweak/InitTweak.cc
r0fe4e62 rf5c3b6c 99 99 class InitExpander::ExpanderImpl { 100 100 public: 101 virtual ~ExpanderImpl() = default;102 101 virtual std::list< Expression * > next( std::list< Expression * > & indices ) = 0; 103 102 virtual Statement * buildListInit( UntypedExpr * callExpr, std::list< Expression * > & indices ) = 0; … … 107 106 public: 108 107 InitImpl( Initializer * init ) : init( init ) {} 109 virtual ~InitImpl() = default;110 108 111 109 virtual std::list< Expression * > next( __attribute((unused)) std::list< Expression * > & indices ) { … … 124 122 public: 125 123 ExprImpl( Expression * expr ) : arg( expr ) {} 126 virtual ~ExprImpl() { delete arg; } 124 125 ~ExprImpl() { delete arg; } 127 126 128 127 virtual std::list< Expression * > next( std::list< Expression * > & indices ) { -
src/ResolvExpr/AlternativeFinder.cc
r0fe4e62 rf5c3b6c 22 22 #include <memory> // for allocator_traits<>::value_type 23 23 #include <utility> // for pair 24 #include <vector> // for vector25 24 26 25 #include "Alternative.h" // for AltList, Alternative … … 334 333 tmpCost.incPoly( -tmpCost.get_polyCost() ); 335 334 if ( tmpCost != Cost::zero ) { 335 // if ( convCost != Cost::zero ) { 336 336 Type *newType = formalType->clone(); 337 337 env.apply( newType ); … … 405 405 /// needAssertions.insert( needAssertions.end(), (*tyvar)->get_assertions().begin(), (*tyvar)->get_assertions().end() ); 406 406 } 407 } 408 409 /// instantiate a single argument by matching actuals from [actualIt, actualEnd) against formalType, 410 /// producing expression(s) in out and their total cost in cost. 411 template< typename AltIterator, typename OutputIterator > 412 bool instantiateArgument( Type * formalType, Initializer * defaultValue, AltIterator & actualIt, AltIterator actualEnd, OpenVarSet & openVars, TypeEnvironment & resultEnv, AssertionSet & resultNeed, AssertionSet & resultHave, const SymTab::Indexer & indexer, Cost & cost, OutputIterator out ) { 413 if ( TupleType * tupleType = dynamic_cast< TupleType * >( formalType ) ) { 414 // formalType is a TupleType - group actuals into a TupleExpr whose type unifies with the TupleType 415 std::list< Expression * > exprs; 416 for ( Type * type : *tupleType ) { 417 if ( ! instantiateArgument( type, defaultValue, actualIt, actualEnd, openVars, resultEnv, resultNeed, resultHave, indexer, cost, back_inserter( exprs ) ) ) { 418 deleteAll( exprs ); 419 return false; 420 } 421 } 422 *out++ = new TupleExpr( exprs ); 423 } else if ( TypeInstType * ttype = Tuples::isTtype( formalType ) ) { 424 // xxx - mixing default arguments with variadic?? 425 std::list< Expression * > exprs; 426 for ( ; actualIt != actualEnd; ++actualIt ) { 427 exprs.push_back( actualIt->expr->clone() ); 428 cost += actualIt->cost; 429 } 430 Expression * arg = nullptr; 431 if ( exprs.size() == 1 && Tuples::isTtype( exprs.front()->get_result() ) ) { 432 // the case where a ttype value is passed directly is special, e.g. for argument forwarding purposes 433 // xxx - what if passing multiple arguments, last of which is ttype? 434 // xxx - what would happen if unify was changed so that unifying tuple types flattened both before unifying lists? then pass in TupleType(ttype) below. 435 arg = exprs.front(); 436 } else { 437 arg = new TupleExpr( exprs ); 438 } 439 assert( arg && arg->get_result() ); 440 if ( ! unify( ttype, arg->get_result(), resultEnv, resultNeed, resultHave, openVars, indexer ) ) { 441 return false; 442 } 443 *out++ = arg; 444 } else if ( actualIt != actualEnd ) { 445 // both actualType and formalType are atomic (non-tuple) types - if they unify 446 // then accept actual as an argument, otherwise return false (fail to instantiate argument) 447 Expression * actual = actualIt->expr; 448 Type * actualType = actual->get_result(); 449 450 PRINT( 451 std::cerr << "formal type is "; 452 formalType->print( std::cerr ); 453 std::cerr << std::endl << "actual type is "; 454 actualType->print( std::cerr ); 455 std::cerr << std::endl; 456 ) 457 if ( ! unify( formalType, actualType, resultEnv, resultNeed, resultHave, openVars, indexer ) ) { 458 // std::cerr << "unify failed" << std::endl; 459 return false; 460 } 461 // move the expression from the alternative to the output iterator 462 *out++ = actual; 463 actualIt->expr = nullptr; 464 cost += actualIt->cost; 465 ++actualIt; 466 } else { 467 // End of actuals - Handle default values 468 if ( SingleInit *si = dynamic_cast<SingleInit *>( defaultValue )) { 469 if ( CastExpr * castExpr = dynamic_cast< CastExpr * >( si->get_value() ) ) { 470 // so far, only constant expressions are accepted as default values 471 if ( ConstantExpr *cnstexpr = dynamic_cast<ConstantExpr *>( castExpr->get_arg() ) ) { 472 if ( Constant *cnst = dynamic_cast<Constant *>( cnstexpr->get_constant() ) ) { 473 if ( unify( formalType, cnst->get_type(), resultEnv, resultNeed, resultHave, openVars, indexer ) ) { 474 *out++ = cnstexpr->clone(); 475 return true; 476 } // if 477 } // if 478 } // if 479 } 480 } // if 481 return false; 482 } // if 483 return true; 484 } 485 486 bool AlternativeFinder::instantiateFunction( std::list< DeclarationWithType* >& formals, const AltList &actuals, bool isVarArgs, OpenVarSet& openVars, TypeEnvironment &resultEnv, AssertionSet &resultNeed, AssertionSet &resultHave, AltList & out ) { 487 simpleCombineEnvironments( actuals.begin(), actuals.end(), resultEnv ); 488 // make sure we don't widen any existing bindings 489 for ( TypeEnvironment::iterator i = resultEnv.begin(); i != resultEnv.end(); ++i ) { 490 i->allowWidening = false; 491 } 492 resultEnv.extractOpenVars( openVars ); 493 494 // flatten actuals so that each actual has an atomic (non-tuple) type 495 AltList exploded; 496 Tuples::explode( actuals, indexer, back_inserter( exploded ) ); 497 498 AltList::iterator actualExpr = exploded.begin(); 499 AltList::iterator actualEnd = exploded.end(); 500 for ( DeclarationWithType * formal : formals ) { 501 // match flattened actuals with formal parameters - actuals will be grouped to match 502 // with formals as appropriate 503 Cost cost = Cost::zero; 504 std::list< Expression * > newExprs; 505 ObjectDecl * obj = strict_dynamic_cast< ObjectDecl * >( formal ); 506 if ( ! instantiateArgument( obj->get_type(), obj->get_init(), actualExpr, actualEnd, openVars, resultEnv, resultNeed, resultHave, indexer, cost, back_inserter( newExprs ) ) ) { 507 deleteAll( newExprs ); 508 return false; 509 } 510 // success - produce argument as a new alternative 511 assert( newExprs.size() == 1 ); 512 out.push_back( Alternative( newExprs.front(), resultEnv, cost ) ); 513 } 514 if ( actualExpr != actualEnd ) { 515 // there are still actuals remaining, but we've run out of formal parameters to match against 516 // this is okay only if the function is variadic 517 if ( ! isVarArgs ) { 518 return false; 519 } 520 out.splice( out.end(), exploded, actualExpr, actualEnd ); 521 } 522 return true; 407 523 } 408 524 … … 559 675 } 560 676 561 /// Gets a default value from an initializer, nullptr if not present 562 ConstantExpr* getDefaultValue( Initializer* init ) { 563 if ( SingleInit* si = dynamic_cast<SingleInit*>( init ) ) { 564 if ( CastExpr* ce = dynamic_cast<CastExpr*>( si->get_value() ) ) { 565 return dynamic_cast<ConstantExpr*>( ce->get_arg() ); 566 } 567 } 568 return nullptr; 569 } 570 571 /// State to iteratively build a match of parameter expressions to arguments 572 struct ArgPack { 573 AltList actuals; ///< Arguments included in this pack 574 TypeEnvironment env; ///< Environment for this pack 575 AssertionSet need; ///< Assertions outstanding for this pack 576 AssertionSet have; ///< Assertions found for this pack 577 OpenVarSet openVars; ///< Open variables for this pack 578 unsigned nextArg; ///< Index of next argument in arguments list 579 std::vector<Alternative> expls; ///< Exploded actuals left over from last match 580 unsigned nextExpl; ///< Index of next exploded alternative to use 581 std::vector<unsigned> tupleEls; /// Number of elements in current tuple element(s) 582 583 ArgPack(const TypeEnvironment& env, const AssertionSet& need, const AssertionSet& have, 584 const OpenVarSet& openVars) 585 : actuals(), env(env), need(need), have(have), openVars(openVars), nextArg(0), 586 expls(), nextExpl(0), tupleEls() {} 587 588 /// Starts a new tuple expression 589 void beginTuple() { 590 if ( ! tupleEls.empty() ) ++tupleEls.back(); 591 tupleEls.push_back(0); 592 } 593 594 /// Ends a tuple expression, consolidating the appropriate actuals 595 void endTuple() { 596 // set up new Tuple alternative 597 std::list<Expression*> exprs; 598 Cost cost = Cost::zero; 599 600 // transfer elements into alternative 601 for (unsigned i = 0; i < tupleEls.back(); ++i) { 602 exprs.push_front( actuals.back().expr ); 603 actuals.back().expr = nullptr; 604 cost += actuals.back().cost; 605 actuals.pop_back(); 606 } 607 tupleEls.pop_back(); 608 609 // build new alternative 610 actuals.emplace_back( new TupleExpr( exprs ), this->env, cost ); 611 } 612 613 /// Clones and adds an actual, returns this 614 ArgPack& withArg( Expression* expr, Cost cost = Cost::zero ) { 615 actuals.emplace_back( expr->clone(), this->env, cost ); 616 if ( ! tupleEls.empty() ) ++tupleEls.back(); 617 return *this; 618 } 619 }; 620 621 /// Instantiates an argument to match a formal, returns false if no results left 622 bool instantiateArgument( Type* formalType, Initializer* initializer, 623 const std::vector< AlternativeFinder >& args, 624 std::vector<ArgPack>& results, std::vector<ArgPack>& nextResults, 625 const SymTab::Indexer& indexer ) { 626 if ( TupleType* tupleType = dynamic_cast<TupleType*>( formalType ) ) { 627 // formalType is a TupleType - group actuals into a TupleExpr 628 for ( ArgPack& result : results ) { result.beginTuple(); } 629 for ( Type* type : *tupleType ) { 630 // xxx - dropping initializer changes behaviour from previous, but seems correct 631 if ( ! instantiateArgument( type, nullptr, args, results, nextResults, indexer ) ) 632 return false; 633 } 634 for ( ArgPack& result : results ) { result.endTuple(); } 635 return true; 636 } else if ( TypeInstType* ttype = Tuples::isTtype( formalType ) ) { 637 // formalType is a ttype, consumes all remaining arguments 638 // xxx - mixing default arguments with variadic?? 639 std::vector<ArgPack> finalResults{}; /// list of completed tuples 640 // start tuples 641 for ( ArgPack& result : results ) { 642 result.beginTuple(); 643 644 // use rest of exploded tuple if present 645 while ( result.nextExpl < result.expls.size() ) { 646 const Alternative& actual = result.expls[result.nextExpl]; 647 result.env.addActual( actual.env, result.openVars ); 648 result.withArg( actual.expr ); 649 ++result.nextExpl; 650 } 651 } 652 // iterate until all results completed 653 while ( ! results.empty() ) { 654 // add another argument to results 655 for ( ArgPack& result : results ) { 656 // finish result when out of arguments 657 if ( result.nextArg >= args.size() ) { 658 Type* argType = result.actuals.back().expr->get_result(); 659 if ( result.tupleEls.back() == 1 && Tuples::isTtype( argType ) ) { 660 // the case where a ttype value is passed directly is special, e.g. for 661 // argument forwarding purposes 662 // xxx - what if passing multiple arguments, last of which is ttype? 663 // xxx - what would happen if unify was changed so that unifying tuple 664 // types flattened both before unifying lists? then pass in TupleType 665 // (ttype) below. 666 result.tupleEls.pop_back(); 667 } else { 668 // collapse leftover arguments into tuple 669 result.endTuple(); 670 argType = result.actuals.back().expr->get_result(); 671 } 672 // check unification for ttype before adding to final 673 if ( unify( ttype, argType, result.env, result.need, result.have, 674 result.openVars, indexer ) ) { 675 finalResults.push_back( std::move(result) ); 676 } 677 continue; 678 } 679 680 // add each possible next argument 681 for ( const Alternative& actual : args[result.nextArg] ) { 682 ArgPack aResult = result; // copy to clone everything 683 // add details of actual to result 684 aResult.env.addActual( actual.env, aResult.openVars ); 685 Cost cost = actual.cost; 686 687 // explode argument 688 std::vector<Alternative> exploded; 689 Tuples::explode( actual, indexer, back_inserter( exploded ) ); 690 691 // add exploded argument to tuple 692 for ( Alternative& aActual : exploded ) { 693 aResult.withArg( aActual.expr, cost ); 694 cost = Cost::zero; 695 } 696 ++aResult.nextArg; 697 nextResults.push_back( std::move(aResult) ); 698 } 699 } 700 701 // reset for next round 702 results.swap( nextResults ); 703 nextResults.clear(); 704 } 705 results.swap( finalResults ); 706 return ! results.empty(); 707 } 708 709 // iterate each current subresult 710 for ( unsigned iResult = 0; iResult < results.size(); ++iResult ) { 711 ArgPack& result = results[iResult]; 712 713 if ( result.nextExpl < result.expls.size() ) { 714 // use remainder of exploded tuple if present 715 const Alternative& actual = result.expls[result.nextExpl]; 716 result.env.addActual( actual.env, result.openVars ); 717 Type* actualType = actual.expr->get_result(); 718 719 PRINT( 720 std::cerr << "formal type is "; 721 formalType->print( std::cerr ); 722 std::cerr << std::endl << "actual type is "; 723 actualType->print( std::cerr ); 724 std::cerr << std::endl; 725 ) 726 727 if ( unify( formalType, actualType, result.env, result.need, result.have, 728 result.openVars, indexer ) ) { 729 ++result.nextExpl; 730 nextResults.push_back( std::move(result.withArg( actual.expr )) ); 731 } 732 733 continue; 734 } else if ( result.nextArg >= args.size() ) { 735 // use default initializers if out of arguments 736 if ( ConstantExpr* cnstExpr = getDefaultValue( initializer ) ) { 737 if ( Constant* cnst = dynamic_cast<Constant*>( cnstExpr->get_constant() ) ) { 738 if ( unify( formalType, cnst->get_type(), result.env, result.need, 739 result.have, result.openVars, indexer ) ) { 740 nextResults.push_back( std::move(result.withArg( cnstExpr )) ); 741 } 742 } 743 } 744 continue; 745 } 746 747 // Check each possible next argument 748 for ( const Alternative& actual : args[result.nextArg] ) { 749 ArgPack aResult = result; // copy to clone everything 750 // add details of actual to result 751 aResult.env.addActual( actual.env, aResult.openVars ); 752 753 // explode argument 754 std::vector<Alternative> exploded; 755 Tuples::explode( actual, indexer, back_inserter( exploded ) ); 756 if ( exploded.empty() ) { 757 // skip empty tuple arguments 758 ++aResult.nextArg; 759 results.push_back( std::move(aResult) ); 760 continue; 761 } 762 763 // consider only first exploded actual 764 const Alternative& aActual = exploded.front(); 765 Type* actualType = aActual.expr->get_result()->clone(); 766 767 PRINT( 768 std::cerr << "formal type is "; 769 formalType->print( std::cerr ); 770 std::cerr << std::endl << "actual type is "; 771 actualType->print( std::cerr ); 772 std::cerr << std::endl; 773 ) 774 775 // attempt to unify types 776 if ( unify( formalType, actualType, aResult.env, aResult.need, aResult.have, aResult.openVars, indexer ) ) { 777 // add argument 778 aResult.withArg( aActual.expr, actual.cost ); 779 ++aResult.nextArg; 780 if ( exploded.size() > 1 ) { 781 // other parts of tuple left over 782 aResult.expls = std::move( exploded ); 783 aResult.nextExpl = 1; 784 } 785 nextResults.push_back( std::move(aResult) ); 786 } 787 } 788 } 789 790 // reset for next parameter 791 results.swap( nextResults ); 792 nextResults.clear(); 793 794 return ! results.empty(); 795 } 796 797 template<typename OutputIterator> 798 void AlternativeFinder::makeFunctionAlternatives( const Alternative &func, 799 FunctionType *funcType, const std::vector< AlternativeFinder > &args, 800 OutputIterator out ) { 801 OpenVarSet funcOpenVars; 802 AssertionSet funcNeed, funcHave; 803 TypeEnvironment funcEnv( func.env ); 804 makeUnifiableVars( funcType, funcOpenVars, funcNeed ); 805 // add all type variables as open variables now so that those not used in the parameter 806 // list are still considered open. 807 funcEnv.add( funcType->get_forall() ); 808 677 template< typename OutputIterator > 678 void AlternativeFinder::makeFunctionAlternatives( const Alternative &func, FunctionType *funcType, const AltList &actualAlt, OutputIterator out ) { 679 OpenVarSet openVars; 680 AssertionSet resultNeed, resultHave; 681 TypeEnvironment resultEnv( func.env ); 682 makeUnifiableVars( funcType, openVars, resultNeed ); 683 resultEnv.add( funcType->get_forall() ); // add all type variables as open variables now so that those not used in the parameter list are still considered open 684 AltList instantiatedActuals; // filled by instantiate function 809 685 if ( targetType && ! targetType->isVoid() && ! funcType->get_returnVals().empty() ) { 810 686 // attempt to narrow based on expected target type 811 687 Type * returnType = funcType->get_returnVals().front()->get_type(); 812 if ( ! unify( returnType, targetType, funcEnv, funcNeed, funcHave, funcOpenVars, 813 indexer ) ) { 814 // unification failed, don't pursue this function alternative 688 if ( ! unify( returnType, targetType, resultEnv, resultNeed, resultHave, openVars, indexer ) ) { 689 // unification failed, don't pursue this alternative 815 690 return; 816 691 } 817 692 } 818 693 819 // iteratively build matches, one parameter at a time 820 std::vector<ArgPack> results{ ArgPack{ funcEnv, funcNeed, funcHave, funcOpenVars } }; 821 std::vector<ArgPack> nextResults{}; 822 for ( DeclarationWithType* formal : funcType->get_parameters() ) { 823 ObjectDecl* obj = strict_dynamic_cast< ObjectDecl* >( formal ); 824 if ( ! instantiateArgument( 825 obj->get_type(), obj->get_init(), args, results, nextResults, indexer ) ) 826 return; 827 } 828 829 // filter out results that don't use all the arguments, and aren't variadic 830 std::vector<ArgPack> finalResults{}; 831 if ( funcType->get_isVarArgs() ) { 832 for ( ArgPack& result : results ) { 833 // use rest of exploded tuple if present 834 while ( result.nextExpl < result.expls.size() ) { 835 const Alternative& actual = result.expls[result.nextExpl]; 836 result.env.addActual( actual.env, result.openVars ); 837 result.withArg( actual.expr ); 838 ++result.nextExpl; 839 } 840 } 841 842 while ( ! results.empty() ) { 843 // build combinations for all remaining arguments 844 for ( ArgPack& result : results ) { 845 // keep if used all arguments 846 if ( result.nextArg >= args.size() ) { 847 finalResults.push_back( std::move(result) ); 848 continue; 849 } 850 851 // add each possible next argument 852 for ( const Alternative& actual : args[result.nextArg] ) { 853 ArgPack aResult = result; // copy to clone everything 854 // add details of actual to result 855 aResult.env.addActual( actual.env, aResult.openVars ); 856 Cost cost = actual.cost; 857 858 // explode argument 859 std::vector<Alternative> exploded; 860 Tuples::explode( actual, indexer, back_inserter( exploded ) ); 861 862 // add exploded argument to arg list 863 for ( Alternative& aActual : exploded ) { 864 aResult.withArg( aActual.expr, cost ); 865 cost = Cost::zero; 866 } 867 ++aResult.nextArg; 868 nextResults.push_back( std::move(aResult) ); 869 } 870 } 871 872 // reset for next round 873 results.swap( nextResults ); 874 nextResults.clear(); 875 } 876 } else { 877 // filter out results that don't use all the arguments 878 for ( ArgPack& result : results ) { 879 if ( result.nextExpl >= result.expls.size() && result.nextArg >= args.size() ) { 880 finalResults.push_back( std::move(result) ); 881 } 882 } 883 } 884 885 // validate matching combos, add to final result list 886 for ( ArgPack& result : finalResults ) { 694 if ( instantiateFunction( funcType->get_parameters(), actualAlt, funcType->get_isVarArgs(), openVars, resultEnv, resultNeed, resultHave, instantiatedActuals ) ) { 887 695 ApplicationExpr *appExpr = new ApplicationExpr( func.expr->clone() ); 888 Alternative newAlt( appExpr, result .env, sumCost( result.actuals ) );889 makeExprList( result.actuals, appExpr->get_args() );696 Alternative newAlt( appExpr, resultEnv, sumCost( instantiatedActuals ) ); 697 makeExprList( instantiatedActuals, appExpr->get_args() ); 890 698 PRINT( 891 699 std::cerr << "instantiate function success: " << appExpr << std::endl; 892 700 std::cerr << "need assertions:" << std::endl; 893 printAssertionSet( result .need, std::cerr, 8 );701 printAssertionSet( resultNeed, std::cerr, 8 ); 894 702 ) 895 inferParameters( result .need, result.have, newAlt, result.openVars, out );703 inferParameters( resultNeed, resultHave, newAlt, openVars, out ); 896 704 } 897 705 } … … 903 711 if ( funcFinder.alternatives.empty() ) return; 904 712 905 std::vector< AlternativeFinder > argAlternatives; 906 findSubExprs( untypedExpr->begin_args(), untypedExpr->end_args(), 907 back_inserter( argAlternatives ) ); 713 std::list< AlternativeFinder > argAlternatives; 714 findSubExprs( untypedExpr->begin_args(), untypedExpr->end_args(), back_inserter( argAlternatives ) ); 715 716 std::list< AltList > possibilities; 717 combos( argAlternatives.begin(), argAlternatives.end(), back_inserter( possibilities ) ); 908 718 909 719 // take care of possible tuple assignments 910 720 // if not tuple assignment, assignment is taken care of as a normal function call 911 Tuples::handleTupleAssignment( *this, untypedExpr, argAlternatives );721 Tuples::handleTupleAssignment( *this, untypedExpr, possibilities ); 912 722 913 723 // find function operators … … 934 744 Alternative newFunc( *func ); 935 745 referenceToRvalueConversion( newFunc.expr ); 936 makeFunctionAlternatives( newFunc, function, argAlternatives, 937 std::back_inserter( candidates ) ); 746 for ( std::list< AltList >::iterator actualAlt = possibilities.begin(); actualAlt != possibilities.end(); ++actualAlt ) { 747 // XXX 748 //Designators::check_alternative( function, *actualAlt ); 749 makeFunctionAlternatives( newFunc, function, *actualAlt, std::back_inserter( candidates ) ); 750 } 938 751 } 939 752 } else if ( TypeInstType *typeInst = dynamic_cast< TypeInstType* >( func->expr->get_result()->stripReferences() ) ) { // handle ftype (e.g. *? on function pointer) … … 943 756 Alternative newFunc( *func ); 944 757 referenceToRvalueConversion( newFunc.expr ); 945 makeFunctionAlternatives( newFunc, function, argAlternatives, 946 std::back_inserter( candidates ) ); 758 for ( std::list< AltList >::iterator actualAlt = possibilities.begin(); actualAlt != possibilities.end(); ++actualAlt ) { 759 makeFunctionAlternatives( newFunc, function, *actualAlt, std::back_inserter( candidates ) ); 760 } // for 947 761 } // if 948 762 } // if 949 763 } 764 765 // try each function operator ?() with the current function alternative and each of the argument combinations 766 for ( AltList::iterator funcOp = funcOpFinder.alternatives.begin(); funcOp != funcOpFinder.alternatives.end(); ++funcOp ) { 767 // check if the type is pointer to function 768 if ( PointerType *pointer = dynamic_cast< PointerType* >( funcOp->expr->get_result()->stripReferences() ) ) { 769 if ( FunctionType *function = dynamic_cast< FunctionType* >( pointer->get_base() ) ) { 770 Alternative newFunc( *funcOp ); 771 referenceToRvalueConversion( newFunc.expr ); 772 for ( std::list< AltList >::iterator actualAlt = possibilities.begin(); actualAlt != possibilities.end(); ++actualAlt ) { 773 AltList currentAlt; 774 currentAlt.push_back( *func ); 775 currentAlt.insert( currentAlt.end(), actualAlt->begin(), actualAlt->end() ); 776 makeFunctionAlternatives( newFunc, function, currentAlt, std::back_inserter( candidates ) ); 777 } // for 778 } // if 779 } // if 780 } // for 950 781 } catch ( SemanticError &e ) { 951 782 errors.append( e ); 952 783 } 953 784 } // for 954 955 // try each function operator ?() with each function alternative956 if ( ! funcOpFinder.alternatives.empty() ) {957 // add function alternatives to front of argument list958 argAlternatives.insert( argAlternatives.begin(), std::move(funcFinder) );959 960 for ( AltList::iterator funcOp = funcOpFinder.alternatives.begin();961 funcOp != funcOpFinder.alternatives.end(); ++funcOp ) {962 try {963 // check if type is a pointer to function964 if ( PointerType* pointer = dynamic_cast<PointerType*>(965 funcOp->expr->get_result()->stripReferences() ) ) {966 if ( FunctionType* function =967 dynamic_cast<FunctionType*>( pointer->get_base() ) ) {968 Alternative newFunc( *funcOp );969 referenceToRvalueConversion( newFunc.expr );970 makeFunctionAlternatives( newFunc, function, argAlternatives,971 std::back_inserter( candidates ) );972 }973 }974 } catch ( SemanticError &e ) {975 errors.append( e );976 }977 }978 }979 785 980 786 // Implement SFINAE; resolution errors are only errors if there aren't any non-erroneous resolutions … … 1007 813 candidates.splice( candidates.end(), alternatives ); 1008 814 1009 // use a new list so that alternatives are not examined by addAnonConversions twice. 1010 AltList winners; 1011 findMinCost( candidates.begin(), candidates.end(), std::back_inserter( winners ) ); 815 findMinCost( candidates.begin(), candidates.end(), std::back_inserter( alternatives ) ); 1012 816 1013 817 // function may return struct or union value, in which case we need to add alternatives for implicit 1014 818 // conversions to each of the anonymous members, must happen after findMinCost since anon conversions 1015 819 // are never the cheapest expression 1016 for ( const Alternative & alt : winners ) {820 for ( const Alternative & alt : alternatives ) { 1017 821 addAnonConversions( alt ); 1018 822 } 1019 alternatives.splice( alternatives.begin(), winners );1020 823 1021 824 if ( alternatives.empty() && targetType && ! targetType->isVoid() ) { -
src/ResolvExpr/AlternativeFinder.h
r0fe4e62 rf5c3b6c 34 34 public: 35 35 AlternativeFinder( const SymTab::Indexer &indexer, const TypeEnvironment &env ); 36 37 AlternativeFinder( const AlternativeFinder& o )38 : indexer(o.indexer), alternatives(o.alternatives), env(o.env),39 targetType(o.targetType) {}40 41 AlternativeFinder( AlternativeFinder&& o )42 : indexer(o.indexer), alternatives(std::move(o.alternatives)), env(o.env),43 targetType(o.targetType) {}44 45 AlternativeFinder& operator= ( const AlternativeFinder& o ) {46 if (&o == this) return *this;47 48 // horrific nasty hack to rebind references...49 alternatives.~AltList();50 new(this) AlternativeFinder(o);51 return *this;52 }53 54 AlternativeFinder& operator= ( AlternativeFinder&& o ) {55 if (&o == this) return *this;56 57 // horrific nasty hack to rebind references...58 alternatives.~AltList();59 new(this) AlternativeFinder(std::move(o));60 return *this;61 }62 63 36 void find( Expression *expr, bool adjust = false, bool prune = true, bool failFast = true ); 64 37 /// Calls find with the adjust flag set; adjustment turns array and function types into equivalent pointer types … … 126 99 /// Adds alternatives for offsetof expressions, given the base type and name of the member 127 100 template< typename StructOrUnionType > void addOffsetof( StructOrUnionType *aggInst, const std::string &name ); 128 template<typename OutputIterator> 129 void makeFunctionAlternatives( const Alternative &func, FunctionType *funcType, const std::vector< AlternativeFinder >& args, OutputIterator out ); 101 bool instantiateFunction( std::list< DeclarationWithType* >& formals, const AltList &actuals, bool isVarArgs, OpenVarSet& openVars, TypeEnvironment &resultEnv, AssertionSet &resultNeed, AssertionSet &resultHave, AltList & out ); 102 template< typename OutputIterator > 103 void makeFunctionAlternatives( const Alternative &func, FunctionType *funcType, const AltList &actualAlt, OutputIterator out ); 130 104 template< typename OutputIterator > 131 105 void inferParameters( const AssertionSet &need, AssertionSet &have, const Alternative &newAlt, OpenVarSet &openVars, OutputIterator out ); -
src/ResolvExpr/CurrentObject.cc
r0fe4e62 rf5c3b6c 260 260 261 261 AggregateIterator( const std::string & kind, const std::string & name, Type * inst, const MemberList & members ) : kind( kind ), name( name ), inst( inst ), members( members ), curMember( members.begin() ), sub( makeGenericSubstitution( inst ) ) { 262 PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; )263 262 init(); 264 263 } -
src/ResolvExpr/RenameVars.cc
r0fe4e62 rf5c3b6c 29 29 RenameVars global_renamer; 30 30 31 RenameVars::RenameVars() : level( 0 ) , resetCount( 0 ){31 RenameVars::RenameVars() : level( 0 ) { 32 32 mapStack.push_front( std::map< std::string, std::string >() ); 33 33 } … … 35 35 void RenameVars::reset() { 36 36 level = 0; 37 resetCount++;38 37 } 39 38 … … 131 130 for ( Type::ForallList::iterator i = type->get_forall().begin(); i != type->get_forall().end(); ++i ) { 132 131 std::ostringstream output; 133 output << "_" << resetCount << "_" <<level << "_" << (*i)->get_name();132 output << "_" << level << "_" << (*i)->get_name(); 134 133 std::string newname( output.str() ); 135 134 mapStack.front()[ (*i)->get_name() ] = newname; -
src/ResolvExpr/RenameVars.h
r0fe4e62 rf5c3b6c 48 48 void typeBefore( Type *type ); 49 49 void typeAfter( Type *type ); 50 int level , resetCount;50 int level; 51 51 std::list< std::map< std::string, std::string > > mapStack; 52 52 }; -
src/ResolvExpr/TypeEnvironment.cc
r0fe4e62 rf5c3b6c 201 201 } 202 202 203 void TypeEnvironment::addActual( const TypeEnvironment& actualEnv, OpenVarSet& openVars ) {204 for ( const EqvClass& c : actualEnv ) {205 EqvClass c2 = c;206 c2.allowWidening = false;207 for ( const std::string& var : c2.vars ) {208 openVars[ var ] = c2.data;209 }210 env.push_back( std::move(c2) );211 }212 }213 214 203 } // namespace ResolvExpr 215 204 -
src/ResolvExpr/TypeEnvironment.h
r0fe4e62 rf5c3b6c 86 86 TypeEnvironment *clone() const { return new TypeEnvironment( *this ); } 87 87 88 /// Iteratively adds the environment of a new actual (with allowWidening = false),89 /// and extracts open variables.90 void addActual( const TypeEnvironment& actualEnv, OpenVarSet& openVars );91 92 88 typedef std::list< EqvClass >::iterator iterator; 93 89 iterator begin() { return env.begin(); } -
src/SymTab/Mangler.cc
r0fe4e62 rf5c3b6c 32 32 namespace SymTab { 33 33 std::string Mangler::mangleType( Type * ty ) { 34 Mangler mangler( false, true , true);34 Mangler mangler( false, true ); 35 35 maybeAccept( ty, mangler ); 36 36 return mangler.get_mangleName(); 37 37 } 38 38 39 std::string Mangler::mangleConcrete( Type* ty ) { 40 Mangler mangler( false, false, false ); 41 maybeAccept( ty, mangler ); 42 return mangler.get_mangleName(); 43 } 44 45 Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams ) 46 : nextVarNum( 0 ), isTopLevel( true ), mangleOverridable( mangleOverridable ), typeMode( typeMode ), mangleGenericParams( mangleGenericParams ) {} 39 Mangler::Mangler( bool mangleOverridable, bool typeMode ) 40 : nextVarNum( 0 ), isTopLevel( true ), mangleOverridable( mangleOverridable ), typeMode( typeMode ) {} 47 41 48 42 Mangler::Mangler( const Mangler &rhs ) : mangleName() { … … 172 166 173 167 mangleName << ( refType->get_name().length() + prefix.length() ) << prefix << refType->get_name(); 174 175 if ( mangleGenericParams ) { 176 std::list< Expression* >& params = refType->get_parameters(); 177 if ( ! params.empty() ) { 178 mangleName << "_"; 179 for ( std::list< Expression* >::const_iterator param = params.begin(); param != params.end(); ++param ) { 180 TypeExpr *paramType = dynamic_cast< TypeExpr* >( *param ); 181 assertf(paramType, "Aggregate parameters should be type expressions: %s", toString(*param).c_str()); 182 maybeAccept( paramType->get_type(), *this ); 183 } 184 mangleName << "_"; 168 } 169 170 void Mangler::mangleGenericRef( ReferenceToType * refType, std::string prefix ) { 171 printQualifiers( refType ); 172 173 std::ostringstream oldName( mangleName.str() ); 174 mangleName.clear(); 175 176 mangleName << prefix << refType->get_name(); 177 178 std::list< Expression* >& params = refType->get_parameters(); 179 if ( ! params.empty() ) { 180 mangleName << "_"; 181 for ( std::list< Expression* >::const_iterator param = params.begin(); param != params.end(); ++param ) { 182 TypeExpr *paramType = dynamic_cast< TypeExpr* >( *param ); 183 assertf(paramType, "Aggregate parameters should be type expressions: %s", toString(*param).c_str()); 184 maybeAccept( paramType->get_type(), *this ); 185 185 } 186 mangleName << "_"; 186 187 } 188 189 oldName << mangleName.str().length() << mangleName.str(); 190 mangleName.str( oldName.str() ); 187 191 } 188 192 189 193 void Mangler::visit( StructInstType * aggregateUseType ) { 190 mangleRef( aggregateUseType, "s" ); 194 if ( typeMode ) mangleGenericRef( aggregateUseType, "s" ); 195 else mangleRef( aggregateUseType, "s" ); 191 196 } 192 197 193 198 void Mangler::visit( UnionInstType * aggregateUseType ) { 194 mangleRef( aggregateUseType, "u" ); 199 if ( typeMode ) mangleGenericRef( aggregateUseType, "u" ); 200 else mangleRef( aggregateUseType, "u" ); 195 201 } 196 202 … … 279 285 varNums[ (*i)->name ] = std::pair< int, int >( nextVarNum++, (int)(*i)->get_kind() ); 280 286 for ( std::list< DeclarationWithType* >::iterator assert = (*i)->assertions.begin(); assert != (*i)->assertions.end(); ++assert ) { 281 Mangler sub_mangler( mangleOverridable, typeMode , mangleGenericParams);287 Mangler sub_mangler( mangleOverridable, typeMode ); 282 288 sub_mangler.nextVarNum = nextVarNum; 283 289 sub_mangler.isTopLevel = false; -
src/SymTab/Mangler.h
r0fe4e62 rf5c3b6c 30 30 /// Mangle syntax tree object; primary interface to clients 31 31 template< typename SynTreeClass > 32 static std::string mangle( SynTreeClass *decl, bool mangleOverridable = true, bool typeMode = false , bool mangleGenericParams = true);32 static std::string mangle( SynTreeClass *decl, bool mangleOverridable = true, bool typeMode = false ); 33 33 /// Mangle a type name; secondary interface 34 34 static std::string mangleType( Type* ty ); 35 /// Mangle ignoring generic type parameters36 static std::string mangleConcrete( Type* ty );37 38 35 39 36 virtual void visit( ObjectDecl *declaration ); … … 65 62 bool mangleOverridable; ///< Specially mangle overridable built-in methods 66 63 bool typeMode; ///< Produce a unique mangled name for a type 67 bool mangleGenericParams; ///< Include generic parameters in name mangling if true68 64 69 Mangler( bool mangleOverridable, bool typeMode , bool mangleGenericParams);65 Mangler( bool mangleOverridable, bool typeMode ); 70 66 Mangler( const Mangler & ); 71 67 72 68 void mangleDecl( DeclarationWithType *declaration ); 73 69 void mangleRef( ReferenceToType *refType, std::string prefix ); 70 void mangleGenericRef( ReferenceToType *refType, std::string prefix ); 74 71 75 72 void printQualifiers( Type *type ); … … 77 74 78 75 template< typename SynTreeClass > 79 std::string Mangler::mangle( SynTreeClass *decl, bool mangleOverridable, bool typeMode , bool mangleGenericParams) {80 Mangler mangler( mangleOverridable, typeMode , mangleGenericParams);76 std::string Mangler::mangle( SynTreeClass *decl, bool mangleOverridable, bool typeMode ) { 77 Mangler mangler( mangleOverridable, typeMode ); 81 78 maybeAccept( decl, mangler ); 82 79 return mangler.get_mangleName(); -
src/SymTab/Validate.cc
r0fe4e62 rf5c3b6c 268 268 HoistStruct::hoistStruct( translationUnit ); // must happen after EliminateTypedef, so that aggregate typedefs occur in the correct order 269 269 ReturnTypeFixer::fix( translationUnit ); // must happen before autogen 270 acceptAll( translationUnit, epc ); // must happen before VerifyCtorDtorAssign, because void return objects should not exist; before LinkReferenceToTypes because it is an indexer and needs correct types for mangling271 270 acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions 272 271 acceptAll( translationUnit, genericParams ); // check as early as possible - can't happen before LinkReferenceToTypes 272 acceptAll( translationUnit, epc ); // must happen before VerifyCtorDtorAssign, because void return objects should not exist 273 273 VerifyCtorDtorAssign::verify( translationUnit ); // must happen before autogen, because autogen examines existing ctor/dtors 274 274 ReturnChecker::checkFunctionReturns( translationUnit ); -
src/Tuples/TupleAssignment.cc
r0fe4e62 rf5c3b6c 20 20 #include <memory> // for unique_ptr, allocator_trai... 21 21 #include <string> // for string 22 #include <vector>23 22 24 23 #include "CodeGen/OperatorTable.h" … … 34 33 #include "ResolvExpr/Resolver.h" // for resolveCtorInit 35 34 #include "ResolvExpr/TypeEnvironment.h" // for TypeEnvironment 36 #include "ResolvExpr/typeops.h" // for combos37 35 #include "SynTree/Declaration.h" // for ObjectDecl 38 36 #include "SynTree/Expression.h" // for Expression, CastExpr, Name... … … 54 52 // dispatcher for Tuple (multiple and mass) assignment operations 55 53 TupleAssignSpotter( ResolvExpr::AlternativeFinder & ); 56 void spot( UntypedExpr * expr, std::vector<ResolvExpr::AlternativeFinder> &args );54 void spot( UntypedExpr * expr, const std::list<ResolvExpr::AltList> &possibilities ); 57 55 58 56 private: … … 61 59 struct Matcher { 62 60 public: 63 Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const 64 ResolvExpr::AltList& rhs ); 61 Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts ); 65 62 virtual ~Matcher() {} 66 63 virtual void match( std::list< Expression * > &out ) = 0; … … 75 72 struct MassAssignMatcher : public Matcher { 76 73 public: 77 MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 78 const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {} 74 MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts ); 79 75 virtual void match( std::list< Expression * > &out ); 80 76 }; … … 82 78 struct MultipleAssignMatcher : public Matcher { 83 79 public: 84 MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 85 const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {} 80 MultipleAssignMatcher( TupleAssignSpotter &spot, const ResolvExpr::AltList & alts ); 86 81 virtual void match( std::list< Expression * > &out ); 87 82 }; … … 119 114 } 120 115 121 void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr, 122 std::vector<ResolvExpr::AlternativeFinder> &args ) { 116 void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr, const std::list<ResolvExpr::AltList> &possibilities ) { 123 117 TupleAssignSpotter spotter( currentFinder ); 124 spotter.spot( expr, args );118 spotter.spot( expr, possibilities ); 125 119 } 126 120 … … 128 122 : currentFinder(f) {} 129 123 130 void TupleAssignSpotter::spot( UntypedExpr * expr, 131 std::vector<ResolvExpr::AlternativeFinder> &args ) { 124 void TupleAssignSpotter::spot( UntypedExpr * expr, const std::list<ResolvExpr::AltList> &possibilities ) { 132 125 if ( NameExpr *op = dynamic_cast< NameExpr * >(expr->get_function()) ) { 133 126 if ( CodeGen::isCtorDtorAssign( op->get_name() ) ) { 134 fname = op->get_name(); 135 136 // AlternativeFinder will naturally handle this case case, if it's legal 137 if ( args.size() == 0 ) return; 138 139 // if an assignment only takes 1 argument, that's odd, but maybe someone wrote 140 // the function, in which case AlternativeFinder will handle it normally 141 if ( args.size() == 1 && CodeGen::isAssignment( fname ) ) return; 142 143 // look over all possible left-hand-sides 144 for ( ResolvExpr::Alternative& lhsAlt : args[0] ) { 145 // skip non-tuple LHS 146 if ( ! refToTuple(lhsAlt.expr) ) continue; 147 148 // explode is aware of casts - ensure every LHS expression is sent into explode 149 // with a reference cast 150 // xxx - this seems to change the alternatives before the normal 151 // AlternativeFinder flow; maybe this is desired? 152 if ( ! dynamic_cast<CastExpr*>( lhsAlt.expr ) ) { 153 lhsAlt.expr = new CastExpr( lhsAlt.expr, 154 new ReferenceType( Type::Qualifiers(), 155 lhsAlt.expr->get_result()->clone() ) ); 127 fname = op->get_name(); 128 PRINT( std::cerr << "TupleAssignment: " << fname << std::endl; ) 129 for ( std::list<ResolvExpr::AltList>::const_iterator ali = possibilities.begin(); ali != possibilities.end(); ++ali ) { 130 if ( ali->size() == 0 ) continue; // AlternativeFinder will natrually handle this case, if it's legal 131 if ( ali->size() <= 1 && CodeGen::isAssignment( op->get_name() ) ) { 132 // what does it mean if an assignment takes 1 argument? maybe someone defined such a function, in which case AlternativeFinder will naturally handle it 133 continue; 156 134 } 157 135 158 // explode the LHS so that each field of a tuple-valued-expr is assigned 159 ResolvExpr::AltList lhs; 160 explode( lhsAlt, currentFinder.get_indexer(), back_inserter(lhs), true ); 161 for ( ResolvExpr::Alternative& alt : lhs ) { 162 // each LHS value must be a reference - some come in with a cast expression, 163 // if not just cast to reference here 164 if ( ! dynamic_cast<ReferenceType*>( alt.expr->get_result() ) ) { 165 alt.expr = new CastExpr( alt.expr, 166 new ReferenceType( Type::Qualifiers(), 167 alt.expr->get_result()->clone() ) ); 136 assert( ! ali->empty() ); 137 // grab args 2-N and group into a TupleExpr 138 const ResolvExpr::Alternative & alt1 = ali->front(); 139 auto begin = std::next(ali->begin(), 1), end = ali->end(); 140 PRINT( std::cerr << "alt1 is " << alt1.expr << std::endl; ) 141 if ( refToTuple(alt1.expr) ) { 142 PRINT( std::cerr << "and is reference to tuple" << std::endl; ) 143 if ( isMultAssign( begin, end ) ) { 144 PRINT( std::cerr << "possible multiple assignment" << std::endl; ) 145 matcher.reset( new MultipleAssignMatcher( *this, *ali ) ); 146 } else { 147 // mass assignment 148 PRINT( std::cerr << "possible mass assignment" << std::endl; ) 149 matcher.reset( new MassAssignMatcher( *this, *ali ) ); 168 150 } 169 }170 171 if ( args.size() == 1 ) {172 // mass default-initialization/destruction173 ResolvExpr::AltList rhs{};174 matcher.reset( new MassAssignMatcher( *this, lhs, rhs ) );175 151 match(); 176 } else if ( args.size() > 2 ) {177 // expand all possible RHS possibilities178 // TODO build iterative version of this instead of using combos179 std::vector< ResolvExpr::AltList > rhsAlts;180 combos( std::next(args.begin(), 1), args.end(),181 std::back_inserter( rhsAlts ) );182 for ( const ResolvExpr::AltList& rhsAlt : rhsAlts ) {183 // multiple assignment184 ResolvExpr::AltList rhs;185 explode( rhsAlt, currentFinder.get_indexer(),186 std::back_inserter(rhs), true );187 matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );188 match();189 }190 } else {191 for ( const ResolvExpr::Alternative& rhsAlt : args[1] ) {192 ResolvExpr::AltList rhs;193 if ( isTuple(rhsAlt.expr) ) {194 // multiple assignment195 explode( rhsAlt, currentFinder.get_indexer(),196 std::back_inserter(rhs), true );197 matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );198 } else {199 // mass assignment200 rhs.push_back( rhsAlt );201 matcher.reset( new MassAssignMatcher( *this, lhs, rhs ) );202 }203 match();204 }205 152 } 206 153 } … … 222 169 ResolvExpr::AltList current; 223 170 // now resolve new assignments 224 for ( std::list< Expression * >::iterator i = new_assigns.begin(); 225 i != new_assigns.end(); ++i ) { 171 for ( std::list< Expression * >::iterator i = new_assigns.begin(); i != new_assigns.end(); ++i ) { 226 172 PRINT( 227 173 std::cerr << "== resolving tuple assign ==" << std::endl; … … 229 175 ) 230 176 231 ResolvExpr::AlternativeFinder finder{ currentFinder.get_indexer(), 232 currentFinder.get_environ() }; 177 ResolvExpr::AlternativeFinder finder( currentFinder.get_indexer(), currentFinder.get_environ() ); 233 178 try { 234 179 finder.findWithAdjustment(*i); … … 251 196 // combine assignment environments into combined expression environment 252 197 simpleCombineEnvironments( current.begin(), current.end(), matcher->compositeEnv ); 253 currentFinder.get_alternatives().push_front( ResolvExpr::Alternative( 254 new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv, 255 ResolvExpr::sumCost( current ) + matcher->baseCost ) ); 256 } 257 258 TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter, 259 const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs ) 260 : lhs(lhs), rhs(rhs), spotter(spotter), 261 baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ) { 262 simpleCombineEnvironments( lhs.begin(), lhs.end(), compositeEnv ); 263 simpleCombineEnvironments( rhs.begin(), rhs.end(), compositeEnv ); 198 currentFinder.get_alternatives().push_front( ResolvExpr::Alternative(new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv, ResolvExpr::sumCost( current ) + matcher->baseCost ) ); 199 } 200 201 TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList &alts ) : spotter(spotter), baseCost( ResolvExpr::sumCost( alts ) ) { 202 assert( ! alts.empty() ); 203 // combine argument environments into combined expression environment 204 simpleCombineEnvironments( alts.begin(), alts.end(), compositeEnv ); 205 206 ResolvExpr::Alternative lhsAlt = alts.front(); 207 // explode is aware of casts - ensure every LHS expression is sent into explode with a reference cast 208 if ( ! dynamic_cast< CastExpr * >( lhsAlt.expr ) ) { 209 lhsAlt.expr = new CastExpr( lhsAlt.expr, new ReferenceType( Type::Qualifiers(), lhsAlt.expr->get_result()->clone() ) ); 210 } 211 212 // explode the lhs so that each field of the tuple-valued-expr is assigned. 213 explode( lhsAlt, spotter.currentFinder.get_indexer(), back_inserter(lhs), true ); 214 215 for ( ResolvExpr::Alternative & alt : lhs ) { 216 // every LHS value must be a reference - some come in with a cast expression, if it doesn't just cast to reference here. 217 if ( ! dynamic_cast< ReferenceType * >( alt.expr->get_result() ) ) { 218 alt.expr = new CastExpr( alt.expr, new ReferenceType( Type::Qualifiers(), alt.expr->get_result()->clone() ) ); 219 } 220 } 221 } 222 223 TupleAssignSpotter::MassAssignMatcher::MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts ) : Matcher( spotter, alts ) { 224 assert( alts.size() == 1 || alts.size() == 2 ); 225 if ( alts.size() == 2 ) { 226 rhs.push_back( alts.back() ); 227 } 228 } 229 230 TupleAssignSpotter::MultipleAssignMatcher::MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts ) : Matcher( spotter, alts ) { 231 // explode the rhs so that each field of the tuple-valued-expr is assigned. 232 explode( std::next(alts.begin(), 1), alts.end(), spotter.currentFinder.get_indexer(), back_inserter(rhs), true ); 264 233 } 265 234 -
src/Tuples/Tuples.h
r0fe4e62 rf5c3b6c 17 17 18 18 #include <string> 19 #include <vector>20 19 21 20 #include "SynTree/Expression.h" … … 27 26 namespace Tuples { 28 27 // TupleAssignment.cc 29 void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * assign, 30 std::vector< ResolvExpr::AlternativeFinder >& args ); 31 28 void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * assign, const std::list<ResolvExpr::AltList> & possibilities ); 29 32 30 // TupleExpansion.cc 33 31 /// expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate -
src/benchmark/Makefile.am
r0fe4e62 rf5c3b6c 23 23 STATS = ${TOOLSDIR}stat.py 24 24 repeats = 30 25 TIME_FORMAT = "%E"26 PRINT_FORMAT = '%20s\t'27 25 28 26 .NOTPARALLEL: … … 30 28 noinst_PROGRAMS = 31 29 32 all : ctxswitch$(EXEEXT) mutex$(EXEEXT) signal$(EXEEXT) waitfor$(EXEEXT) creation$(EXEEXT) 30 bench$(EXEEXT) : 31 @for ccflags in "-debug" "-nodebug"; do \ 32 echo ${CC} ${AM_CFLAGS} ${CFLAGS} ${ccflags} @CFA_FLAGS@ -lrt bench.c;\ 33 ${CC} ${AM_CFLAGS} ${CFLAGS} $${ccflags} -lrt bench.c;\ 34 ./a.out ; \ 35 done ; \ 36 rm -f ./a.out ; 33 37 34 %.run : %$(EXEEXT) ${REPEAT} 35 @rm -f .result.log 36 @echo "------------------------------------------------------" 37 @echo $< 38 @${REPEAT} ${repeats} ./a.out | tee -a .result.log 39 @${STATS} .result.log 40 @echo "------------------------------------------------------" 41 @rm -f a.out .result.log 42 43 %.runquiet : 44 @+make $(basename $@) 38 csv-data$(EXEEXT): 39 @${CC} ${AM_CFLAGS} ${CFLAGS} ${ccflags} @CFA_FLAGS@ -nodebug -lrt -quiet -DN=50000000 csv-data.c 45 40 @./a.out 46 @rm -f a.out 47 48 %.make : 49 @printf "${PRINT_FORMAT}" $(basename $(subst compile-,,$@)) 50 @+/usr/bin/time -f ${TIME_FORMAT} make $(basename $@) 2>&1 51 52 ${REPEAT} : 53 @+make -C ${TOOLSDIR} repeat 54 55 ## ========================================================================================================= 56 57 jenkins$(EXEEXT): 58 @echo "{" 59 @echo -e '\t"githash": "'${githash}'",' 60 @echo -e '\t"arch": "' ${arch} '",' 61 @echo -e '\t"compile": {' 62 @+make compile TIME_FORMAT='%e,' PRINT_FORMAT='\t\t\"%s\" :' 63 @echo -e '\t\t"dummy" : {}' 64 @echo -e '\t},' 65 @echo -e '\t"ctxswitch": {' 66 @echo -en '\t\t"coroutine":' 67 @+make ctxswitch-cfa_coroutine.runquiet 68 @echo -en '\t\t,"thread":' 69 @+make ctxswitch-cfa_thread.runquiet 70 @echo -e '\t},' 71 @echo -e '\t"mutex": [' 72 @echo -en '\t\t' 73 @+make mutex-cfa1.runquiet 74 @echo -en '\t\t,' 75 @+make mutex-cfa2.runquiet 76 @echo -e '\t],' 77 @echo -e '\t"scheduling": [' 78 @echo -en '\t\t' 79 @+make signal-cfa1.runquiet 80 @echo -en '\t\t,' 81 @+make signal-cfa2.runquiet 82 @echo -en '\t\t,' 83 @+make waitfor-cfa1.runquiet 84 @echo -en '\t\t,' 85 @+make waitfor-cfa2.runquiet 86 @echo -e '\n\t],' 87 @echo -e '\t"epoch": ' $(shell date +%s) 88 @echo "}" 41 @rm -f ./a.out 89 42 90 43 ## ========================================================================================================= … … 97 50 98 51 ctxswitch-cfa_coroutine$(EXEEXT): 99 @${CC} ctxswitch/cfa_cor.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}52 ${CC} ctxswitch/cfa_cor.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 100 53 101 54 ctxswitch-cfa_thread$(EXEEXT): 102 @${CC} ctxswitch/cfa_thrd.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}55 ${CC} ctxswitch/cfa_thrd.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 103 56 104 57 ctxswitch-upp_coroutine$(EXEEXT): 105 @u++ ctxswitch/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}58 u++ ctxswitch/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 106 59 107 60 ctxswitch-upp_thread$(EXEEXT): 108 @u++ ctxswitch/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}61 u++ ctxswitch/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 109 62 110 63 ctxswitch-pthread$(EXEEXT): 111 @@BACKEND_CC@ ctxswitch/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 64 @BACKEND_CC@ ctxswitch/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 65 66 ## ========================================================================================================= 67 creation$(EXEEXT) :\ 68 creation-pthread.run \ 69 creation-cfa_coroutine.run \ 70 creation-cfa_thread.run \ 71 creation-upp_coroutine.run \ 72 creation-upp_thread.run 73 74 creation-cfa_coroutine$(EXEEXT): 75 ${CC} creation/cfa_cor.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 76 77 creation-cfa_thread$(EXEEXT): 78 ${CC} creation/cfa_thrd.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 79 80 creation-upp_coroutine$(EXEEXT): 81 u++ creation/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 82 83 creation-upp_thread$(EXEEXT): 84 u++ creation/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 85 86 creation-pthread$(EXEEXT): 87 @BACKEND_CC@ creation/pthreads.c -DBENCH_N=250000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 112 88 113 89 ## ========================================================================================================= … … 121 97 122 98 mutex-function$(EXEEXT): 123 @ @BACKEND_CC@ mutex/function.c -DBENCH_N=500000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags}99 @BACKEND_CC@ mutex/function.c -DBENCH_N=500000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 124 100 125 101 mutex-pthread_lock$(EXEEXT): 126 @ @BACKEND_CC@ mutex/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags}102 @BACKEND_CC@ mutex/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 127 103 128 104 mutex-upp$(EXEEXT): 129 @u++ mutex/upp.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}105 u++ mutex/upp.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 130 106 131 107 mutex-cfa1$(EXEEXT): 132 @${CC} mutex/cfa1.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}108 ${CC} mutex/cfa1.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 133 109 134 110 mutex-cfa2$(EXEEXT): 135 @${CC} mutex/cfa2.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}111 ${CC} mutex/cfa2.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 136 112 137 113 mutex-cfa4$(EXEEXT): 138 @${CC} mutex/cfa4.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}114 ${CC} mutex/cfa4.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 139 115 140 116 ## ========================================================================================================= … … 146 122 147 123 signal-upp$(EXEEXT): 148 @u++ schedint/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}124 u++ schedint/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 149 125 150 126 signal-cfa1$(EXEEXT): 151 @${CC} schedint/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}127 ${CC} schedint/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 152 128 153 129 signal-cfa2$(EXEEXT): 154 @${CC} schedint/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}130 ${CC} schedint/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 155 131 156 132 signal-cfa4$(EXEEXT): 157 @${CC} schedint/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}133 ${CC} schedint/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 158 134 159 135 ## ========================================================================================================= … … 165 141 166 142 waitfor-upp$(EXEEXT): 167 @u++ schedext/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}143 u++ schedext/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 168 144 169 145 waitfor-cfa1$(EXEEXT): 170 @${CC} schedext/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}146 ${CC} schedext/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 171 147 172 148 waitfor-cfa2$(EXEEXT): 173 @${CC} schedext/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}149 ${CC} schedext/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 174 150 175 151 waitfor-cfa4$(EXEEXT): 176 @${CC} schedext/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 177 178 ## ========================================================================================================= 179 creation$(EXEEXT) :\ 180 creation-pthread.run \ 181 creation-cfa_coroutine.run \ 182 creation-cfa_coroutine_eager.run \ 183 creation-cfa_thread.run \ 184 creation-upp_coroutine.run \ 185 creation-upp_thread.run 186 187 creation-cfa_coroutine$(EXEEXT): 188 @${CC} creation/cfa_cor.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 189 190 creation-cfa_coroutine_eager$(EXEEXT): 191 @${CC} creation/cfa_cor.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} -DEAGER 192 193 creation-cfa_thread$(EXEEXT): 194 @${CC} creation/cfa_thrd.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 195 196 creation-upp_coroutine$(EXEEXT): 197 @u++ creation/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 198 199 creation-upp_thread$(EXEEXT): 200 @u++ creation/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 201 202 creation-pthread$(EXEEXT): 203 @@BACKEND_CC@ creation/pthreads.c -DBENCH_N=250000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 152 ${CC} schedext/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 204 153 205 154 ## ========================================================================================================= 206 155 207 compile$(EXEEXT) :\ 208 compile-array.make \ 209 compile-attributes.make \ 210 compile-empty.make \ 211 compile-expression.make \ 212 compile-io.make \ 213 compile-monitor.make \ 214 compile-operators.make \ 215 compile-typeof.make 156 %.run : %$(EXEEXT) ${REPEAT} 157 @rm -f .result.log 158 @echo "------------------------------------------------------" 159 @echo $< 160 @${REPEAT} ${repeats} ./a.out | tee -a .result.log 161 @${STATS} .result.log 162 @echo "------------------------------------------------------" 163 @rm -f a.out .result.log 216 164 217 218 compile-array$(EXEEXT): 219 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/array.c 220 221 compile-attributes$(EXEEXT): 222 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/attributes.c 223 224 compile-empty$(EXEEXT): 225 @${CC} -nodebug -quiet -fsyntax-only -w compile/empty.c 226 227 compile-expression$(EXEEXT): 228 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/expression.c 229 230 compile-io$(EXEEXT): 231 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/io.c 232 233 compile-monitor$(EXEEXT): 234 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/monitor.c 235 236 compile-operators$(EXEEXT): 237 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/operators.c 238 239 compile-thread$(EXEEXT): 240 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/thread.c 241 242 compile-typeof$(EXEEXT): 243 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/typeof.c 244 165 ${REPEAT} : 166 @+make -C ${TOOLSDIR} repeat -
src/benchmark/Makefile.in
r0fe4e62 rf5c3b6c 124 124 esac 125 125 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) 126 am__DIST_COMMON = $(srcdir)/Makefile.in compile126 am__DIST_COMMON = $(srcdir)/Makefile.in 127 127 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) 128 128 ACLOCAL = @ACLOCAL@ … … 253 253 STATS = ${TOOLSDIR}stat.py 254 254 repeats = 30 255 TIME_FORMAT = "%E"256 PRINT_FORMAT = '%20s\t'257 255 all: all-am 258 256 … … 446 444 .NOTPARALLEL: 447 445 448 all : ctxswitch$(EXEEXT) mutex$(EXEEXT) signal$(EXEEXT) waitfor$(EXEEXT) creation$(EXEEXT) 446 bench$(EXEEXT) : 447 @for ccflags in "-debug" "-nodebug"; do \ 448 echo ${CC} ${AM_CFLAGS} ${CFLAGS} ${ccflags} @CFA_FLAGS@ -lrt bench.c;\ 449 ${CC} ${AM_CFLAGS} ${CFLAGS} $${ccflags} -lrt bench.c;\ 450 ./a.out ; \ 451 done ; \ 452 rm -f ./a.out ; 453 454 csv-data$(EXEEXT): 455 @${CC} ${AM_CFLAGS} ${CFLAGS} ${ccflags} @CFA_FLAGS@ -nodebug -lrt -quiet -DN=50000000 csv-data.c 456 @./a.out 457 @rm -f ./a.out 458 459 ctxswitch$(EXEEXT): \ 460 ctxswitch-pthread.run \ 461 ctxswitch-cfa_coroutine.run \ 462 ctxswitch-cfa_thread.run \ 463 ctxswitch-upp_coroutine.run \ 464 ctxswitch-upp_thread.run 465 466 ctxswitch-cfa_coroutine$(EXEEXT): 467 ${CC} ctxswitch/cfa_cor.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 468 469 ctxswitch-cfa_thread$(EXEEXT): 470 ${CC} ctxswitch/cfa_thrd.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 471 472 ctxswitch-upp_coroutine$(EXEEXT): 473 u++ ctxswitch/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 474 475 ctxswitch-upp_thread$(EXEEXT): 476 u++ ctxswitch/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 477 478 ctxswitch-pthread$(EXEEXT): 479 @BACKEND_CC@ ctxswitch/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 480 481 creation$(EXEEXT) :\ 482 creation-pthread.run \ 483 creation-cfa_coroutine.run \ 484 creation-cfa_thread.run \ 485 creation-upp_coroutine.run \ 486 creation-upp_thread.run 487 488 creation-cfa_coroutine$(EXEEXT): 489 ${CC} creation/cfa_cor.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 490 491 creation-cfa_thread$(EXEEXT): 492 ${CC} creation/cfa_thrd.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 493 494 creation-upp_coroutine$(EXEEXT): 495 u++ creation/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 496 497 creation-upp_thread$(EXEEXT): 498 u++ creation/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 499 500 creation-pthread$(EXEEXT): 501 @BACKEND_CC@ creation/pthreads.c -DBENCH_N=250000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 502 503 mutex$(EXEEXT) :\ 504 mutex-function.run \ 505 mutex-pthread_lock.run \ 506 mutex-upp.run \ 507 mutex-cfa1.run \ 508 mutex-cfa2.run \ 509 mutex-cfa4.run 510 511 mutex-function$(EXEEXT): 512 @BACKEND_CC@ mutex/function.c -DBENCH_N=500000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 513 514 mutex-pthread_lock$(EXEEXT): 515 @BACKEND_CC@ mutex/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags} 516 517 mutex-upp$(EXEEXT): 518 u++ mutex/upp.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 519 520 mutex-cfa1$(EXEEXT): 521 ${CC} mutex/cfa1.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 522 523 mutex-cfa2$(EXEEXT): 524 ${CC} mutex/cfa2.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 525 526 mutex-cfa4$(EXEEXT): 527 ${CC} mutex/cfa4.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 528 529 signal$(EXEEXT) :\ 530 signal-upp.run \ 531 signal-cfa1.run \ 532 signal-cfa2.run \ 533 signal-cfa4.run 534 535 signal-upp$(EXEEXT): 536 u++ schedint/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 537 538 signal-cfa1$(EXEEXT): 539 ${CC} schedint/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 540 541 signal-cfa2$(EXEEXT): 542 ${CC} schedint/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 543 544 signal-cfa4$(EXEEXT): 545 ${CC} schedint/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 546 547 waitfor$(EXEEXT) :\ 548 waitfor-upp.run \ 549 waitfor-cfa1.run \ 550 waitfor-cfa2.run \ 551 waitfor-cfa4.run 552 553 waitfor-upp$(EXEEXT): 554 u++ schedext/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags} 555 556 waitfor-cfa1$(EXEEXT): 557 ${CC} schedext/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 558 559 waitfor-cfa2$(EXEEXT): 560 ${CC} schedext/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 561 562 waitfor-cfa4$(EXEEXT): 563 ${CC} schedext/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} 449 564 450 565 %.run : %$(EXEEXT) ${REPEAT} … … 457 572 @rm -f a.out .result.log 458 573 459 %.runquiet :460 @+make $(basename $@)461 @./a.out462 @rm -f a.out463 464 %.make :465 @printf "${PRINT_FORMAT}" $(basename $(subst compile-,,$@))466 @+/usr/bin/time -f ${TIME_FORMAT} make $(basename $@) 2>&1467 468 574 ${REPEAT} : 469 575 @+make -C ${TOOLSDIR} repeat 470 471 jenkins$(EXEEXT):472 @echo "{"473 @echo -e '\t"githash": "'${githash}'",'474 @echo -e '\t"arch": "' ${arch} '",'475 @echo -e '\t"compile": {'476 @+make compile TIME_FORMAT='%e,' PRINT_FORMAT='\t\t\"%s\" :'477 @echo -e '\t\t"dummy" : {}'478 @echo -e '\t},'479 @echo -e '\t"ctxswitch": {'480 @echo -en '\t\t"coroutine":'481 @+make ctxswitch-cfa_coroutine.runquiet482 @echo -en '\t\t,"thread":'483 @+make ctxswitch-cfa_thread.runquiet484 @echo -e '\t},'485 @echo -e '\t"mutex": ['486 @echo -en '\t\t'487 @+make mutex-cfa1.runquiet488 @echo -en '\t\t,'489 @+make mutex-cfa2.runquiet490 @echo -e '\t],'491 @echo -e '\t"scheduling": ['492 @echo -en '\t\t'493 @+make signal-cfa1.runquiet494 @echo -en '\t\t,'495 @+make signal-cfa2.runquiet496 @echo -en '\t\t,'497 @+make waitfor-cfa1.runquiet498 @echo -en '\t\t,'499 @+make waitfor-cfa2.runquiet500 @echo -e '\n\t],'501 @echo -e '\t"epoch": ' $(shell date +%s)502 @echo "}"503 504 ctxswitch$(EXEEXT): \505 ctxswitch-pthread.run \506 ctxswitch-cfa_coroutine.run \507 ctxswitch-cfa_thread.run \508 ctxswitch-upp_coroutine.run \509 ctxswitch-upp_thread.run510 511 ctxswitch-cfa_coroutine$(EXEEXT):512 @${CC} ctxswitch/cfa_cor.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}513 514 ctxswitch-cfa_thread$(EXEEXT):515 @${CC} ctxswitch/cfa_thrd.c -DBENCH_N=50000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}516 517 ctxswitch-upp_coroutine$(EXEEXT):518 @u++ ctxswitch/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}519 520 ctxswitch-upp_thread$(EXEEXT):521 @u++ ctxswitch/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}522 523 ctxswitch-pthread$(EXEEXT):524 @@BACKEND_CC@ ctxswitch/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags}525 526 mutex$(EXEEXT) :\527 mutex-function.run \528 mutex-pthread_lock.run \529 mutex-upp.run \530 mutex-cfa1.run \531 mutex-cfa2.run \532 mutex-cfa4.run533 534 mutex-function$(EXEEXT):535 @@BACKEND_CC@ mutex/function.c -DBENCH_N=500000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags}536 537 mutex-pthread_lock$(EXEEXT):538 @@BACKEND_CC@ mutex/pthreads.c -DBENCH_N=50000000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags}539 540 mutex-upp$(EXEEXT):541 @u++ mutex/upp.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}542 543 mutex-cfa1$(EXEEXT):544 @${CC} mutex/cfa1.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}545 546 mutex-cfa2$(EXEEXT):547 @${CC} mutex/cfa2.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}548 549 mutex-cfa4$(EXEEXT):550 @${CC} mutex/cfa4.c -DBENCH_N=5000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}551 552 signal$(EXEEXT) :\553 signal-upp.run \554 signal-cfa1.run \555 signal-cfa2.run \556 signal-cfa4.run557 558 signal-upp$(EXEEXT):559 @u++ schedint/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}560 561 signal-cfa1$(EXEEXT):562 @${CC} schedint/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}563 564 signal-cfa2$(EXEEXT):565 @${CC} schedint/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}566 567 signal-cfa4$(EXEEXT):568 @${CC} schedint/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}569 570 waitfor$(EXEEXT) :\571 waitfor-upp.run \572 waitfor-cfa1.run \573 waitfor-cfa2.run \574 waitfor-cfa4.run575 576 waitfor-upp$(EXEEXT):577 @u++ schedext/upp.cc -DBENCH_N=5000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}578 579 waitfor-cfa1$(EXEEXT):580 @${CC} schedext/cfa1.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}581 582 waitfor-cfa2$(EXEEXT):583 @${CC} schedext/cfa2.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}584 585 waitfor-cfa4$(EXEEXT):586 @${CC} schedext/cfa4.c -DBENCH_N=500000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}587 588 creation$(EXEEXT) :\589 creation-pthread.run \590 creation-cfa_coroutine.run \591 creation-cfa_coroutine_eager.run \592 creation-cfa_thread.run \593 creation-upp_coroutine.run \594 creation-upp_thread.run595 596 creation-cfa_coroutine$(EXEEXT):597 @${CC} creation/cfa_cor.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}598 599 creation-cfa_coroutine_eager$(EXEEXT):600 @${CC} creation/cfa_cor.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags} -DEAGER601 602 creation-cfa_thread$(EXEEXT):603 @${CC} creation/cfa_thrd.c -DBENCH_N=10000000 -I. -nodebug -lrt -quiet @CFA_FLAGS@ ${AM_CFLAGS} ${CFLAGS} ${ccflags}604 605 creation-upp_coroutine$(EXEEXT):606 @u++ creation/upp_cor.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}607 608 creation-upp_thread$(EXEEXT):609 @u++ creation/upp_thrd.cc -DBENCH_N=50000000 -I. -nodebug -lrt -quiet ${AM_CFLAGS} ${CFLAGS} ${ccflags}610 611 creation-pthread$(EXEEXT):612 @@BACKEND_CC@ creation/pthreads.c -DBENCH_N=250000 -I. -lrt -pthread ${AM_CFLAGS} ${CFLAGS} ${ccflags}613 614 compile$(EXEEXT) :\615 compile-array.make \616 compile-attributes.make \617 compile-empty.make \618 compile-expression.make \619 compile-io.make \620 compile-monitor.make \621 compile-operators.make \622 compile-typeof.make623 624 compile-array$(EXEEXT):625 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/array.c626 627 compile-attributes$(EXEEXT):628 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/attributes.c629 630 compile-empty$(EXEEXT):631 @${CC} -nodebug -quiet -fsyntax-only -w compile/empty.c632 633 compile-expression$(EXEEXT):634 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/expression.c635 636 compile-io$(EXEEXT):637 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/io.c638 639 compile-monitor$(EXEEXT):640 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/monitor.c641 642 compile-operators$(EXEEXT):643 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/operators.c644 645 compile-thread$(EXEEXT):646 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/thread.c647 648 compile-typeof$(EXEEXT):649 @${CC} -nodebug -quiet -fsyntax-only -w ../tests/typeof.c650 576 651 577 # Tell versions [3.59,3.63) of GNU make to not export all variables. -
src/benchmark/creation/cfa_cor.c
r0fe4e62 rf5c3b6c 5 5 6 6 coroutine MyCoroutine {}; 7 void ?{} (MyCoroutine & this) { 8 #ifdef EAGER 9 prime(this); 10 #endif 11 } 7 void ?{} (MyCoroutine & this) { prime(this); } 12 8 void main(MyCoroutine & this) {} 13 9 -
src/benchmark/csv-data.c
r0fe4e62 rf5c3b6c 28 28 // coroutine context switch 29 29 long long int measure_coroutine() { 30 const unsigned int NoOfTimes = 50000000;30 const unsigned int NoOfTimes = N; 31 31 long long int StartTime, EndTime; 32 32 … … 43 43 // thread context switch 44 44 long long int measure_thread() { 45 const unsigned int NoOfTimes = 50000000;45 const unsigned int NoOfTimes = N; 46 46 long long int StartTime, EndTime; 47 47 … … 61 61 62 62 long long int measure_1_monitor_entry() { 63 const unsigned int NoOfTimes = 5000000;63 const unsigned int NoOfTimes = N; 64 64 long long int StartTime, EndTime; 65 65 mon_t mon; … … 79 79 80 80 long long int measure_2_monitor_entry() { 81 const unsigned int NoOfTimes = 5000000;81 const unsigned int NoOfTimes = N; 82 82 long long int StartTime, EndTime; 83 83 mon_t mon1, mon2; … … 94 94 //----------------------------------------------------------------------------- 95 95 // single internal sched entry 96 const unsigned int NoOfTimes = 500000;97 98 96 mon_t mon1; 99 97 … … 109 107 110 108 void side1A( mon_t & mutex a, long long int * out ) { 111 const unsigned int NoOfTimes = 500000; 112 long long int StartTime, EndTime; 113 114 StartTime = Time(); 115 for( int i = 0;; i++ ) { 116 signal(cond1a); 117 if( i > NoOfTimes ) break; 118 wait(cond1b); 119 } 120 EndTime = Time(); 121 122 *out = ( EndTime - StartTime ) / NoOfTimes; 109 long long int StartTime, EndTime; 110 111 StartTime = Time(); 112 for( int i = 0;; i++ ) { 113 signal(&cond1a); 114 if( i > N ) break; 115 wait(&cond1b); 116 } 117 EndTime = Time(); 118 119 *out = ( EndTime - StartTime ) / N; 123 120 } 124 121 125 122 void side1B( mon_t & mutex a ) { 126 123 for( int i = 0;; i++ ) { 127 signal( cond1b);128 if( i > N ) break; 129 wait( cond1a);124 signal(&cond1b); 125 if( i > N ) break; 126 wait(&cond1a); 130 127 } 131 128 } … … 144 141 145 142 //----------------------------------------------------------------------------- 146 // multi internal sched 143 // multi internal sched entry 147 144 mon_t mon2; 148 145 … … 158 155 159 156 void side2A( mon_t & mutex a, mon_t & mutex b, long long int * out ) { 160 const unsigned int NoOfTimes = 500000; 161 long long int StartTime, EndTime; 162 163 StartTime = Time(); 164 for( int i = 0;; i++ ) { 165 signal(cond2a); 166 if( i > NoOfTimes ) break; 167 wait(cond2b); 168 } 169 EndTime = Time(); 170 171 *out = ( EndTime - StartTime ) / NoOfTimes; 157 long long int StartTime, EndTime; 158 159 StartTime = Time(); 160 for( int i = 0;; i++ ) { 161 signal(&cond2a); 162 if( i > N ) break; 163 wait(&cond2b); 164 } 165 EndTime = Time(); 166 167 *out = ( EndTime - StartTime ) / N; 172 168 } 173 169 174 170 void side2B( mon_t & mutex a, mon_t & mutex b ) { 175 171 for( int i = 0;; i++ ) { 176 signal( cond2b);177 if( i > N ) break; 178 wait( cond2a);172 signal(&cond2b); 173 if( i > N ) break; 174 wait(&cond2a); 179 175 } 180 176 } … … 193 189 194 190 //----------------------------------------------------------------------------- 195 // single external sched196 197 volatile int go = 0;198 199 void __attribute__((noinline)) call( mon_t & mutex m1 ) {}200 201 long long int __attribute__((noinline)) wait( mon_t & mutex m1 ) {202 go = 1;203 const unsigned int NoOfTimes = 5000000;204 long long int StartTime, EndTime;205 206 StartTime = Time();207 for (size_t i = 0; i < NoOfTimes; i++) {208 waitfor(call, m1);209 }210 211 EndTime = Time();212 go = 0;213 return ( EndTime - StartTime ) / NoOfTimes;214 }215 216 thread thrd3 {};217 void ^?{}( thrd3 & mutex this ) {}218 void main( thrd3 & this ) {219 while(go == 0) { yield(); }220 while(go == 1) { call(mon1); }221 222 }223 224 long long int measure_1_sched_ext() {225 go = 0;226 thrd3 t;227 return wait(mon1);228 }229 230 //-----------------------------------------------------------------------------231 // multi external sched232 233 void __attribute__((noinline)) call( mon_t & mutex m1, mon_t & mutex m2 ) {}234 235 long long int __attribute__((noinline)) wait( mon_t & mutex m1, mon_t & mutex m2 ) {236 go = 1;237 const unsigned int NoOfTimes = 5000000;238 long long int StartTime, EndTime;239 240 StartTime = Time();241 for (size_t i = 0; i < NoOfTimes; i++) {242 waitfor(call, m1, m2);243 }244 245 EndTime = Time();246 go = 0;247 return ( EndTime - StartTime ) / NoOfTimes;248 }249 250 thread thrd4 {};251 void ^?{}( thrd4 & mutex this ) {}252 void main( thrd4 & this ) {253 while(go == 0) { yield(); }254 while(go == 1) { call(mon1, mon2); }255 256 }257 258 long long int measure_2_sched_ext() {259 go = 0;260 thrd3 t;261 return wait(mon1, mon2);262 }263 264 //-----------------------------------------------------------------------------265 191 // main loop 266 192 int main() 267 193 { 268 sout | "\tepoch:" | time(NULL) | ',' | endl; 269 sout | "\tctxswitch: {" | endl; 270 sout | "\t\tcoroutine: "| measure_coroutine() | ',' | endl; 271 sout | "\t\tthread:" | measure_thread() | ',' | endl; 272 sout | "\t}," | endl; 273 sout | "\tmutex: [" | measure_1_monitor_entry() | ',' | measure_2_monitor_entry() | "]," | endl; 274 sout | "\tscheduling: ["| measure_1_sched_int() | ',' | measure_2_sched_int() | ',' | 275 measure_1_sched_ext() | ',' | measure_2_sched_ext() | "]," | endl; 276 } 194 sout | time(NULL) | ','; 195 sout | measure_coroutine() | ','; 196 sout | measure_thread() | ','; 197 sout | measure_1_monitor_entry() | ','; 198 sout | measure_2_monitor_entry() | ','; 199 sout | measure_1_sched_int() | ','; 200 sout | measure_2_sched_int() | endl; 201 } -
src/benchmark/schedint/cfa1.c
r0fe4e62 rf5c3b6c 15 15 16 16 void __attribute__((noinline)) call( M & mutex a1 ) { 17 signal( c);17 signal(&c); 18 18 } 19 19 … … 22 22 BENCH( 23 23 for (size_t i = 0; i < n; i++) { 24 wait( c);24 wait(&c); 25 25 }, 26 26 result -
src/benchmark/schedint/cfa2.c
r0fe4e62 rf5c3b6c 15 15 16 16 void __attribute__((noinline)) call( M & mutex a1, M & mutex a2 ) { 17 signal( c);17 signal(&c); 18 18 } 19 19 … … 22 22 BENCH( 23 23 for (size_t i = 0; i < n; i++) { 24 wait( c);24 wait(&c); 25 25 }, 26 26 result -
src/benchmark/schedint/cfa4.c
r0fe4e62 rf5c3b6c 15 15 16 16 void __attribute__((noinline)) call( M & mutex a1, M & mutex a2, M & mutex a3, M & mutex a4 ) { 17 signal( c);17 signal(&c); 18 18 } 19 19 … … 22 22 BENCH( 23 23 for (size_t i = 0; i < n; i++) { 24 wait( c);24 wait(&c); 25 25 }, 26 26 result -
src/libcfa/Makefile.am
r0fe4e62 rf5c3b6c 95 95 96 96 cfa_includedir = $(CFA_INCDIR) 97 nobase_cfa_include_HEADERS = \ 98 ${headers} \ 99 ${stdhdr} \ 100 math \ 101 gmp \ 102 bits/defs.h \ 103 bits/locks.h \ 104 concurrency/invoke.h \ 105 libhdr.h \ 106 libhdr/libalign.h \ 107 libhdr/libdebug.h \ 108 libhdr/libtools.h 97 nobase_cfa_include_HEADERS = ${headers} ${stdhdr} math gmp concurrency/invoke.h 109 98 110 99 CLEANFILES = libcfa-prelude.c -
src/libcfa/Makefile.in
r0fe4e62 rf5c3b6c 264 264 containers/result containers/vector concurrency/coroutine \ 265 265 concurrency/thread concurrency/kernel concurrency/monitor \ 266 ${shell echo stdhdr/*} math gmp bits/defs.h bits/locks.h \ 267 concurrency/invoke.h libhdr.h libhdr/libalign.h \ 268 libhdr/libdebug.h libhdr/libtools.h 266 ${shell echo stdhdr/*} math gmp concurrency/invoke.h 269 267 HEADERS = $(nobase_cfa_include_HEADERS) 270 268 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) … … 432 430 stdhdr = ${shell echo stdhdr/*} 433 431 cfa_includedir = $(CFA_INCDIR) 434 nobase_cfa_include_HEADERS = \ 435 ${headers} \ 436 ${stdhdr} \ 437 math \ 438 gmp \ 439 bits/defs.h \ 440 bits/locks.h \ 441 concurrency/invoke.h \ 442 libhdr.h \ 443 libhdr/libalign.h \ 444 libhdr/libdebug.h \ 445 libhdr/libtools.h 446 432 nobase_cfa_include_HEADERS = ${headers} ${stdhdr} math gmp concurrency/invoke.h 447 433 CLEANFILES = libcfa-prelude.c 448 434 all: all-am -
src/libcfa/concurrency/alarm.c
r0fe4e62 rf5c3b6c 186 186 187 187 disable_interrupts(); 188 lock( event_kernel->lock DEBUG_CTX2 );188 lock( &event_kernel->lock DEBUG_CTX2 ); 189 189 { 190 190 verify( validate( alarms ) ); … … 196 196 } 197 197 } 198 unlock( event_kernel->lock );198 unlock( &event_kernel->lock ); 199 199 this->set = true; 200 200 enable_interrupts( DEBUG_CTX ); … … 203 203 void unregister_self( alarm_node_t * this ) { 204 204 disable_interrupts(); 205 lock( event_kernel->lock DEBUG_CTX2 );205 lock( &event_kernel->lock DEBUG_CTX2 ); 206 206 { 207 207 verify( validate( &event_kernel->alarms ) ); 208 208 remove( &event_kernel->alarms, this ); 209 209 } 210 unlock( event_kernel->lock );210 unlock( &event_kernel->lock ); 211 211 enable_interrupts( DEBUG_CTX ); 212 212 this->set = false; -
src/libcfa/concurrency/coroutine.c
r0fe4e62 rf5c3b6c 156 156 this->limit = (char *)libCeiling( (unsigned long)this->storage, 16 ); // minimum alignment 157 157 } // if 158 assertf( this->size >= MinStackSize, "Stack size % zd provides less than minimum of %d bytes for a stack.", this->size, MinStackSize );158 assertf( this->size >= MinStackSize, "Stack size %d provides less than minimum of %d bytes for a stack.", this->size, MinStackSize ); 159 159 160 160 this->base = (char *)this->limit + this->size; -
src/libcfa/concurrency/invoke.h
r0fe4e62 rf5c3b6c 14 14 // 15 15 16 #include "bits/defs.h"17 #include "bits/locks.h"16 #include <stdbool.h> 17 #include <stdint.h> 18 18 19 19 #ifdef __CFORALL__ … … 25 25 #define _INVOKE_H_ 26 26 27 typedef void (*fptr_t)(); 28 typedef int_fast16_t __lock_size_t; 27 #define unlikely(x) __builtin_expect(!!(x), 0) 28 #define thread_local _Thread_local 29 29 30 struct __thread_queue_t { 31 struct thread_desc * head; 32 struct thread_desc ** tail; 33 }; 30 typedef void (*fptr_t)(); 34 31 35 struct __condition_stack_t { 36 struct __condition_criterion_t * top; 37 }; 32 struct spinlock { 33 volatile int lock; 34 #ifdef __CFA_DEBUG__ 35 const char * prev_name; 36 void* prev_thrd; 37 #endif 38 }; 38 39 39 #ifdef __CFORALL__ 40 extern "Cforall" { 41 void ?{}( struct __thread_queue_t & ); 42 void append( struct __thread_queue_t &, struct thread_desc * ); 43 struct thread_desc * pop_head( struct __thread_queue_t & ); 44 struct thread_desc * remove( struct __thread_queue_t &, struct thread_desc ** ); 40 struct __thread_queue_t { 41 struct thread_desc * head; 42 struct thread_desc ** tail; 43 }; 45 44 46 void ?{}( struct __condition_stack_t & ); 47 void push( struct __condition_stack_t &, struct __condition_criterion_t * ); 48 struct __condition_criterion_t * pop( struct __condition_stack_t & ); 49 } 50 #endif 45 struct __condition_stack_t { 46 struct __condition_criterion_t * top; 47 }; 51 48 52 struct coStack_t { 53 // size of stack 54 size_t size; 49 #ifdef __CFORALL__ 50 extern "Cforall" { 51 void ?{}( struct __thread_queue_t & ); 52 void append( struct __thread_queue_t *, struct thread_desc * ); 53 struct thread_desc * pop_head( struct __thread_queue_t * ); 54 struct thread_desc * remove( struct __thread_queue_t *, struct thread_desc ** ); 55 55 56 // pointer to stack 57 void *storage; 56 void ?{}( struct __condition_stack_t & ); 57 void push( struct __condition_stack_t *, struct __condition_criterion_t * ); 58 struct __condition_criterion_t * pop( struct __condition_stack_t * ); 58 59 59 // stack grows towards stack limit 60 void *limit; 60 void ?{}(spinlock & this); 61 void ^?{}(spinlock & this); 62 } 63 #endif 61 64 62 // base of stack 63 void *base; 65 struct coStack_t { 66 unsigned int size; // size of stack 67 void *storage; // pointer to stack 68 void *limit; // stack grows towards stack limit 69 void *base; // base of stack 70 void *context; // address of cfa_context_t 71 void *top; // address of top of storage 72 bool userStack; // whether or not the user allocated the stack 73 }; 64 74 65 // address of cfa_context_t 66 void *context; 75 enum coroutine_state { Halted, Start, Inactive, Active, Primed }; 67 76 68 // address of top of storage 69 void *top; 77 struct coroutine_desc { 78 struct coStack_t stack; // stack information of the coroutine 79 const char *name; // textual name for coroutine/task, initialized by uC++ generated code 80 int errno_; // copy of global UNIX variable errno 81 enum coroutine_state state; // current execution status for coroutine 82 struct coroutine_desc * starter; // first coroutine to resume this one 83 struct coroutine_desc * last; // last coroutine to resume this one 84 }; 70 85 71 // whether or not the user allocated the stack 72 bool userStack; 73 }; 86 struct __waitfor_mask_t { 87 short * accepted; // the index of the accepted function, -1 if none 88 struct __acceptable_t * clauses; // list of acceptable functions, null if any 89 short size; // number of acceptable functions 90 }; 74 91 75 enum coroutine_state { Halted, Start, Inactive, Active, Primed }; 92 struct monitor_desc { 93 struct spinlock lock; // spinlock to protect internal data 94 struct thread_desc * owner; // current owner of the monitor 95 struct __thread_queue_t entry_queue; // queue of threads that are blocked waiting for the monitor 96 struct __condition_stack_t signal_stack; // stack of conditions to run next once we exit the monitor 97 unsigned int recursion; // monitor routines can be called recursively, we need to keep track of that 98 struct __waitfor_mask_t mask; // mask used to know if some thread is waiting for something while holding the monitor 99 struct __condition_node_t * dtor_node; // node used to signal the dtor in a waitfor dtor 100 }; 76 101 77 struct coroutine_desc { 78 // stack information of the coroutine 79 struct coStack_t stack; 102 struct __monitor_group_t { 103 struct monitor_desc ** list; // currently held monitors 104 short size; // number of currently held monitors 105 fptr_t func; // last function that acquired monitors 106 }; 80 107 81 // textual name for coroutine/task, initialized by uC++ generated code 82 const char *name; 108 struct thread_desc { 109 // Core threading fields 110 struct coroutine_desc self_cor; // coroutine body used to store context 111 struct monitor_desc self_mon; // monitor body used for mutual exclusion 112 struct monitor_desc * self_mon_p; // pointer to monitor with sufficient lifetime for current monitors 113 struct __monitor_group_t monitors; // monitors currently held by this thread 83 114 84 // copy of global UNIX variable errno 85 int errno_; 115 // Link lists fields 116 struct thread_desc * next; // instrusive link field for threads 86 117 87 // current execution status for coroutine88 enum coroutine_state state;89 118 90 // first coroutine to resume this one91 struct coroutine_desc * starter;92 93 // last coroutine to resume this one94 struct coroutine_desc * last;95 };96 97 struct __waitfor_mask_t {98 // the index of the accepted function, -1 if none99 short * accepted;100 101 // list of acceptable functions, null if any102 struct __acceptable_t * clauses;103 104 // number of acceptable functions105 __lock_size_t size;106 };107 108 struct monitor_desc {109 // spinlock to protect internal data110 struct __spinlock_t lock;111 112 // current owner of the monitor113 struct thread_desc * owner;114 115 // queue of threads that are blocked waiting for the monitor116 struct __thread_queue_t entry_queue;117 118 // stack of conditions to run next once we exit the monitor119 struct __condition_stack_t signal_stack;120 121 // monitor routines can be called recursively, we need to keep track of that122 unsigned int recursion;123 124 // mask used to know if some thread is waiting for something while holding the monitor125 struct __waitfor_mask_t mask;126 127 // node used to signal the dtor in a waitfor dtor128 struct __condition_node_t * dtor_node;129 };130 131 struct __monitor_group_t {132 // currently held monitors133 struct monitor_desc ** list;134 135 // number of currently held monitors136 __lock_size_t size;137 138 // last function that acquired monitors139 fptr_t func;140 };141 142 struct thread_desc {143 // Core threading fields144 // coroutine body used to store context145 struct coroutine_desc self_cor;146 147 // monitor body used for mutual exclusion148 struct monitor_desc self_mon;149 150 // pointer to monitor with sufficient lifetime for current monitors151 struct monitor_desc * self_mon_p;152 153 // monitors currently held by this thread154 struct __monitor_group_t monitors;155 156 // Link lists fields157 // instrusive link field for threads158 struct thread_desc * next;159 119 }; 160 120 161 121 #ifdef __CFORALL__ 162 122 extern "Cforall" { 163 static inline monitor_desc * ?[?]( const __monitor_group_t & this, ptrdiff_t index ) {164 return this.list[index];165 }123 static inline monitor_desc * ?[?]( const __monitor_group_t & this, ptrdiff_t index ) { 124 return this.list[index]; 125 } 166 126 167 static inline bool ?==?( const __monitor_group_t & lhs, const __monitor_group_t & rhs ) {168 if( (lhs.list != 0) != (rhs.list != 0) ) return false;169 if( lhs.size != rhs.size ) return false;170 if( lhs.func != rhs.func ) return false;127 static inline bool ?==?( const __monitor_group_t & lhs, const __monitor_group_t & rhs ) { 128 if( (lhs.list != 0) != (rhs.list != 0) ) return false; 129 if( lhs.size != rhs.size ) return false; 130 if( lhs.func != rhs.func ) return false; 171 131 172 // Check that all the monitors match173 for( int i = 0; i < lhs.size; i++ ) {174 // If not a match, check next function175 if( lhs[i] != rhs[i] ) return false;176 }132 // Check that all the monitors match 133 for( int i = 0; i < lhs.size; i++ ) { 134 // If not a match, check next function 135 if( lhs[i] != rhs[i] ) return false; 136 } 177 137 178 return true;179 }180 }181 #endif138 return true; 139 } 140 } 141 #endif 182 142 183 143 #endif //_INVOKE_H_ … … 186 146 #define _INVOKE_PRIVATE_H_ 187 147 188 struct machine_context_t {189 void *SP;190 void *FP;191 void *PC;192 };148 struct machine_context_t { 149 void *SP; 150 void *FP; 151 void *PC; 152 }; 193 153 194 // assembler routines that performs the context switch195 extern void CtxInvokeStub( void );196 void CtxSwitch( void * from, void * to ) asm ("CtxSwitch");154 // assembler routines that performs the context switch 155 extern void CtxInvokeStub( void ); 156 void CtxSwitch( void * from, void * to ) asm ("CtxSwitch"); 197 157 198 #if defined( __x86_64__ )199 #define CtxGet( ctx ) __asm__ ( \200 "movq %%rsp,%0\n" \201 "movq %%rbp,%1\n" \202 : "=rm" (ctx.SP), "=rm" (ctx.FP) )203 #elif defined( __i386__ )204 #define CtxGet( ctx ) __asm__ ( \205 "movl %%esp,%0\n" \206 "movl %%ebp,%1\n" \207 : "=rm" (ctx.SP), "=rm" (ctx.FP) )208 #endif158 #if defined( __x86_64__ ) 159 #define CtxGet( ctx ) __asm__ ( \ 160 "movq %%rsp,%0\n" \ 161 "movq %%rbp,%1\n" \ 162 : "=rm" (ctx.SP), "=rm" (ctx.FP) ) 163 #elif defined( __i386__ ) 164 #define CtxGet( ctx ) __asm__ ( \ 165 "movl %%esp,%0\n" \ 166 "movl %%ebp,%1\n" \ 167 : "=rm" (ctx.SP), "=rm" (ctx.FP) ) 168 #endif 209 169 210 170 #endif //_INVOKE_PRIVATE_H_ -
src/libcfa/concurrency/kernel
r0fe4e62 rf5c3b6c 26 26 //----------------------------------------------------------------------------- 27 27 // Locks 28 // // Lock the spinlock, spin if already acquired 29 // void lock ( spinlock * DEBUG_CTX_PARAM2 ); 30 31 // // Lock the spinlock, yield repeatedly if already acquired 32 // void lock_yield( spinlock * DEBUG_CTX_PARAM2 ); 33 34 // // Lock the spinlock, return false if already acquired 35 // bool try_lock ( spinlock * DEBUG_CTX_PARAM2 ); 36 37 // // Unlock the spinlock 38 // void unlock ( spinlock * ); 28 void lock ( spinlock * DEBUG_CTX_PARAM2 ); // Lock the spinlock, spin if already acquired 29 void lock_yield( spinlock * DEBUG_CTX_PARAM2 ); // Lock the spinlock, yield repeatedly if already acquired 30 bool try_lock ( spinlock * DEBUG_CTX_PARAM2 ); // Lock the spinlock, return false if already acquired 31 void unlock ( spinlock * ); // Unlock the spinlock 39 32 40 33 struct semaphore { 41 __spinlock_tlock;34 spinlock lock; 42 35 int count; 43 36 __thread_queue_t waiting; … … 46 39 void ?{}(semaphore & this, int count = 1); 47 40 void ^?{}(semaphore & this); 48 void P (semaphore &this);49 void V (semaphore &this);41 void P(semaphore * this); 42 void V(semaphore * this); 50 43 51 44 … … 53 46 // Cluster 54 47 struct cluster { 55 // Ready queue locks 56 __spinlock_t ready_queue_lock; 57 58 // Ready queue for threads 59 __thread_queue_t ready_queue; 60 61 // Preemption rate on this cluster 62 unsigned long long int preemption; 48 spinlock ready_queue_lock; // Ready queue locks 49 __thread_queue_t ready_queue; // Ready queue for threads 50 unsigned long long int preemption; // Preemption rate on this cluster 63 51 }; 64 52 65 void ?{} (cluster & this);53 void ?{}(cluster & this); 66 54 void ^?{}(cluster & this); 67 55 … … 74 62 FinishOpCode action_code; 75 63 thread_desc * thrd; 76 __spinlock_t* lock;77 __spinlock_t** locks;64 spinlock * lock; 65 spinlock ** locks; 78 66 unsigned short lock_count; 79 67 thread_desc ** thrds; … … 91 79 struct processor { 92 80 // Main state 93 // Coroutine ctx who does keeps the state of the processor 94 struct processorCtx_t * runner; 95 96 // Cluster from which to get threads 97 cluster * cltr; 98 99 // Handle to pthreads 100 pthread_t kernel_thread; 81 struct processorCtx_t * runner; // Coroutine ctx who does keeps the state of the processor 82 cluster * cltr; // Cluster from which to get threads 83 pthread_t kernel_thread; // Handle to pthreads 101 84 102 85 // Termination 103 // Set to true to notify the processor should terminate 104 volatile bool do_terminate; 105 106 // Termination synchronisation 107 semaphore terminated; 86 volatile bool do_terminate; // Set to true to notify the processor should terminate 87 semaphore terminated; // Termination synchronisation 108 88 109 89 // RunThread data 110 // Action to do after a thread is ran 111 struct FinishAction finish; 90 struct FinishAction finish; // Action to do after a thread is ran 112 91 113 92 // Preemption data 114 // Node which is added in the discrete event simulaiton 115 struct alarm_node_t * preemption_alarm; 116 117 // If true, a preemption was triggered in an unsafe region, the processor must preempt as soon as possible 118 bool pending_preemption; 93 struct alarm_node_t * preemption_alarm; // Node which is added in the discrete event simulaiton 94 bool pending_preemption; // If true, a preemption was triggered in an unsafe region, the processor must preempt as soon as possible 119 95 120 96 #ifdef __CFA_DEBUG__ 121 // Last function to enable preemption on this processor 122 char * last_enable; 97 char * last_enable; // Last function to enable preemption on this processor 123 98 #endif 124 99 }; 125 100 126 void ?{}(processor & this);127 void ?{}(processor & this, cluster * cltr);101 void ?{}(processor & this); 102 void ?{}(processor & this, cluster * cltr); 128 103 void ^?{}(processor & this); 129 104 -
src/libcfa/concurrency/kernel.c
r0fe4e62 rf5c3b6c 158 158 LIB_DEBUG_PRINT_SAFE("Kernel : core %p signaling termination\n", &this); 159 159 this.do_terminate = true; 160 P( this.terminated );160 P( &this.terminated ); 161 161 pthread_join( this.kernel_thread, NULL ); 162 162 } … … 216 216 } 217 217 218 V( this->terminated );218 V( &this->terminated ); 219 219 220 220 LIB_DEBUG_PRINT_SAFE("Kernel : core %p terminated\n", this); … … 242 242 void finishRunning(processor * this) { 243 243 if( this->finish.action_code == Release ) { 244 unlock( *this->finish.lock );244 unlock( this->finish.lock ); 245 245 } 246 246 else if( this->finish.action_code == Schedule ) { … … 248 248 } 249 249 else if( this->finish.action_code == Release_Schedule ) { 250 unlock( *this->finish.lock );250 unlock( this->finish.lock ); 251 251 ScheduleThread( this->finish.thrd ); 252 252 } 253 253 else if( this->finish.action_code == Release_Multi ) { 254 254 for(int i = 0; i < this->finish.lock_count; i++) { 255 unlock( *this->finish.locks[i] );255 unlock( this->finish.locks[i] ); 256 256 } 257 257 } 258 258 else if( this->finish.action_code == Release_Multi_Schedule ) { 259 259 for(int i = 0; i < this->finish.lock_count; i++) { 260 unlock( *this->finish.locks[i] );260 unlock( this->finish.locks[i] ); 261 261 } 262 262 for(int i = 0; i < this->finish.thrd_count; i++) { … … 334 334 verifyf( thrd->next == NULL, "Expected null got %p", thrd->next ); 335 335 336 lock( this_processor->cltr->ready_queue_lock DEBUG_CTX2 );337 append( this_processor->cltr->ready_queue, thrd );338 unlock( this_processor->cltr->ready_queue_lock );336 lock( &this_processor->cltr->ready_queue_lock DEBUG_CTX2 ); 337 append( &this_processor->cltr->ready_queue, thrd ); 338 unlock( &this_processor->cltr->ready_queue_lock ); 339 339 340 340 verify( disable_preempt_count > 0 ); … … 343 343 thread_desc * nextThread(cluster * this) { 344 344 verify( disable_preempt_count > 0 ); 345 lock( this->ready_queue_lock DEBUG_CTX2 );346 thread_desc * head = pop_head( this->ready_queue );347 unlock( this->ready_queue_lock );345 lock( &this->ready_queue_lock DEBUG_CTX2 ); 346 thread_desc * head = pop_head( &this->ready_queue ); 347 unlock( &this->ready_queue_lock ); 348 348 verify( disable_preempt_count > 0 ); 349 349 return head; … … 358 358 } 359 359 360 void BlockInternal( __spinlock_t* lock ) {360 void BlockInternal( spinlock * lock ) { 361 361 disable_interrupts(); 362 362 this_processor->finish.action_code = Release; … … 384 384 } 385 385 386 void BlockInternal( __spinlock_t* lock, thread_desc * thrd ) {386 void BlockInternal( spinlock * lock, thread_desc * thrd ) { 387 387 assert(thrd); 388 388 disable_interrupts(); … … 398 398 } 399 399 400 void BlockInternal( __spinlock_t * locks [], unsigned short count) {400 void BlockInternal(spinlock ** locks, unsigned short count) { 401 401 disable_interrupts(); 402 402 this_processor->finish.action_code = Release_Multi; … … 411 411 } 412 412 413 void BlockInternal( __spinlock_t * locks [], unsigned short lock_count, thread_desc * thrds [], unsigned short thrd_count) {413 void BlockInternal(spinlock ** locks, unsigned short lock_count, thread_desc ** thrds, unsigned short thrd_count) { 414 414 disable_interrupts(); 415 415 this_processor->finish.action_code = Release_Multi_Schedule; … … 426 426 } 427 427 428 void LeaveThread( __spinlock_t* lock, thread_desc * thrd) {428 void LeaveThread(spinlock * lock, thread_desc * thrd) { 429 429 verify( disable_preempt_count > 0 ); 430 430 this_processor->finish.action_code = thrd ? Release_Schedule : Release; … … 516 516 } 517 517 518 static __spinlock_tkernel_abort_lock;519 static __spinlock_tkernel_debug_lock;518 static spinlock kernel_abort_lock; 519 static spinlock kernel_debug_lock; 520 520 static bool kernel_abort_called = false; 521 521 … … 523 523 // abort cannot be recursively entered by the same or different processors because all signal handlers return when 524 524 // the globalAbort flag is true. 525 lock( kernel_abort_lock DEBUG_CTX2 );525 lock( &kernel_abort_lock DEBUG_CTX2 ); 526 526 527 527 // first task to abort ? 528 528 if ( !kernel_abort_called ) { // not first task to abort ? 529 529 kernel_abort_called = true; 530 unlock( kernel_abort_lock );530 unlock( &kernel_abort_lock ); 531 531 } 532 532 else { 533 unlock( kernel_abort_lock );533 unlock( &kernel_abort_lock ); 534 534 535 535 sigset_t mask; … … 561 561 extern "C" { 562 562 void __lib_debug_acquire() { 563 lock( kernel_debug_lock DEBUG_CTX2 );563 lock( &kernel_debug_lock DEBUG_CTX2 ); 564 564 } 565 565 566 566 void __lib_debug_release() { 567 unlock( kernel_debug_lock );567 unlock( &kernel_debug_lock ); 568 568 } 569 569 } … … 574 574 //----------------------------------------------------------------------------- 575 575 // Locks 576 void ?{}( spinlock & this ) { 577 this.lock = 0; 578 } 579 void ^?{}( spinlock & this ) { 580 581 } 582 583 bool try_lock( spinlock * this DEBUG_CTX_PARAM2 ) { 584 return this->lock == 0 && __sync_lock_test_and_set_4( &this->lock, 1 ) == 0; 585 } 586 587 void lock( spinlock * this DEBUG_CTX_PARAM2 ) { 588 for ( unsigned int i = 1;; i += 1 ) { 589 if ( this->lock == 0 && __sync_lock_test_and_set_4( &this->lock, 1 ) == 0 ) { break; } 590 } 591 LIB_DEBUG_DO( 592 this->prev_name = caller; 593 this->prev_thrd = this_thread; 594 ) 595 } 596 597 void lock_yield( spinlock * this DEBUG_CTX_PARAM2 ) { 598 for ( unsigned int i = 1;; i += 1 ) { 599 if ( this->lock == 0 && __sync_lock_test_and_set_4( &this->lock, 1 ) == 0 ) { break; } 600 yield(); 601 } 602 LIB_DEBUG_DO( 603 this->prev_name = caller; 604 this->prev_thrd = this_thread; 605 ) 606 } 607 608 609 void unlock( spinlock * this ) { 610 __sync_lock_release_4( &this->lock ); 611 } 612 576 613 void ?{}( semaphore & this, int count = 1 ) { 577 614 (this.lock){}; … … 581 618 void ^?{}(semaphore & this) {} 582 619 583 void P(semaphore &this) {584 lock( this.lock DEBUG_CTX2 );585 this .count -= 1;586 if ( this .count < 0 ) {620 void P(semaphore * this) { 621 lock( &this->lock DEBUG_CTX2 ); 622 this->count -= 1; 623 if ( this->count < 0 ) { 587 624 // queue current task 588 append( this.waiting, (thread_desc *)this_thread );625 append( &this->waiting, (thread_desc *)this_thread ); 589 626 590 627 // atomically release spin lock and block 591 BlockInternal( &this .lock );628 BlockInternal( &this->lock ); 592 629 } 593 630 else { 594 unlock( this.lock );595 } 596 } 597 598 void V(semaphore &this) {631 unlock( &this->lock ); 632 } 633 } 634 635 void V(semaphore * this) { 599 636 thread_desc * thrd = NULL; 600 lock( this.lock DEBUG_CTX2 );601 this .count += 1;602 if ( this .count <= 0 ) {637 lock( &this->lock DEBUG_CTX2 ); 638 this->count += 1; 639 if ( this->count <= 0 ) { 603 640 // remove task at head of waiting list 604 thrd = pop_head( this.waiting );605 } 606 607 unlock( this.lock );641 thrd = pop_head( &this->waiting ); 642 } 643 644 unlock( &this->lock ); 608 645 609 646 // make new owner … … 618 655 } 619 656 620 void append( __thread_queue_t &this, thread_desc * t ) {621 verify(this .tail != NULL);622 *this .tail = t;623 this .tail = &t->next;624 } 625 626 thread_desc * pop_head( __thread_queue_t &this ) {627 thread_desc * head = this .head;657 void append( __thread_queue_t * this, thread_desc * t ) { 658 verify(this->tail != NULL); 659 *this->tail = t; 660 this->tail = &t->next; 661 } 662 663 thread_desc * pop_head( __thread_queue_t * this ) { 664 thread_desc * head = this->head; 628 665 if( head ) { 629 this .head = head->next;666 this->head = head->next; 630 667 if( !head->next ) { 631 this .tail = &this.head;668 this->tail = &this->head; 632 669 } 633 670 head->next = NULL; … … 636 673 } 637 674 638 thread_desc * remove( __thread_queue_t &this, thread_desc ** it ) {675 thread_desc * remove( __thread_queue_t * this, thread_desc ** it ) { 639 676 thread_desc * thrd = *it; 640 677 verify( thrd ); … … 642 679 (*it) = thrd->next; 643 680 644 if( this .tail == &thrd->next ) {645 this .tail = it;681 if( this->tail == &thrd->next ) { 682 this->tail = it; 646 683 } 647 684 648 685 thrd->next = NULL; 649 686 650 verify( (this .head == NULL) == (&this.head == this.tail) );651 verify( *this .tail == NULL );687 verify( (this->head == NULL) == (&this->head == this->tail) ); 688 verify( *this->tail == NULL ); 652 689 return thrd; 653 690 } … … 657 694 } 658 695 659 void push( __condition_stack_t &this, __condition_criterion_t * t ) {696 void push( __condition_stack_t * this, __condition_criterion_t * t ) { 660 697 verify( !t->next ); 661 t->next = this .top;662 this .top = t;663 } 664 665 __condition_criterion_t * pop( __condition_stack_t &this ) {666 __condition_criterion_t * top = this .top;698 t->next = this->top; 699 this->top = t; 700 } 701 702 __condition_criterion_t * pop( __condition_stack_t * this ) { 703 __condition_criterion_t * top = this->top; 667 704 if( top ) { 668 this .top = top->next;705 this->top = top->next; 669 706 top->next = NULL; 670 707 } -
src/libcfa/concurrency/kernel_private.h
r0fe4e62 rf5c3b6c 45 45 //Block current thread and release/wake-up the following resources 46 46 void BlockInternal(void); 47 void BlockInternal( __spinlock_t* lock);47 void BlockInternal(spinlock * lock); 48 48 void BlockInternal(thread_desc * thrd); 49 void BlockInternal( __spinlock_t* lock, thread_desc * thrd);50 void BlockInternal( __spinlock_t * locks [], unsigned short count);51 void BlockInternal( __spinlock_t * locks [], unsigned short count, thread_desc * thrds [], unsigned short thrd_count);52 void LeaveThread( __spinlock_t* lock, thread_desc * thrd);49 void BlockInternal(spinlock * lock, thread_desc * thrd); 50 void BlockInternal(spinlock ** locks, unsigned short count); 51 void BlockInternal(spinlock ** locks, unsigned short count, thread_desc ** thrds, unsigned short thrd_count); 52 void LeaveThread(spinlock * lock, thread_desc * thrd); 53 53 54 54 //----------------------------------------------------------------------------- … … 66 66 struct event_kernel_t { 67 67 alarm_list_t alarms; 68 __spinlock_tlock;68 spinlock lock; 69 69 }; 70 70 -
src/libcfa/concurrency/monitor
r0fe4e62 rf5c3b6c 39 39 } 40 40 41 // static inline int ?<?(monitor_desc* lhs, monitor_desc* rhs) { 42 // return ((intptr_t)lhs) < ((intptr_t)rhs); 43 // } 44 41 45 struct monitor_guard_t { 42 46 monitor_desc ** m; 43 __lock_size_tcount;47 int count; 44 48 monitor_desc ** prev_mntrs; 45 __lock_size_tprev_count;49 unsigned short prev_count; 46 50 fptr_t prev_func; 47 51 }; 48 52 49 void ?{}( monitor_guard_t & this, monitor_desc ** m, __lock_size_t count, void (*func)() );53 void ?{}( monitor_guard_t & this, monitor_desc ** m, int count, void (*func)() ); 50 54 void ^?{}( monitor_guard_t & this ); 51 55 … … 53 57 monitor_desc * m; 54 58 monitor_desc ** prev_mntrs; 55 __lock_size_tprev_count;59 unsigned short prev_count; 56 60 fptr_t prev_func; 57 61 }; … … 70 74 71 75 struct __condition_criterion_t { 72 // Whether or not the criterion is met (True if met) 73 bool ready; 74 75 // The monitor this criterion concerns 76 monitor_desc * target; 77 78 // The parent node to which this criterion belongs 79 struct __condition_node_t * owner; 80 81 // Intrusive linked list Next field 82 __condition_criterion_t * next; 76 bool ready; //Whether or not the criterion is met (True if met) 77 monitor_desc * target; //The monitor this criterion concerns 78 struct __condition_node_t * owner; //The parent node to which this criterion belongs 79 __condition_criterion_t * next; //Intrusive linked list Next field 83 80 }; 84 81 85 82 struct __condition_node_t { 86 // Thread that needs to be woken when all criteria are met 87 thread_desc * waiting_thread; 88 89 // Array of criteria (Criterions are contiguous in memory) 90 __condition_criterion_t * criteria; 91 92 // Number of criterions in the criteria 93 __lock_size_t count; 94 95 // Intrusive linked list Next field 96 __condition_node_t * next; 97 98 // Custom user info accessible before signalling 99 uintptr_t user_info; 83 thread_desc * waiting_thread; //Thread that needs to be woken when all criteria are met 84 __condition_criterion_t * criteria; //Array of criteria (Criterions are contiguous in memory) 85 unsigned short count; //Number of criterions in the criteria 86 __condition_node_t * next; //Intrusive linked list Next field 87 uintptr_t user_info; //Custom user info accessible before signalling 100 88 }; 101 89 … … 105 93 }; 106 94 107 void ?{}(__condition_node_t & this, thread_desc * waiting_thread, __lock_size_t count, uintptr_t user_info );95 void ?{}(__condition_node_t & this, thread_desc * waiting_thread, unsigned short count, uintptr_t user_info ); 108 96 void ?{}(__condition_criterion_t & this ); 109 97 void ?{}(__condition_criterion_t & this, monitor_desc * target, __condition_node_t * owner ); 110 98 111 99 void ?{}( __condition_blocked_queue_t & ); 112 void append( __condition_blocked_queue_t &, __condition_node_t * );113 __condition_node_t * pop_head( __condition_blocked_queue_t &);100 void append( __condition_blocked_queue_t *, __condition_node_t * ); 101 __condition_node_t * pop_head( __condition_blocked_queue_t * ); 114 102 115 103 struct condition { 116 // Link list which contains the blocked threads as-well as the information needed to unblock them 117 __condition_blocked_queue_t blocked; 118 119 // Array of monitor pointers (Monitors are NOT contiguous in memory) 120 monitor_desc ** monitors; 121 122 // Number of monitors in the array 123 __lock_size_t monitor_count; 104 __condition_blocked_queue_t blocked; //Link list which contains the blocked threads as-well as the information needed to unblock them 105 monitor_desc ** monitors; //Array of monitor pointers (Monitors are NOT contiguous in memory) 106 unsigned short monitor_count; //Number of monitors in the array 124 107 }; 125 108 … … 133 116 } 134 117 135 void wait ( condition &this, uintptr_t user_info = 0 );136 bool signal ( condition &this );137 bool signal_block( condition &this );138 static inline bool is_empty ( condition & this ) { return !this.blocked.head; }139 uintptr_t front ( condition &this );118 void wait( condition * this, uintptr_t user_info = 0 ); 119 bool signal( condition * this ); 120 bool signal_block( condition * this ); 121 static inline bool is_empty( condition * this ) { return !this->blocked.head; } 122 uintptr_t front( condition * this ); 140 123 141 124 //----------------------------------------------------------------------------- -
src/libcfa/concurrency/monitor.c
r0fe4e62 rf5c3b6c 17 17 18 18 #include <stdlib> 19 #include <inttypes.h>20 19 21 20 #include "libhdr.h" … … 27 26 // Forward declarations 28 27 static inline void set_owner ( monitor_desc * this, thread_desc * owner ); 29 static inline void set_owner ( monitor_desc * storage [], __lock_size_t count, thread_desc * owner );30 static inline void set_mask ( monitor_desc * storage [], __lock_size_t count, const __waitfor_mask_t & mask );28 static inline void set_owner ( monitor_desc ** storage, short count, thread_desc * owner ); 29 static inline void set_mask ( monitor_desc ** storage, short count, const __waitfor_mask_t & mask ); 31 30 static inline void reset_mask( monitor_desc * this ); 32 31 … … 34 33 static inline bool is_accepted( monitor_desc * this, const __monitor_group_t & monitors ); 35 34 36 static inline void lock_all ( __spinlock_t * locks [], __lock_size_t count );37 static inline void lock_all ( monitor_desc * source [], __spinlock_t * /*out*/ locks [], __lock_size_t count );38 static inline void unlock_all( __spinlock_t * locks [], __lock_size_t count );39 static inline void unlock_all( monitor_desc * locks [], __lock_size_t count );40 41 static inline void save ( monitor_desc * ctx [], __lock_size_t count, __spinlock_t * locks [], unsigned int /*out*/ recursions [], __waitfor_mask_t /*out*/ masks []);42 static inline void restore( monitor_desc * ctx [], __lock_size_t count, __spinlock_t * locks [], unsigned int /*in */ recursions [], __waitfor_mask_t /*in */ masks []);43 44 static inline void init ( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria []);45 static inline void init_push( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria []);35 static inline void lock_all( spinlock ** locks, unsigned short count ); 36 static inline void lock_all( monitor_desc ** source, spinlock ** /*out*/ locks, unsigned short count ); 37 static inline void unlock_all( spinlock ** locks, unsigned short count ); 38 static inline void unlock_all( monitor_desc ** locks, unsigned short count ); 39 40 static inline void save ( monitor_desc ** ctx, short count, spinlock ** locks, unsigned int * /*out*/ recursions, __waitfor_mask_t * /*out*/ masks ); 41 static inline void restore( monitor_desc ** ctx, short count, spinlock ** locks, unsigned int * /*in */ recursions, __waitfor_mask_t * /*in */ masks ); 42 43 static inline void init ( int count, monitor_desc ** monitors, __condition_node_t * waiter, __condition_criterion_t * criteria ); 44 static inline void init_push( int count, monitor_desc ** monitors, __condition_node_t * waiter, __condition_criterion_t * criteria ); 46 45 47 46 static inline thread_desc * check_condition ( __condition_criterion_t * ); 48 static inline void brand_condition ( condition &);49 static inline [thread_desc *, int] search_entry_queue( const __waitfor_mask_t &, monitor_desc * monitors [], __lock_size_t count );47 static inline void brand_condition ( condition * ); 48 static inline [thread_desc *, int] search_entry_queue( const __waitfor_mask_t &, monitor_desc ** monitors, int count ); 50 49 51 50 forall(dtype T | sized( T )) 52 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ); 53 static inline __lock_size_t count_max ( const __waitfor_mask_t & mask ); 54 static inline __lock_size_t aggregate ( monitor_desc * storage [], const __waitfor_mask_t & mask ); 55 56 #ifndef __CFA_LOCK_NO_YIELD 57 #define DO_LOCK lock_yield 58 #else 59 #define DO_LOCK lock 60 #endif 51 static inline short insert_unique( T ** array, short & size, T * val ); 52 static inline short count_max ( const __waitfor_mask_t & mask ); 53 static inline short aggregate ( monitor_desc ** storage, const __waitfor_mask_t & mask ); 61 54 62 55 //----------------------------------------------------------------------------- … … 65 58 __condition_node_t waiter = { thrd, count, user_info }; /* Create the node specific to this wait operation */ \ 66 59 __condition_criterion_t criteria[count]; /* Create the creteria this wait operation needs to wake up */ \ 67 init( count, monitors, waiter, criteria );/* Link everything together */ \60 init( count, monitors, &waiter, criteria ); /* Link everything together */ \ 68 61 69 62 #define wait_ctx_primed(thrd, user_info) /* Create the necessary information to use the signaller stack */ \ 70 63 __condition_node_t waiter = { thrd, count, user_info }; /* Create the node specific to this wait operation */ \ 71 64 __condition_criterion_t criteria[count]; /* Create the creteria this wait operation needs to wake up */ \ 72 init_push( count, monitors, waiter, criteria );/* Link everything together and push it to the AS-Stack */ \65 init_push( count, monitors, &waiter, criteria ); /* Link everything together and push it to the AS-Stack */ \ 73 66 74 67 #define monitor_ctx( mons, cnt ) /* Define that create the necessary struct for internal/external scheduling operations */ \ 75 68 monitor_desc ** monitors = mons; /* Save the targeted monitors */ \ 76 __lock_size_t count = cnt;/* Save the count to a local variable */ \69 unsigned short count = cnt; /* Save the count to a local variable */ \ 77 70 unsigned int recursions[ count ]; /* Save the current recursion levels to restore them later */ \ 78 __waitfor_mask_t masks [ count ];/* Save the current waitfor masks to restore them later */ \79 __spinlock_t * locks[ count ]; /* We need to pass-in an array of locks to BlockInternal */ \71 __waitfor_mask_t masks[ count ]; /* Save the current waitfor masks to restore them later */ \ 72 spinlock * locks [ count ]; /* We need to pass-in an array of locks to BlockInternal */ \ 80 73 81 74 #define monitor_save save ( monitors, count, locks, recursions, masks ) … … 90 83 // Enter single monitor 91 84 static void __enter_monitor_desc( monitor_desc * this, const __monitor_group_t & group ) { 92 // Lock the monitor spinlock 93 DO_LOCK(this->lock DEBUG_CTX2 );85 // Lock the monitor spinlock, lock_yield to reduce contention 86 lock_yield( &this->lock DEBUG_CTX2 ); 94 87 thread_desc * thrd = this_thread; 95 88 … … 121 114 122 115 // Some one else has the monitor, wait in line for it 123 append( this->entry_queue, thrd );116 append( &this->entry_queue, thrd ); 124 117 BlockInternal( &this->lock ); 125 118 … … 133 126 134 127 // Release the lock and leave 135 unlock( this->lock );128 unlock( &this->lock ); 136 129 return; 137 130 } 138 131 139 132 static void __enter_monitor_dtor( monitor_desc * this, fptr_t func ) { 140 // Lock the monitor spinlock 141 DO_LOCK(this->lock DEBUG_CTX2 );133 // Lock the monitor spinlock, lock_yield to reduce contention 134 lock_yield( &this->lock DEBUG_CTX2 ); 142 135 thread_desc * thrd = this_thread; 143 136 … … 151 144 set_owner( this, thrd ); 152 145 153 unlock( this->lock );146 unlock( &this->lock ); 154 147 return; 155 148 } … … 160 153 } 161 154 162 __lock_size_t count = 1;155 int count = 1; 163 156 monitor_desc ** monitors = &this; 164 157 __monitor_group_t group = { &this, 1, func }; … … 167 160 168 161 // Wake the thread that is waiting for this 169 __condition_criterion_t * urgent = pop( this->signal_stack );162 __condition_criterion_t * urgent = pop( &this->signal_stack ); 170 163 verify( urgent ); 171 164 … … 189 182 190 183 // Some one else has the monitor, wait in line for it 191 append( this->entry_queue, thrd );184 append( &this->entry_queue, thrd ); 192 185 BlockInternal( &this->lock ); 193 186 … … 202 195 // Leave single monitor 203 196 void __leave_monitor_desc( monitor_desc * this ) { 204 // Lock the monitor spinlock, DO_LOCKto reduce contention205 DO_LOCK(this->lock DEBUG_CTX2 );197 // Lock the monitor spinlock, lock_yield to reduce contention 198 lock_yield( &this->lock DEBUG_CTX2 ); 206 199 207 200 LIB_DEBUG_PRINT_SAFE("Kernel : %10p Leaving mon %p (%p)\n", this_thread, this, this->owner); … … 216 209 if( this->recursion != 0) { 217 210 LIB_DEBUG_PRINT_SAFE("Kernel : recursion still %d\n", this->recursion); 218 unlock( this->lock );211 unlock( &this->lock ); 219 212 return; 220 213 } … … 224 217 225 218 // We can now let other threads in safely 226 unlock( this->lock );219 unlock( &this->lock ); 227 220 228 221 //We need to wake-up the thread … … 249 242 250 243 // Lock the monitor now 251 DO_LOCK(this->lock DEBUG_CTX2 );244 lock_yield( &this->lock DEBUG_CTX2 ); 252 245 253 246 disable_interrupts(); … … 279 272 // relies on the monitor array being sorted 280 273 static inline void enter( __monitor_group_t monitors ) { 281 for( __lock_size_t i = 0; i < monitors.size; i++) {274 for(int i = 0; i < monitors.size; i++) { 282 275 __enter_monitor_desc( monitors.list[i], monitors ); 283 276 } … … 286 279 // Leave multiple monitor 287 280 // relies on the monitor array being sorted 288 static inline void leave(monitor_desc * monitors [], __lock_size_t count) {289 for( __lock_size_t i = count - 1; i >= 0; i--) {281 static inline void leave(monitor_desc ** monitors, int count) { 282 for(int i = count - 1; i >= 0; i--) { 290 283 __leave_monitor_desc( monitors[i] ); 291 284 } … … 294 287 // Ctor for monitor guard 295 288 // Sorts monitors before entering 296 void ?{}( monitor_guard_t & this, monitor_desc * m [], __lock_size_t count, fptr_t func ) {289 void ?{}( monitor_guard_t & this, monitor_desc ** m, int count, fptr_t func ) { 297 290 // Store current array 298 291 this.m = m; … … 303 296 304 297 // Save previous thread context 305 this.[prev_mntrs, prev_count, prev_func] = this_thread->monitors.[list, size, func]; 298 this.prev_mntrs = this_thread->monitors.list; 299 this.prev_count = this_thread->monitors.size; 300 this.prev_func = this_thread->monitors.func; 306 301 307 302 // Update thread context (needed for conditions) 308 this_thread->monitors.[list, size, func] = [m, count, func]; 303 this_thread->monitors.list = m; 304 this_thread->monitors.size = count; 305 this_thread->monitors.func = func; 309 306 310 307 // LIB_DEBUG_PRINT_SAFE("MGUARD : enter %d\n", count); … … 328 325 329 326 // Restore thread context 330 this_thread->monitors.[list, size, func] = this.[prev_mntrs, prev_count, prev_func]; 331 } 327 this_thread->monitors.list = this.prev_mntrs; 328 this_thread->monitors.size = this.prev_count; 329 this_thread->monitors.func = this.prev_func; 330 } 331 332 332 333 333 // Ctor for monitor guard 334 334 // Sorts monitors before entering 335 void ?{}( monitor_dtor_guard_t & this, monitor_desc * m [], fptr_t func ) {335 void ?{}( monitor_dtor_guard_t & this, monitor_desc ** m, fptr_t func ) { 336 336 // Store current array 337 337 this.m = *m; 338 338 339 339 // Save previous thread context 340 this.[prev_mntrs, prev_count, prev_func] = this_thread->monitors.[list, size, func]; 340 this.prev_mntrs = this_thread->monitors.list; 341 this.prev_count = this_thread->monitors.size; 342 this.prev_func = this_thread->monitors.func; 341 343 342 344 // Update thread context (needed for conditions) 343 this_thread->monitors.[list, size, func] = [m, 1, func]; 345 this_thread->monitors.list = m; 346 this_thread->monitors.size = 1; 347 this_thread->monitors.func = func; 344 348 345 349 __enter_monitor_dtor( this.m, func ); 346 350 } 351 347 352 348 353 // Dtor for monitor guard … … 352 357 353 358 // Restore thread context 354 this_thread->monitors.[list, size, func] = this.[prev_mntrs, prev_count, prev_func]; 359 this_thread->monitors.list = this.prev_mntrs; 360 this_thread->monitors.size = this.prev_count; 361 this_thread->monitors.func = this.prev_func; 355 362 } 356 363 357 364 //----------------------------------------------------------------------------- 358 365 // Internal scheduling types 359 void ?{}(__condition_node_t & this, thread_desc * waiting_thread, __lock_size_t count, uintptr_t user_info ) {366 void ?{}(__condition_node_t & this, thread_desc * waiting_thread, unsigned short count, uintptr_t user_info ) { 360 367 this.waiting_thread = waiting_thread; 361 368 this.count = count; … … 371 378 } 372 379 373 void ?{}(__condition_criterion_t & this, monitor_desc * target, __condition_node_t &owner ) {380 void ?{}(__condition_criterion_t & this, monitor_desc * target, __condition_node_t * owner ) { 374 381 this.ready = false; 375 382 this.target = target; 376 this.owner = &owner;383 this.owner = owner; 377 384 this.next = NULL; 378 385 } … … 380 387 //----------------------------------------------------------------------------- 381 388 // Internal scheduling 382 void wait( condition &this, uintptr_t user_info = 0 ) {389 void wait( condition * this, uintptr_t user_info = 0 ) { 383 390 brand_condition( this ); 384 391 385 392 // Check that everything is as expected 386 assertf( this .monitors != NULL, "Waiting with no monitors (%p)", this.monitors );387 verifyf( this .monitor_count != 0, "Waiting with 0 monitors (%"PRIiFAST16")", this.monitor_count );388 verifyf( this .monitor_count < 32u, "Excessive monitor count (%"PRIiFAST16")", this.monitor_count );393 assertf( this->monitors != NULL, "Waiting with no monitors (%p)", this->monitors ); 394 verifyf( this->monitor_count != 0, "Waiting with 0 monitors (%i)", this->monitor_count ); 395 verifyf( this->monitor_count < 32u, "Excessive monitor count (%i)", this->monitor_count ); 389 396 390 397 // Create storage for monitor context 391 monitor_ctx( this .monitors, this.monitor_count );398 monitor_ctx( this->monitors, this->monitor_count ); 392 399 393 400 // Create the node specific to this wait operation … … 396 403 // Append the current wait operation to the ones already queued on the condition 397 404 // We don't need locks for that since conditions must always be waited on inside monitor mutual exclusion 398 append( this.blocked, &waiter );405 append( &this->blocked, &waiter ); 399 406 400 407 // Lock all monitors (aggregates the locks as well) … … 402 409 403 410 // Find the next thread(s) to run 404 __lock_size_t thread_count = 0;411 short thread_count = 0; 405 412 thread_desc * threads[ count ]; 406 413 __builtin_memset( threads, 0, sizeof( threads ) ); … … 410 417 411 418 // Remove any duplicate threads 412 for( __lock_size_t i = 0; i < count; i++) {419 for( int i = 0; i < count; i++) { 413 420 thread_desc * new_owner = next_thread( monitors[i] ); 414 421 insert_unique( threads, thread_count, new_owner ); … … 422 429 } 423 430 424 bool signal( condition &this ) {431 bool signal( condition * this ) { 425 432 if( is_empty( this ) ) { return false; } 426 433 427 434 //Check that everything is as expected 428 verify( this .monitors );429 verify( this .monitor_count != 0 );435 verify( this->monitors ); 436 verify( this->monitor_count != 0 ); 430 437 431 438 //Some more checking in debug 432 439 LIB_DEBUG_DO( 433 440 thread_desc * this_thrd = this_thread; 434 if ( this .monitor_count != this_thrd->monitors.size ) {435 abortf( "Signal on condition %p made with different number of monitor(s), expected %i got %i", &this, this.monitor_count, this_thrd->monitors.size );436 } 437 438 for(int i = 0; i < this .monitor_count; i++) {439 if ( this .monitors[i] != this_thrd->monitors.list[i] ) {440 abortf( "Signal on condition %p made with different monitor, expected %p got %i", &this, this.monitors[i], this_thrd->monitors.list[i] );441 if ( this->monitor_count != this_thrd->monitors.size ) { 442 abortf( "Signal on condition %p made with different number of monitor(s), expected %i got %i", this, this->monitor_count, this_thrd->monitors.size ); 443 } 444 445 for(int i = 0; i < this->monitor_count; i++) { 446 if ( this->monitors[i] != this_thrd->monitors.list[i] ) { 447 abortf( "Signal on condition %p made with different monitor, expected %p got %i", this, this->monitors[i], this_thrd->monitors.list[i] ); 441 448 } 442 449 } 443 450 ); 444 451 445 __lock_size_t count = this.monitor_count;452 unsigned short count = this->monitor_count; 446 453 447 454 // Lock all monitors 448 lock_all( this .monitors, NULL, count );455 lock_all( this->monitors, NULL, count ); 449 456 450 457 //Pop the head of the waiting queue 451 __condition_node_t * node = pop_head( this.blocked );458 __condition_node_t * node = pop_head( &this->blocked ); 452 459 453 460 //Add the thread to the proper AS stack … … 455 462 __condition_criterion_t * crit = &node->criteria[i]; 456 463 assert( !crit->ready ); 457 push( crit->target->signal_stack, crit );464 push( &crit->target->signal_stack, crit ); 458 465 } 459 466 460 467 //Release 461 unlock_all( this .monitors, count );468 unlock_all( this->monitors, count ); 462 469 463 470 return true; 464 471 } 465 472 466 bool signal_block( condition &this ) {467 if( !this .blocked.head ) { return false; }473 bool signal_block( condition * this ) { 474 if( !this->blocked.head ) { return false; } 468 475 469 476 //Check that everything is as expected 470 verifyf( this .monitors != NULL, "Waiting with no monitors (%p)", this.monitors );471 verifyf( this .monitor_count != 0, "Waiting with 0 monitors (%"PRIiFAST16")", this.monitor_count );477 verifyf( this->monitors != NULL, "Waiting with no monitors (%p)", this->monitors ); 478 verifyf( this->monitor_count != 0, "Waiting with 0 monitors (%i)", this->monitor_count ); 472 479 473 480 // Create storage for monitor context 474 monitor_ctx( this .monitors, this.monitor_count );481 monitor_ctx( this->monitors, this->monitor_count ); 475 482 476 483 // Lock all monitors (aggregates the locks them as well) … … 484 491 485 492 //Find the thread to run 486 thread_desc * signallee = pop_head( this.blocked )->waiting_thread;493 thread_desc * signallee = pop_head( &this->blocked )->waiting_thread; 487 494 set_owner( monitors, count, signallee ); 488 495 489 LIB_DEBUG_PRINT_BUFFER_DECL( "Kernel : signal_block condition %p (s: %p)\n", &this, signallee );496 LIB_DEBUG_PRINT_BUFFER_DECL( "Kernel : signal_block condition %p (s: %p)\n", this, signallee ); 490 497 491 498 //Everything is ready to go to sleep … … 505 512 506 513 // Access the user_info of the thread waiting at the front of the queue 507 uintptr_t front( condition &this ) {514 uintptr_t front( condition * this ) { 508 515 verifyf( !is_empty(this), 509 516 "Attempt to access user data on an empty condition.\n" 510 517 "Possible cause is not checking if the condition is empty before reading stored data." 511 518 ); 512 return this .blocked.head->user_info;519 return this->blocked.head->user_info; 513 520 } 514 521 … … 530 537 // This statment doesn't have a contiguous list of monitors... 531 538 // Create one! 532 __lock_size_t max = count_max( mask );539 short max = count_max( mask ); 533 540 monitor_desc * mon_storage[max]; 534 541 __builtin_memset( mon_storage, 0, sizeof( mon_storage ) ); 535 __lock_size_t actual_count = aggregate( mon_storage, mask );536 537 LIB_DEBUG_PRINT_BUFFER_DECL( "Kernel : waitfor %d (s: %d, m: %d)\n", actual_count, mask.size, ( __lock_size_t)max);542 short actual_count = aggregate( mon_storage, mask ); 543 544 LIB_DEBUG_PRINT_BUFFER_DECL( "Kernel : waitfor %d (s: %d, m: %d)\n", actual_count, mask.size, (short)max); 538 545 539 546 if(actual_count == 0) return; … … 562 569 563 570 __condition_criterion_t * dtor_crit = mon2dtor->dtor_node->criteria; 564 push( mon2dtor->signal_stack, dtor_crit );571 push( &mon2dtor->signal_stack, dtor_crit ); 565 572 566 573 unlock_all( locks, count ); … … 622 629 set_mask( monitors, count, mask ); 623 630 624 for( __lock_size_t i = 0; i < count; i++) {631 for(int i = 0; i < count; i++) { 625 632 verify( monitors[i]->owner == this_thread ); 626 633 } … … 654 661 } 655 662 656 static inline void set_owner( monitor_desc * monitors [], __lock_size_t count, thread_desc * owner ) {663 static inline void set_owner( monitor_desc ** monitors, short count, thread_desc * owner ) { 657 664 monitors[0]->owner = owner; 658 665 monitors[0]->recursion = 1; 659 for( __lock_size_t i = 1; i < count; i++ ) {666 for( int i = 1; i < count; i++ ) { 660 667 monitors[i]->owner = owner; 661 668 monitors[i]->recursion = 0; … … 663 670 } 664 671 665 static inline void set_mask( monitor_desc * storage [], __lock_size_t count, const __waitfor_mask_t & mask ) {666 for( __lock_size_t i = 0; i < count; i++) {672 static inline void set_mask( monitor_desc ** storage, short count, const __waitfor_mask_t & mask ) { 673 for(int i = 0; i < count; i++) { 667 674 storage[i]->mask = mask; 668 675 } … … 678 685 //Check the signaller stack 679 686 LIB_DEBUG_PRINT_SAFE("Kernel : mon %p AS-stack top %p\n", this, this->signal_stack.top); 680 __condition_criterion_t * urgent = pop( this->signal_stack );687 __condition_criterion_t * urgent = pop( &this->signal_stack ); 681 688 if( urgent ) { 682 689 //The signaller stack is not empty, … … 690 697 // No signaller thread 691 698 // Get the next thread in the entry_queue 692 thread_desc * new_owner = pop_head( this->entry_queue );699 thread_desc * new_owner = pop_head( &this->entry_queue ); 693 700 set_owner( this, new_owner ); 694 701 … … 698 705 static inline bool is_accepted( monitor_desc * this, const __monitor_group_t & group ) { 699 706 __acceptable_t * it = this->mask.clauses; // Optim 700 __lock_size_t count = this->mask.size;707 int count = this->mask.size; 701 708 702 709 // Check if there are any acceptable functions … … 707 714 708 715 // For all acceptable functions check if this is the current function. 709 for( __lock_size_t i = 0; i < count; i++, it++ ) {716 for( short i = 0; i < count; i++, it++ ) { 710 717 if( *it == group ) { 711 718 *this->mask.accepted = i; … … 718 725 } 719 726 720 static inline void init( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria []) {721 for( __lock_size_t i = 0; i < count; i++) {727 static inline void init( int count, monitor_desc ** monitors, __condition_node_t * waiter, __condition_criterion_t * criteria ) { 728 for(int i = 0; i < count; i++) { 722 729 (criteria[i]){ monitors[i], waiter }; 723 730 } 724 731 725 waiter .criteria = criteria;726 } 727 728 static inline void init_push( __lock_size_t count, monitor_desc * monitors [], __condition_node_t & waiter, __condition_criterion_t criteria []) {729 for( __lock_size_t i = 0; i < count; i++) {732 waiter->criteria = criteria; 733 } 734 735 static inline void init_push( int count, monitor_desc ** monitors, __condition_node_t * waiter, __condition_criterion_t * criteria ) { 736 for(int i = 0; i < count; i++) { 730 737 (criteria[i]){ monitors[i], waiter }; 731 738 LIB_DEBUG_PRINT_SAFE( "Kernel : target %p = %p\n", criteria[i].target, &criteria[i] ); 732 push( criteria[i].target->signal_stack, &criteria[i] );733 } 734 735 waiter .criteria = criteria;736 } 737 738 static inline void lock_all( __spinlock_t * locks [], __lock_size_t count ) {739 for( __lock_size_t i = 0; i < count; i++ ) {740 DO_LOCK( *locks[i] DEBUG_CTX2 );741 } 742 } 743 744 static inline void lock_all( monitor_desc * source [], __spinlock_t * /*out*/ locks [], __lock_size_t count ) {745 for( __lock_size_t i = 0; i < count; i++ ) {746 __spinlock_t* l = &source[i]->lock;747 DO_LOCK( *l DEBUG_CTX2 );739 push( &criteria[i].target->signal_stack, &criteria[i] ); 740 } 741 742 waiter->criteria = criteria; 743 } 744 745 static inline void lock_all( spinlock ** locks, unsigned short count ) { 746 for( int i = 0; i < count; i++ ) { 747 lock_yield( locks[i] DEBUG_CTX2 ); 748 } 749 } 750 751 static inline void lock_all( monitor_desc ** source, spinlock ** /*out*/ locks, unsigned short count ) { 752 for( int i = 0; i < count; i++ ) { 753 spinlock * l = &source[i]->lock; 754 lock_yield( l DEBUG_CTX2 ); 748 755 if(locks) locks[i] = l; 749 756 } 750 757 } 751 758 752 static inline void unlock_all( __spinlock_t * locks [], __lock_size_t count ) { 753 for( __lock_size_t i = 0; i < count; i++ ) { 754 unlock( *locks[i] ); 755 } 756 } 757 758 static inline void unlock_all( monitor_desc * locks [], __lock_size_t count ) { 759 for( __lock_size_t i = 0; i < count; i++ ) { 760 unlock( locks[i]->lock ); 761 } 762 } 763 764 static inline void save( 765 monitor_desc * ctx [], 766 __lock_size_t count, 767 __attribute((unused)) __spinlock_t * locks [], 768 unsigned int /*out*/ recursions [], 769 __waitfor_mask_t /*out*/ masks [] 770 ) { 771 for( __lock_size_t i = 0; i < count; i++ ) { 759 static inline void unlock_all( spinlock ** locks, unsigned short count ) { 760 for( int i = 0; i < count; i++ ) { 761 unlock( locks[i] ); 762 } 763 } 764 765 static inline void unlock_all( monitor_desc ** locks, unsigned short count ) { 766 for( int i = 0; i < count; i++ ) { 767 unlock( &locks[i]->lock ); 768 } 769 } 770 771 static inline void save( monitor_desc ** ctx, short count, __attribute((unused)) spinlock ** locks, unsigned int * /*out*/ recursions, __waitfor_mask_t * /*out*/ masks ) { 772 for( int i = 0; i < count; i++ ) { 772 773 recursions[i] = ctx[i]->recursion; 773 774 masks[i] = ctx[i]->mask; … … 775 776 } 776 777 777 static inline void restore( 778 monitor_desc * ctx [], 779 __lock_size_t count, 780 __spinlock_t * locks [], 781 unsigned int /*out*/ recursions [], 782 __waitfor_mask_t /*out*/ masks [] 783 ) { 778 static inline void restore( monitor_desc ** ctx, short count, spinlock ** locks, unsigned int * /*out*/ recursions, __waitfor_mask_t * /*out*/ masks ) { 784 779 lock_all( locks, count ); 785 for( __lock_size_t i = 0; i < count; i++ ) {780 for( int i = 0; i < count; i++ ) { 786 781 ctx[i]->recursion = recursions[i]; 787 782 ctx[i]->mask = masks[i]; … … 816 811 } 817 812 818 static inline void brand_condition( condition &this ) {813 static inline void brand_condition( condition * this ) { 819 814 thread_desc * thrd = this_thread; 820 if( !this .monitors ) {815 if( !this->monitors ) { 821 816 // LIB_DEBUG_PRINT_SAFE("Branding\n"); 822 817 assertf( thrd->monitors.list != NULL, "No current monitor to brand condition %p", thrd->monitors.list ); 823 this .monitor_count = thrd->monitors.size;824 825 this .monitors = malloc( this.monitor_count * sizeof( *this.monitors ) );826 for( int i = 0; i < this .monitor_count; i++ ) {827 this .monitors[i] = thrd->monitors.list[i];828 } 829 } 830 } 831 832 static inline [thread_desc *, int] search_entry_queue( const __waitfor_mask_t & mask, monitor_desc * monitors [], __lock_size_t count ) {833 834 __thread_queue_t & entry_queue =monitors[0]->entry_queue;818 this->monitor_count = thrd->monitors.size; 819 820 this->monitors = malloc( this->monitor_count * sizeof( *this->monitors ) ); 821 for( int i = 0; i < this->monitor_count; i++ ) { 822 this->monitors[i] = thrd->monitors.list[i]; 823 } 824 } 825 } 826 827 static inline [thread_desc *, int] search_entry_queue( const __waitfor_mask_t & mask, monitor_desc ** monitors, int count ) { 828 829 __thread_queue_t * entry_queue = &monitors[0]->entry_queue; 835 830 836 831 // For each thread in the entry-queue 837 for( thread_desc ** thrd_it = &entry_queue .head;832 for( thread_desc ** thrd_it = &entry_queue->head; 838 833 *thrd_it; 839 834 thrd_it = &(*thrd_it)->next … … 857 852 858 853 forall(dtype T | sized( T )) 859 static inline __lock_size_t insert_unique( T * array [], __lock_size_t & size, T * val ) {854 static inline short insert_unique( T ** array, short & size, T * val ) { 860 855 if( !val ) return size; 861 856 862 for( __lock_size_t i = 0; i <= size; i++) {857 for(int i = 0; i <= size; i++) { 863 858 if( array[i] == val ) return size; 864 859 } … … 869 864 } 870 865 871 static inline __lock_size_t count_max( const __waitfor_mask_t & mask ) {872 __lock_size_t max = 0;873 for( __lock_size_t i = 0; i < mask.size; i++ ) {866 static inline short count_max( const __waitfor_mask_t & mask ) { 867 short max = 0; 868 for( int i = 0; i < mask.size; i++ ) { 874 869 max += mask.clauses[i].size; 875 870 } … … 877 872 } 878 873 879 static inline __lock_size_t aggregate( monitor_desc * storage [], const __waitfor_mask_t & mask ) {880 __lock_size_t size = 0;881 for( __lock_size_t i = 0; i < mask.size; i++ ) {874 static inline short aggregate( monitor_desc ** storage, const __waitfor_mask_t & mask ) { 875 short size = 0; 876 for( int i = 0; i < mask.size; i++ ) { 882 877 __libcfa_small_sort( mask.clauses[i].list, mask.clauses[i].size ); 883 for( __lock_size_t j = 0; j < mask.clauses[i].size; j++) {878 for( int j = 0; j < mask.clauses[i].size; j++) { 884 879 insert_unique( storage, size, mask.clauses[i].list[j] ); 885 880 } … … 895 890 } 896 891 897 void append( __condition_blocked_queue_t &this, __condition_node_t * c ) {898 verify(this .tail != NULL);899 *this .tail = c;900 this .tail = &c->next;901 } 902 903 __condition_node_t * pop_head( __condition_blocked_queue_t &this ) {904 __condition_node_t * head = this .head;892 void append( __condition_blocked_queue_t * this, __condition_node_t * c ) { 893 verify(this->tail != NULL); 894 *this->tail = c; 895 this->tail = &c->next; 896 } 897 898 __condition_node_t * pop_head( __condition_blocked_queue_t * this ) { 899 __condition_node_t * head = this->head; 905 900 if( head ) { 906 this .head = head->next;901 this->head = head->next; 907 902 if( !head->next ) { 908 this .tail = &this.head;903 this->tail = &this->head; 909 904 } 910 905 head->next = NULL; -
src/libcfa/concurrency/preemption.c
r0fe4e62 rf5c3b6c 355 355 case SI_KERNEL: 356 356 // LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n"); 357 lock( event_kernel->lock DEBUG_CTX2 );357 lock( &event_kernel->lock DEBUG_CTX2 ); 358 358 tick_preemption(); 359 unlock( event_kernel->lock );359 unlock( &event_kernel->lock ); 360 360 break; 361 361 // Signal was not sent by the kernel but by an other thread -
src/main.cc
r0fe4e62 rf5c3b6c 206 206 FILE * extras = fopen( libcfap | treep ? "../prelude/extras.cf" : CFA_LIBDIR "/extras.cf", "r" ); 207 207 assertf( extras, "cannot open extras.cf\n" ); 208 parse( extras, LinkageSpec:: BuiltinC );208 parse( extras, LinkageSpec::C ); 209 209 210 210 if ( ! libcfap ) { -
src/prelude/builtins.c
r0fe4e62 rf5c3b6c 80 80 } // ?\? 81 81 82 // FIXME (x \ (unsigned long int)y) relies on X ?\?(T, unsigned long) a function that is neither 83 // defined, nor passed as an assertion parameter. Without user-defined conversions, cannot specify 84 // X as a type that casts to double, yet it doesn't make sense to write functions with that type 85 // signature where X is double. 86 87 // static inline forall( otype T | { void ?{}( T & this, one_t ); T ?*?( T, T ); double ?/?( double, T ); } ) 88 // double ?\?( T x, signed long int y ) { 89 // if ( y >= 0 ) return (double)(x \ (unsigned long int)y); 90 // else return 1.0 / x \ (unsigned long int)(-y); 91 // } // ?\? 82 static inline forall( otype T | { void ?{}( T & this, one_t ); T ?*?( T, T ); double ?/?( double, T ); } ) 83 double ?\?( T x, signed long int y ) { 84 if ( y >= 0 ) return (double)(x \ (unsigned long int)y); 85 else return 1.0 / x \ (unsigned long int)(-y); 86 } // ?\? 92 87 93 88 static inline long int ?\=?( long int & x, unsigned long int y ) { x = x \ y; return x; } -
src/tests/.expect/32/KRfunctions.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 signed int __f0__Fi_iPCii__1(signed int __a__i_1, const signed int *__b__PCi_1, signed int __c__i_1){ 2 8 __attribute__ ((unused)) signed int ___retval_f0__i_1; -
src/tests/.expect/32/attributes.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 signed int __la__Fi___1(){ 2 8 __attribute__ ((unused)) signed int ___retval_la__i_1; -
src/tests/.expect/32/declarationSpecifier.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 volatile const signed short int __x1__CVs_1; 2 8 static volatile const signed short int __x2__CVs_1; … … 695 701 } 696 702 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return __main__Fi_iPPCc__1(argc, argv); } 703 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 704 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 705 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 706 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 707 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 708 extern signed int printf(const char *__restrict __format, ...); 697 709 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 698 710 signed int main(signed int __argc__i_1, char **__argv__PPc_1, char **__envp__PPc_1){ -
src/tests/.expect/32/extension.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 __extension__ signed int __a__i_1; 2 8 __extension__ signed int __b__i_1; -
src/tests/.expect/32/gccExtensions.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 extern signed int __x__i_1 asm ( "xx" ); 2 8 signed int __main__Fi_iPPCc__1(signed int __argc__i_1, const char **__argv__PPCc_1){ … … 168 174 } 169 175 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return __main__Fi_iPPCc__1(argc, argv); } 176 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 177 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 178 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 179 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 180 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 181 extern signed int printf(const char *__restrict __format, ...); 170 182 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 171 183 signed int main(signed int __argc__i_1, char **__argv__PPc_1, char **__envp__PPc_1){ -
src/tests/.expect/32/literals.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 void __for_each__A0_2_0_0____operator_assign__PFd0_Rd0d0____constructor__PF_Rd0____constructor__PF_Rd0d0____destructor__PF_Rd0____operator_assign__PFd1_Rd1d1____constructor__PF_Rd1____constructor__PF_Rd1d1____destructor__PF_Rd1____operator_preincr__PFd0_Rd0____operator_predecr__PFd0_Rd0____operator_equal__PFi_d0d0____operator_notequal__PFi_d0d0____operator_deref__PFRd1_d0__F_d0d0PF_d1___1(__attribute__ ((unused)) void (*_adapterF_9telt_type__P)(void (*__anonymous_object0)(), void *__anonymous_object1), __attribute__ ((unused)) void *(*_adapterFP9telt_type_14titerator_type_M_P)(void (*__anonymous_object2)(), void *__anonymous_object3), __attribute__ ((unused)) signed int (*_adapterFi_14titerator_type14titerator_type_M_PP)(void (*__anonymous_object4)(), void *__anonymous_object5, void *__anonymous_object6), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type_P_M)(void (*__anonymous_object7)(), __attribute__ ((unused)) void *___retval__operator_preincr__14titerator_type_1, void *__anonymous_object8), __attribute__ ((unused)) void (*_adapterF_P9telt_type9telt_type__MP)(void (*__anonymous_object9)(), void *__anonymous_object10, void *__anonymous_object11), __attribute__ ((unused)) void (*_adapterF9telt_type_P9telt_type9telt_type_P_MP)(void (*__anonymous_object12)(), __attribute__ ((unused)) void *___retval__operator_assign__9telt_type_1, void *__anonymous_object13, void *__anonymous_object14), __attribute__ ((unused)) void (*_adapterF_P14titerator_type14titerator_type__MP)(void (*__anonymous_object15)(), void *__anonymous_object16, void *__anonymous_object17), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type14titerator_type_P_MP)(void (*__anonymous_object18)(), __attribute__ ((unused)) void *___retval__operator_assign__14titerator_type_1, void *__anonymous_object19, void *__anonymous_object20), __attribute__ ((unused)) unsigned long int _sizeof_14titerator_type, __attribute__ ((unused)) unsigned long int _alignof_14titerator_type, __attribute__ ((unused)) unsigned long int _sizeof_9telt_type, __attribute__ ((unused)) unsigned long int _alignof_9telt_type, __attribute__ ((unused)) void *(*___operator_assign__PF14titerator_type_R14titerator_type14titerator_type__1)(void *__anonymous_object21, void *__anonymous_object22), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type__1)(void *__anonymous_object23), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type14titerator_type__1)(void *__anonymous_object24, void *__anonymous_object25), __attribute__ ((unused)) void (*___destructor__PF_R14titerator_type__1)(void *__anonymous_object26), __attribute__ ((unused)) void *(*___operator_assign__PF9telt_type_R9telt_type9telt_type__1)(void *__anonymous_object27, void *__anonymous_object28), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type__1)(void *__anonymous_object29), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type9telt_type__1)(void *__anonymous_object30, void *__anonymous_object31), __attribute__ ((unused)) void (*___destructor__PF_R9telt_type__1)(void *__anonymous_object32), __attribute__ ((unused)) void *(*___operator_preincr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object33), __attribute__ ((unused)) void *(*___operator_predecr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object34), __attribute__ ((unused)) signed int (*___operator_equal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object35, void *__anonymous_object36), __attribute__ ((unused)) signed int (*___operator_notequal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object37, void *__anonymous_object38), __attribute__ ((unused)) void *(*___operator_deref__PFR9telt_type_14titerator_type__1)(void *__anonymous_object39), void *__begin__14titerator_type_1, void *__end__14titerator_type_1, void (*__func__PF_9telt_type__1)(void *__anonymous_object40)); 2 8 void __for_each_reverse__A0_2_0_0____operator_assign__PFd0_Rd0d0____constructor__PF_Rd0____constructor__PF_Rd0d0____destructor__PF_Rd0____operator_assign__PFd1_Rd1d1____constructor__PF_Rd1____constructor__PF_Rd1d1____destructor__PF_Rd1____operator_preincr__PFd0_Rd0____operator_predecr__PFd0_Rd0____operator_equal__PFi_d0d0____operator_notequal__PFi_d0d0____operator_deref__PFRd1_d0__F_d0d0PF_d1___1(__attribute__ ((unused)) void (*_adapterF_9telt_type__P)(void (*__anonymous_object41)(), void *__anonymous_object42), __attribute__ ((unused)) void *(*_adapterFP9telt_type_14titerator_type_M_P)(void (*__anonymous_object43)(), void *__anonymous_object44), __attribute__ ((unused)) signed int (*_adapterFi_14titerator_type14titerator_type_M_PP)(void (*__anonymous_object45)(), void *__anonymous_object46, void *__anonymous_object47), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type_P_M)(void (*__anonymous_object48)(), __attribute__ ((unused)) void *___retval__operator_preincr__14titerator_type_1, void *__anonymous_object49), __attribute__ ((unused)) void (*_adapterF_P9telt_type9telt_type__MP)(void (*__anonymous_object50)(), void *__anonymous_object51, void *__anonymous_object52), __attribute__ ((unused)) void (*_adapterF9telt_type_P9telt_type9telt_type_P_MP)(void (*__anonymous_object53)(), __attribute__ ((unused)) void *___retval__operator_assign__9telt_type_1, void *__anonymous_object54, void *__anonymous_object55), __attribute__ ((unused)) void (*_adapterF_P14titerator_type14titerator_type__MP)(void (*__anonymous_object56)(), void *__anonymous_object57, void *__anonymous_object58), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type14titerator_type_P_MP)(void (*__anonymous_object59)(), __attribute__ ((unused)) void *___retval__operator_assign__14titerator_type_1, void *__anonymous_object60, void *__anonymous_object61), __attribute__ ((unused)) unsigned long int _sizeof_14titerator_type, __attribute__ ((unused)) unsigned long int _alignof_14titerator_type, __attribute__ ((unused)) unsigned long int _sizeof_9telt_type, __attribute__ ((unused)) unsigned long int _alignof_9telt_type, __attribute__ ((unused)) void *(*___operator_assign__PF14titerator_type_R14titerator_type14titerator_type__1)(void *__anonymous_object62, void *__anonymous_object63), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type__1)(void *__anonymous_object64), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type14titerator_type__1)(void *__anonymous_object65, void *__anonymous_object66), __attribute__ ((unused)) void (*___destructor__PF_R14titerator_type__1)(void *__anonymous_object67), __attribute__ ((unused)) void *(*___operator_assign__PF9telt_type_R9telt_type9telt_type__1)(void *__anonymous_object68, void *__anonymous_object69), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type__1)(void *__anonymous_object70), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type9telt_type__1)(void *__anonymous_object71, void *__anonymous_object72), __attribute__ ((unused)) void (*___destructor__PF_R9telt_type__1)(void *__anonymous_object73), __attribute__ ((unused)) void *(*___operator_preincr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object74), __attribute__ ((unused)) void *(*___operator_predecr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object75), __attribute__ ((unused)) signed int (*___operator_equal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object76, void *__anonymous_object77), __attribute__ ((unused)) signed int (*___operator_notequal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object78, void *__anonymous_object79), __attribute__ ((unused)) void *(*___operator_deref__PFR9telt_type_14titerator_type__1)(void *__anonymous_object80), void *__begin__14titerator_type_1, void *__end__14titerator_type_1, void (*__func__PF_9telt_type__1)(void *__anonymous_object81)); … … 1371 1377 } 1372 1378 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return __main__Fi___1(); } 1379 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned int __size); 1380 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 1381 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 1382 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 1383 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 1384 extern signed int printf(const char *__restrict __format, ...); 1373 1385 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 1374 1386 signed int main(signed int __argc__i_1, char **__argv__PPc_1, char **__envp__PPc_1){ -
src/tests/.expect/64/KRfunctions.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 signed int __f0__Fi_iPCii__1(signed int __a__i_1, const signed int *__b__PCi_1, signed int __c__i_1){ 2 8 __attribute__ ((unused)) signed int ___retval_f0__i_1; -
src/tests/.expect/64/attributes.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 signed int __la__Fi___1(){ 2 8 __attribute__ ((unused)) signed int ___retval_la__i_1; -
src/tests/.expect/64/declarationSpecifier.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 volatile const signed short int __x1__CVs_1; 2 8 static volatile const signed short int __x2__CVs_1; … … 695 701 } 696 702 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return __main__Fi_iPPCc__1(argc, argv); } 703 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 704 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 705 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 706 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 707 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 708 extern signed int printf(const char *__restrict __format, ...); 697 709 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 698 710 signed int main(signed int __argc__i_1, char **__argv__PPc_1, char **__envp__PPc_1){ -
src/tests/.expect/64/extension.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 __extension__ signed int __a__i_1; 2 8 __extension__ signed int __b__i_1; -
src/tests/.expect/64/gccExtensions.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 extern signed int __x__i_1 asm ( "xx" ); 2 8 signed int __main__Fi_iPPCc__1(signed int __argc__i_1, const char **__argv__PPCc_1){ … … 168 174 } 169 175 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return __main__Fi_iPPCc__1(argc, argv); } 176 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 177 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 178 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 179 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 180 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 181 extern signed int printf(const char *__restrict __format, ...); 170 182 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 171 183 signed int main(signed int __argc__i_1, char **__argv__PPc_1, char **__envp__PPc_1){ -
src/tests/.expect/64/literals.txt
r0fe4e62 rf5c3b6c 1 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 2 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 3 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 4 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 5 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 6 extern signed int printf(const char *__restrict __format, ...); 1 7 void __for_each__A0_2_0_0____operator_assign__PFd0_Rd0d0____constructor__PF_Rd0____constructor__PF_Rd0d0____destructor__PF_Rd0____operator_assign__PFd1_Rd1d1____constructor__PF_Rd1____constructor__PF_Rd1d1____destructor__PF_Rd1____operator_preincr__PFd0_Rd0____operator_predecr__PFd0_Rd0____operator_equal__PFi_d0d0____operator_notequal__PFi_d0d0____operator_deref__PFRd1_d0__F_d0d0PF_d1___1(__attribute__ ((unused)) void (*_adapterF_9telt_type__P)(void (*__anonymous_object0)(), void *__anonymous_object1), __attribute__ ((unused)) void *(*_adapterFP9telt_type_14titerator_type_M_P)(void (*__anonymous_object2)(), void *__anonymous_object3), __attribute__ ((unused)) signed int (*_adapterFi_14titerator_type14titerator_type_M_PP)(void (*__anonymous_object4)(), void *__anonymous_object5, void *__anonymous_object6), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type_P_M)(void (*__anonymous_object7)(), __attribute__ ((unused)) void *___retval__operator_preincr__14titerator_type_1, void *__anonymous_object8), __attribute__ ((unused)) void (*_adapterF_P9telt_type9telt_type__MP)(void (*__anonymous_object9)(), void *__anonymous_object10, void *__anonymous_object11), __attribute__ ((unused)) void (*_adapterF9telt_type_P9telt_type9telt_type_P_MP)(void (*__anonymous_object12)(), __attribute__ ((unused)) void *___retval__operator_assign__9telt_type_1, void *__anonymous_object13, void *__anonymous_object14), __attribute__ ((unused)) void (*_adapterF_P14titerator_type14titerator_type__MP)(void (*__anonymous_object15)(), void *__anonymous_object16, void *__anonymous_object17), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type14titerator_type_P_MP)(void (*__anonymous_object18)(), __attribute__ ((unused)) void *___retval__operator_assign__14titerator_type_1, void *__anonymous_object19, void *__anonymous_object20), __attribute__ ((unused)) unsigned long int _sizeof_14titerator_type, __attribute__ ((unused)) unsigned long int _alignof_14titerator_type, __attribute__ ((unused)) unsigned long int _sizeof_9telt_type, __attribute__ ((unused)) unsigned long int _alignof_9telt_type, __attribute__ ((unused)) void *(*___operator_assign__PF14titerator_type_R14titerator_type14titerator_type__1)(void *__anonymous_object21, void *__anonymous_object22), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type__1)(void *__anonymous_object23), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type14titerator_type__1)(void *__anonymous_object24, void *__anonymous_object25), __attribute__ ((unused)) void (*___destructor__PF_R14titerator_type__1)(void *__anonymous_object26), __attribute__ ((unused)) void *(*___operator_assign__PF9telt_type_R9telt_type9telt_type__1)(void *__anonymous_object27, void *__anonymous_object28), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type__1)(void *__anonymous_object29), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type9telt_type__1)(void *__anonymous_object30, void *__anonymous_object31), __attribute__ ((unused)) void (*___destructor__PF_R9telt_type__1)(void *__anonymous_object32), __attribute__ ((unused)) void *(*___operator_preincr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object33), __attribute__ ((unused)) void *(*___operator_predecr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object34), __attribute__ ((unused)) signed int (*___operator_equal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object35, void *__anonymous_object36), __attribute__ ((unused)) signed int (*___operator_notequal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object37, void *__anonymous_object38), __attribute__ ((unused)) void *(*___operator_deref__PFR9telt_type_14titerator_type__1)(void *__anonymous_object39), void *__begin__14titerator_type_1, void *__end__14titerator_type_1, void (*__func__PF_9telt_type__1)(void *__anonymous_object40)); 2 8 void __for_each_reverse__A0_2_0_0____operator_assign__PFd0_Rd0d0____constructor__PF_Rd0____constructor__PF_Rd0d0____destructor__PF_Rd0____operator_assign__PFd1_Rd1d1____constructor__PF_Rd1____constructor__PF_Rd1d1____destructor__PF_Rd1____operator_preincr__PFd0_Rd0____operator_predecr__PFd0_Rd0____operator_equal__PFi_d0d0____operator_notequal__PFi_d0d0____operator_deref__PFRd1_d0__F_d0d0PF_d1___1(__attribute__ ((unused)) void (*_adapterF_9telt_type__P)(void (*__anonymous_object41)(), void *__anonymous_object42), __attribute__ ((unused)) void *(*_adapterFP9telt_type_14titerator_type_M_P)(void (*__anonymous_object43)(), void *__anonymous_object44), __attribute__ ((unused)) signed int (*_adapterFi_14titerator_type14titerator_type_M_PP)(void (*__anonymous_object45)(), void *__anonymous_object46, void *__anonymous_object47), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type_P_M)(void (*__anonymous_object48)(), __attribute__ ((unused)) void *___retval__operator_preincr__14titerator_type_1, void *__anonymous_object49), __attribute__ ((unused)) void (*_adapterF_P9telt_type9telt_type__MP)(void (*__anonymous_object50)(), void *__anonymous_object51, void *__anonymous_object52), __attribute__ ((unused)) void (*_adapterF9telt_type_P9telt_type9telt_type_P_MP)(void (*__anonymous_object53)(), __attribute__ ((unused)) void *___retval__operator_assign__9telt_type_1, void *__anonymous_object54, void *__anonymous_object55), __attribute__ ((unused)) void (*_adapterF_P14titerator_type14titerator_type__MP)(void (*__anonymous_object56)(), void *__anonymous_object57, void *__anonymous_object58), __attribute__ ((unused)) void (*_adapterF14titerator_type_P14titerator_type14titerator_type_P_MP)(void (*__anonymous_object59)(), __attribute__ ((unused)) void *___retval__operator_assign__14titerator_type_1, void *__anonymous_object60, void *__anonymous_object61), __attribute__ ((unused)) unsigned long int _sizeof_14titerator_type, __attribute__ ((unused)) unsigned long int _alignof_14titerator_type, __attribute__ ((unused)) unsigned long int _sizeof_9telt_type, __attribute__ ((unused)) unsigned long int _alignof_9telt_type, __attribute__ ((unused)) void *(*___operator_assign__PF14titerator_type_R14titerator_type14titerator_type__1)(void *__anonymous_object62, void *__anonymous_object63), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type__1)(void *__anonymous_object64), __attribute__ ((unused)) void (*___constructor__PF_R14titerator_type14titerator_type__1)(void *__anonymous_object65, void *__anonymous_object66), __attribute__ ((unused)) void (*___destructor__PF_R14titerator_type__1)(void *__anonymous_object67), __attribute__ ((unused)) void *(*___operator_assign__PF9telt_type_R9telt_type9telt_type__1)(void *__anonymous_object68, void *__anonymous_object69), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type__1)(void *__anonymous_object70), __attribute__ ((unused)) void (*___constructor__PF_R9telt_type9telt_type__1)(void *__anonymous_object71, void *__anonymous_object72), __attribute__ ((unused)) void (*___destructor__PF_R9telt_type__1)(void *__anonymous_object73), __attribute__ ((unused)) void *(*___operator_preincr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object74), __attribute__ ((unused)) void *(*___operator_predecr__PF14titerator_type_R14titerator_type__1)(void *__anonymous_object75), __attribute__ ((unused)) signed int (*___operator_equal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object76, void *__anonymous_object77), __attribute__ ((unused)) signed int (*___operator_notequal__PFi_14titerator_type14titerator_type__1)(void *__anonymous_object78, void *__anonymous_object79), __attribute__ ((unused)) void *(*___operator_deref__PFR9telt_type_14titerator_type__1)(void *__anonymous_object80), void *__begin__14titerator_type_1, void *__end__14titerator_type_1, void (*__func__PF_9telt_type__1)(void *__anonymous_object81)); … … 1371 1377 } 1372 1378 static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return __main__Fi___1(); } 1379 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(unsigned long int __size); 1380 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 1381 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 1382 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern signed int atexit(void (*__func)(void)); 1383 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(signed int __status); 1384 extern signed int printf(const char *__restrict __format, ...); 1373 1385 static inline signed int invoke_main(signed int argc, char **argv, char **envp); 1374 1386 signed int main(signed int __argc__i_1, char **__argv__PPc_1, char **__envp__PPc_1){ -
src/tests/boundedBuffer.c
r0fe4e62 rf5c3b6c 1 // 1 // 2 2 // The contents of this file are covered under the licence agreement in the 3 3 // file "LICENCE" distributed with Cforall. 4 // 5 // boundedBuffer.c -- 6 // 4 // 5 // boundedBuffer.c -- 6 // 7 7 // Author : Peter A. Buhr 8 8 // Created On : Mon Oct 30 12:45:13 2017 … … 10 10 // Last Modified On : Mon Oct 30 23:02:46 2017 11 11 // Update Count : 9 12 // 12 // 13 13 14 14 #include <stdlib> … … 31 31 32 32 void insert( Buffer & mutex buffer, int elem ) { 33 if ( buffer.count == 20 ) wait( buffer.empty );33 if ( buffer.count == 20 ) wait( &buffer.empty ); 34 34 buffer.elements[buffer.back] = elem; 35 35 buffer.back = ( buffer.back + 1 ) % 20; 36 36 buffer.count += 1; 37 signal( buffer.full );37 signal( &buffer.full ); 38 38 } 39 39 int remove( Buffer & mutex buffer ) { 40 if ( buffer.count == 0 ) wait( buffer.full );40 if ( buffer.count == 0 ) wait( &buffer.full ); 41 41 int elem = buffer.elements[buffer.front]; 42 42 buffer.front = ( buffer.front + 1 ) % 20; 43 43 buffer.count -= 1; 44 signal( buffer.empty );44 signal( &buffer.empty ); 45 45 return elem; 46 46 } -
src/tests/datingService.c
r0fe4e62 rf5c3b6c 1 // -*- Mode: C -*- 2 // 1 // -*- Mode: C -*- 2 // 3 3 // The contents of this file are covered under the licence agreement in the 4 4 // file "LICENCE" distributed with Cforall. 5 // 6 // datingService.c -- 7 // 5 // 6 // datingService.c -- 7 // 8 8 // Author : Peter A. Buhr 9 9 // Created On : Mon Oct 30 12:56:20 2017 … … 11 11 // Last Modified On : Mon Oct 30 23:02:11 2017 12 12 // Update Count : 15 13 // 13 // 14 14 15 15 #include <stdlib> // random … … 18 18 #include <thread> 19 19 #include <unistd.h> // getpid 20 21 bool empty( condition & c ) { 22 return c.blocked.head == NULL; 23 } 20 24 21 25 enum { NoOfPairs = 20 }; … … 27 31 28 32 unsigned int girl( DatingService & mutex ds, unsigned int PhoneNo, unsigned int ccode ) { 29 if ( is_empty( ds.Boys[ccode] ) ) {30 wait( ds.Girls[ccode] );33 if ( empty( ds.Boys[ccode] ) ) { 34 wait( &ds.Girls[ccode] ); 31 35 ds.GirlPhoneNo = PhoneNo; 32 36 } else { 33 37 ds.GirlPhoneNo = PhoneNo; 34 signal_block( ds.Boys[ccode] );38 signal_block( &ds.Boys[ccode] ); 35 39 } // if 36 40 return ds.BoyPhoneNo; … … 38 42 39 43 unsigned int boy( DatingService & mutex ds, unsigned int PhoneNo, unsigned int ccode ) { 40 if ( is_empty( ds.Girls[ccode] ) ) {41 wait( ds.Boys[ccode] );44 if ( empty( ds.Girls[ccode] ) ) { 45 wait( &ds.Boys[ccode] ); 42 46 ds.BoyPhoneNo = PhoneNo; 43 47 } else { 44 48 ds.BoyPhoneNo = PhoneNo; 45 signal_block( ds.Girls[ccode] );49 signal_block( &ds.Girls[ccode] ); 46 50 } // if 47 51 return ds.GirlPhoneNo; -
src/tests/sched-int-barge.c
r0fe4e62 rf5c3b6c 73 73 if( action == c.do_wait1 || action == c.do_wait2 ) { 74 74 c.state = WAIT; 75 wait( cond );75 wait( &cond ); 76 76 77 77 if(c.state != SIGNAL) { … … 83 83 c.state = SIGNAL; 84 84 85 signal( cond );86 signal( cond );85 signal( &cond ); 86 signal( &cond ); 87 87 } 88 88 else { -
src/tests/sched-int-block.c
r0fe4e62 rf5c3b6c 47 47 //------------------------------------------------------------------------------ 48 48 void wait_op( global_data_t & mutex a, global_data_t & mutex b, unsigned i ) { 49 wait( cond, (uintptr_t)this_thread );49 wait( &cond, (uintptr_t)this_thread ); 50 50 51 51 yield( random( 10 ) ); … … 74 74 [a.last_thread, b.last_thread, a.last_signaller, b.last_signaller] = this_thread; 75 75 76 if( !is_empty( cond ) ) {76 if( !is_empty( &cond ) ) { 77 77 78 thread_desc * next = front( cond );78 thread_desc * next = front( &cond ); 79 79 80 if( ! signal_block( cond ) ) {80 if( ! signal_block( &cond ) ) { 81 81 sout | "ERROR expected to be able to signal" | endl; 82 82 abort(); -
src/tests/sched-int-disjoint.c
r0fe4e62 rf5c3b6c 59 59 // Waiting logic 60 60 bool wait( global_t & mutex m, global_data_t & mutex d ) { 61 wait( cond );61 wait( &cond ); 62 62 if( d.state != SIGNAL ) { 63 63 sout | "ERROR barging!" | endl; … … 80 80 //------------------------------------------------------------------------------ 81 81 // Signalling logic 82 void signal( condition &cond, global_t & mutex a, global_data_t & mutex b ) {82 void signal( condition * cond, global_t & mutex a, global_data_t & mutex b ) { 83 83 b.state = SIGNAL; 84 84 signal( cond ); … … 86 86 87 87 void logic( global_t & mutex a ) { 88 signal( cond, a, data );88 signal( &cond, a, data ); 89 89 90 90 yield( random( 10 ) ); -
src/tests/sched-int-wait.c
r0fe4e62 rf5c3b6c 41 41 //---------------------------------------------------------------------------------------------------- 42 42 // Tools 43 void signal( condition &cond, global_t & mutex a, global_t & mutex b ) {43 void signal( condition * cond, global_t & mutex a, global_t & mutex b ) { 44 44 signal( cond ); 45 45 } 46 46 47 void signal( condition &cond, global_t & mutex a, global_t & mutex b, global_t & mutex c ) {47 void signal( condition * cond, global_t & mutex a, global_t & mutex b, global_t & mutex c ) { 48 48 signal( cond ); 49 49 } 50 50 51 void wait( condition &cond, global_t & mutex a, global_t & mutex b ) {51 void wait( condition * cond, global_t & mutex a, global_t & mutex b ) { 52 52 wait( cond ); 53 53 } 54 54 55 void wait( condition &cond, global_t & mutex a, global_t & mutex b, global_t & mutex c ) {55 void wait( condition * cond, global_t & mutex a, global_t & mutex b, global_t & mutex c ) { 56 56 wait( cond ); 57 57 } … … 65 65 switch( action ) { 66 66 case 0: 67 signal( condABC, globalA, globalB, globalC );67 signal( &condABC, globalA, globalB, globalC ); 68 68 break; 69 69 case 1: 70 signal( condAB , globalA, globalB );70 signal( &condAB , globalA, globalB ); 71 71 break; 72 72 case 2: 73 signal( condBC , globalB, globalC );73 signal( &condBC , globalB, globalC ); 74 74 break; 75 75 case 3: 76 signal( condAC , globalA, globalC );76 signal( &condAC , globalA, globalC ); 77 77 break; 78 78 default: … … 88 88 void main( WaiterABC & this ) { 89 89 for( int i = 0; i < N; i++ ) { 90 wait( condABC, globalA, globalB, globalC );90 wait( &condABC, globalA, globalB, globalC ); 91 91 } 92 92 … … 98 98 void main( WaiterAB & this ) { 99 99 for( int i = 0; i < N; i++ ) { 100 wait( condAB , globalA, globalB );100 wait( &condAB , globalA, globalB ); 101 101 } 102 102 … … 108 108 void main( WaiterAC & this ) { 109 109 for( int i = 0; i < N; i++ ) { 110 wait( condAC , globalA, globalC );110 wait( &condAC , globalA, globalC ); 111 111 } 112 112 … … 118 118 void main( WaiterBC & this ) { 119 119 for( int i = 0; i < N; i++ ) { 120 wait( condBC , globalB, globalC );120 wait( &condBC , globalB, globalC ); 121 121 } 122 122 -
src/tests/thread.c
r0fe4e62 rf5c3b6c 15 15 yield(); 16 16 } 17 V( *this.lock);17 V(this.lock); 18 18 } 19 19 20 20 void main(Second& this) { 21 P( *this.lock);21 P(this.lock); 22 22 for(int i = 0; i < 10; i++) { 23 23 sout | "Second : Suspend No." | i + 1 | endl;
Note:
See TracChangeset
for help on using the changeset viewer.