Changeset 5407cdc for doc/theses
- Timestamp:
- Apr 28, 2021, 4:56:50 PM (4 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 8d66610
- Parents:
- feacef9 (diff), b7fd2db6 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Location:
- doc/theses
- Files:
-
- 17 added
- 3 deleted
- 15 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/andrew_beach_MMath/Makefile
rfeacef9 r5407cdc 4 4 BUILD=out 5 5 TEXSRC=$(wildcard *.tex) 6 FIGSRC=$(wildcard *.fig) 6 7 BIBSRC=$(wildcard *.bib) 7 8 STYSRC=$(wildcard *.sty) … … 13 14 BASE= ${DOC:%.pdf=%} 14 15 16 RAWSRC=${TEXSRC} ${BIBSRC} ${STYSRC} ${CLSSRC} 17 FIGTEX=${FIGSRC:%.fig=${BUILD}/%.tex} 18 15 19 ### Special Rules: 16 20 … … 18 22 19 23 ### Commands: 20 LATEX=TEXINPUTS=${TEXLIB} pdflatex -halt-on-error -output-directory=${BUILD}24 LATEX=TEXINPUTS=${TEXLIB} latex -halt-on-error -output-directory=${BUILD} 21 25 BIBTEX=BIBINPUTS=${BIBLIB} bibtex 22 26 GLOSSARY=INDEXSTYLE=${BUILD} makeglossaries-lite … … 26 30 all: ${DOC} 27 31 28 ${BUILD}/${DOC}: ${TEXSRC} ${BIBSRC} ${STYSRC} ${CLSSRC} Makefile | ${BUILD} 32 # The main rule, it does all the tex/latex processing. 33 ${BUILD}/${BASE}.dvi: ${RAWSRC} ${FIGTEX} Makefile | ${BUILD} 29 34 ${LATEX} ${BASE} 30 35 ${BIBTEX} ${BUILD}/${BASE} … … 33 38 ${LATEX} ${BASE} 34 39 35 ${DOC}: ${BUILD}/${DOC} 36 cp $< $@ 40 # Convert xfig output to tex. (Generates \special declarations.) 41 ${FIGTEX}: ${BUILD}/%.tex: %.fig | ${BUILD} 42 fig2dev -L eepic $< > $@ 43 44 # Step through dvi & postscript to handle xfig specials. 45 %.pdf : ${BUILD}/%.dvi 46 dvipdf $^ $@ 37 47 38 48 ${BUILD}: -
doc/theses/andrew_beach_MMath/existing.tex
rfeacef9 r5407cdc 14 14 \section{Overloading and \lstinline{extern}} 15 15 \CFA has extensive overloading, allowing multiple definitions of the same name 16 to be defined .~\cite{Moss18}16 to be defined~\cite{Moss18}. 17 17 \begin{cfa} 18 18 char i; int i; double i; $\C[3.75in]{// variable overload}$ … … 46 46 pointers using the ampersand (@&@) instead of the pointer asterisk (@*@). \CFA 47 47 references may also be mutable or non-mutable. If mutable, a reference variable 48 may be assigned tousing the address-of operator (@&@), which converts the48 may be assigned using the address-of operator (@&@), which converts the 49 49 reference to a pointer. 50 50 \begin{cfa} … … 58 58 \section{Constructors and Destructors} 59 59 60 Both constructors and destructors are operators, which means they are just60 Both constructors and destructors are operators, which means they are 61 61 functions with special operator names rather than type names in \Cpp. The 62 62 special operator names may be used to call the functions explicitly (not … … 64 64 65 65 In general, operator names in \CFA are constructed by bracketing an operator 66 token with @?@, which indicates wherethe arguments. For example, infixed66 token with @?@, which indicates the position of the arguments. For example, infixed 67 67 multiplication is @?*?@ while prefix dereference is @*?@. This syntax make it 68 68 easy to tell the difference between prefix operations (such as @++?@) and … … 89 89 definition, \CFA creates a default and copy constructor, destructor and 90 90 assignment (like \Cpp). It is possible to define constructors/destructors for 91 basic and existing types .91 basic and existing types (unlike \Cpp). 92 92 93 93 \section{Polymorphism} … … 120 120 do_once(value); 121 121 } 122 void do_once( inti) { ... } // provide assertion123 inti;122 void do_once(@int@ i) { ... } // provide assertion 123 @int@ i; 124 124 do_twice(i); // implicitly pass assertion do_once to do_twice 125 125 \end{cfa} … … 172 172 declarations instead of parameters, returns, and local variable declarations. 173 173 \begin{cfa} 174 forall(dtype T)174 forall(dtype @T@) 175 175 struct node { 176 node(T) * next; // generic linked node 177 T * data; 178 } 176 node(@T@) * next; // generic linked node 177 @T@ * data; 178 } 179 node(@int@) inode; 179 180 \end{cfa} 180 181 The generic type @node(T)@ is an example of a polymorphic-type usage. Like \Cpp 181 template susage, a polymorphic-type usage must specify a type parameter.182 template usage, a polymorphic-type usage must specify a type parameter. 182 183 183 184 There are many other polymorphism features in \CFA but these are the ones used 184 185 by the exception system. 185 186 186 \section{Con currency}187 \CFA has a number of concurrency features: @thread@, @monitor@, @mutex@188 parameters, @coroutine@ and @generator@.The two features that interact with189 the exception system are @ thread@ and @coroutine@; they and their supporting187 \section{Control Flow} 188 \CFA has a number of advanced control-flow features: @generator@, @coroutine@, @monitor@, @mutex@ parameters, and @thread@. 189 The two features that interact with 190 the exception system are @coroutine@ and @thread@; they and their supporting 190 191 constructs are described here. 191 192 … … 216 217 CountUp countup; 217 218 \end{cfa} 218 Each coroutine has @main@ function, which takes a reference to a coroutine219 Each coroutine has a @main@ function, which takes a reference to a coroutine 219 220 object and returns @void@. 220 221 \begin{cfa}[numbers=left] … … 230 231 In this function, or functions called by this function (helper functions), the 231 232 @suspend@ statement is used to return execution to the coroutine's caller 232 without terminating the coroutine .233 without terminating the coroutine's function. 233 234 234 235 A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@. … … 242 243 @resume(countup).next@. 243 244 244 \subsection{Monitor s and Mutex}245 \subsection{Monitor and Mutex Parameter} 245 246 Concurrency does not guarantee ordering; without ordering results are 246 247 non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ … … 260 261 and only one runs at a time. 261 262 262 \subsection{Thread s}263 \subsection{Thread} 263 264 Functions, generators, and coroutines are sequential so there is only a single 264 265 (but potentially sophisticated) execution path in a program. Threads introduce … … 268 269 monitors and mutex parameters. For threads to work safely with other threads, 269 270 also requires mutual exclusion in the form of a communication rendezvous, which 270 also supports internal synchronization as for mutex objects. For exceptions 271 only t he basic two basic operations are important: threadfork and join.271 also supports internal synchronization as for mutex objects. For exceptions, 272 only two basic thread operations are important: fork and join. 272 273 273 274 Threads are created like coroutines with an associated @main@ function: -
doc/theses/andrew_beach_MMath/features.tex
rfeacef9 r5407cdc 2 2 3 3 This chapter covers the design and user interface of the \CFA 4 exception-handling mechanism. 4 exception-handling mechanism (EHM). % or exception system. 5 6 We will begin with an overview of EHMs in general. It is not a strict 7 definition of all EHMs nor an exaustive list of all possible features. 8 However it does cover the most common structure and features found in them. 9 10 % We should cover what is an exception handling mechanism and what is an 11 % exception before this. Probably in the introduction. Some of this could 12 % move there. 13 \paragraph{Raise / Handle} 14 An exception operation has two main parts: raise and handle. 15 These terms are sometimes also known as throw and catch but this work uses 16 throw/catch as a particular kind of raise/handle. 17 These are the two parts that the user will write themselves and may 18 be the only two pieces of the EHM that have any syntax in the language. 19 20 \subparagraph{Raise} 21 The raise is the starting point for exception handling. It marks the beginning 22 of exception handling by \newterm{raising} an excepion, which passes it to 23 the EHM. 24 25 Some well known examples include the @throw@ statements of \Cpp and Java and 26 the \codePy{raise} statement from Python. In real systems a raise may preform 27 some other work (such as memory management) but for the purposes of this 28 overview that can be ignored. 29 30 \subparagraph{Handle} 31 The purpose of most exception operations is to run some user code to handle 32 that exception. This code is given, with some other information, in a handler. 33 34 A handler has three common features: the previously mentioned user code, a 35 region of code they cover and an exception label/condition that matches 36 certain exceptions. 37 Only raises inside the covered region and raising exceptions that match the 38 label can be handled by a given handler. 39 Different EHMs will have different rules to pick a handler 40 if multipe handlers could be used such as ``best match" or ``first found". 41 42 The @try@ statements of \Cpp, Java and Python are common examples. All three 43 also show another common feature of handlers, they are grouped by the covered 44 region. 45 46 \paragraph{Propagation} 47 After an exception is raised comes what is usually the biggest step for the 48 EHM: finding and setting up the handler. The propogation from raise to 49 handler can be broken up into three different tasks: searching for a handler, 50 matching against the handler and installing the handler. 51 52 \subparagraph{Searching} 53 The EHM begins by searching for handlers that might be used to handle 54 the exception. Searching is usually independent of the exception that was 55 thrown as it looks for handlers that have the raise site in their covered 56 region. 57 This includes handlers in the current function, as well as any in callers 58 on the stack that have the function call in their covered region. 59 60 \subparagraph{Matching} 61 Each handler found has to be matched with the raised exception. The exception 62 label defines a condition that be use used with exception and decides if 63 there is a match or not. 64 65 In languages where the first match is used this step is intertwined with 66 searching, a match check is preformed immediately after the search finds 67 a possible handler. 68 69 \subparagraph{Installing} 70 After a handler is chosen it must be made ready to run. 71 The implementation can vary widely to fit with the rest of the 72 design of the EHM. The installation step might be trivial or it could be 73 the most expensive step in handling an exception. The latter tends to be the 74 case when stack unwinding is involved. 75 76 If a matching handler is not guarantied to be found the EHM will need a 77 different course of action here in the cases where no handler matches. 78 This is only required with unchecked exceptions as checked exceptions 79 (such as in Java) can make than guaranty. 80 This different action can also be installing a handler but it is usually an 81 implicat and much more general one. 82 83 \subparagraph{Hierarchy} 84 A common way to organize exceptions is in a hierarchical structure. 85 This is especially true in object-orientated languages where the 86 exception hierarchy is a natural extension of the object hierarchy. 87 88 Consider the following hierarchy of exceptions: 89 \begin{center} 90 \input{exception-hierarchy} 91 \end{center} 92 93 A handler labelled with any given exception can handle exceptions of that 94 type or any child type of that exception. The root of the exception hierarchy 95 (here \codeC{exception}) acts as a catch-all, leaf types catch single types 96 and the exceptions in the middle can be used to catch different groups of 97 related exceptions. 98 99 This system has some notable advantages, such as multiple levels of grouping, 100 the ability for libraries to add new exception types and the isolation 101 between different sub-hierarchies. 102 This design is used in \CFA even though it is not a object-orientated 103 language using different tools to create the hierarchy. 104 105 % Could I cite the rational for the Python IO exception rework? 106 107 \paragraph{Completion} 108 After the handler has finished the entire exception operation has to complete 109 and continue executing somewhere else. This step is usually simple, 110 both logically and in its implementation, as the installation of the handler 111 is usually set up to do most of the work. 112 113 The EHM can return control to many different places, 114 the most common are after the handler definition and after the raise. 115 116 \paragraph{Communication} 117 For effective exception handling, additional information is usually passed 118 from the raise to the handler. 119 So far only communication of the exceptions' identity has been covered. 120 A common method is putting fields into the exception instance and giving the 121 handler access to them. 5 122 6 123 \section{Virtuals} 7 Virtual types and casts are not part of the exception system nor are they 8 required for an exception system. But an object-oriented style hierarchy is a 9 great way of organizing exceptions so a minimal virtual system has been added 10 to \CFA. 11 12 The pattern of a simple hierarchy was borrowed from object-oriented 13 programming was chosen for several reasons. 14 The first is that it allows new exceptions to be added in user code 15 and in libraries independently of each other. Another is it allows for 16 different levels of exception grouping (all exceptions, all IO exceptions or 17 a particular IO exception). Also it also provides a simple way of passing 18 data back and forth across the throw. 19 20 Virtual types and casts are not required for a basic exception-system but are 21 useful for advanced exception features. However, \CFA is not object-oriented so 22 there is no obvious concept of virtuals. Hence, to create advanced exception 23 features for this work, I needed to design and implement a virtual-like 24 system for \CFA. 25 26 % NOTE: Maybe we should but less of the rational here. 27 Object-oriented languages often organized exceptions into a simple hierarchy, 28 \eg Java. 29 \begin{center} 30 \setlength{\unitlength}{4000sp}% 31 \begin{picture}(1605,612)(2011,-1951) 32 \put(2100,-1411){\vector(1, 0){225}} 33 \put(3450,-1411){\vector(1, 0){225}} 34 \put(3550,-1411){\line(0,-1){225}} 35 \put(3550,-1636){\vector(1, 0){150}} 36 \put(3550,-1636){\line(0,-1){225}} 37 \put(3550,-1861){\vector(1, 0){150}} 38 \put(2025,-1490){\makebox(0,0)[rb]{\LstBasicStyle{exception}}} 39 \put(2400,-1460){\makebox(0,0)[lb]{\LstBasicStyle{arithmetic}}} 40 \put(3750,-1460){\makebox(0,0)[lb]{\LstBasicStyle{underflow}}} 41 \put(3750,-1690){\makebox(0,0)[lb]{\LstBasicStyle{overflow}}} 42 \put(3750,-1920){\makebox(0,0)[lb]{\LstBasicStyle{zerodivide}}} 43 \end{picture}% 44 \end{center} 45 The hierarchy provides the ability to handle an exception at different degrees 46 of specificity (left to right). Hence, it is possible to catch a more general 47 exception-type in higher-level code where the implementation details are 48 unknown, which reduces tight coupling to the lower-level implementation. 49 Otherwise, low-level code changes require higher-level code changes, \eg, 50 changing from raising @underflow@ to @overflow@ at the low level means changing 51 the matching catch at the high level versus catching the general @arithmetic@ 52 exception. In detail, each virtual type may have a parent and can have any 53 number of children. A type's descendants are its children and its children's 54 descendants. A type may not be its own descendant. 55 56 The exception hierarchy allows a handler (@catch@ clause) to match multiple 57 exceptions, \eg a base-type handler catches both base and derived 58 exception-types. 59 \begin{cfa} 60 try { 61 ... 62 } catch(arithmetic &) { 63 ... // handle arithmetic, underflow, overflow, zerodivide 64 } 65 \end{cfa} 66 Most exception mechanisms perform a linear search of the handlers and select 67 the first matching handler, so the order of handers is now important because 68 matching is many to one. 69 70 Each virtual type needs an associated virtual table. A virtual table is a 71 structure with fields for all the virtual members of a type. A virtual type has 72 all the virtual members of its parent and can add more. It may also update the 73 values of the virtual members and often does. 124 Virtual types and casts are not part of \CFA's EHM nor are they required for 125 any EHM. But \CFA uses a hierarchial system of exceptions and this feature 126 is leveraged to create that. 127 128 % Maybe talk about why the virtual system is so minimal. 129 % Created for but not a part of the exception system. 130 131 The virtual system supports multiple ``trees" of types. Each tree is 132 a simple hierarchy with a single root type. Each type in a tree has exactly 133 one parent -- except for the root type which has zero parents -- and any 134 number of children. 135 Any type that belongs to any of these trees is called a virtual type. 136 137 % A type's ancestors are its parent and its parent's ancestors. 138 % The root type has no ancestors. 139 % A type's decendents are its children and its children's decendents. 140 141 Every virtual type also has a list of virtual members. Children inherit 142 their parent's list of virtual members but may add new members to it. 143 It is important to note that these are virtual members, not virtual methods 144 of object-orientated programming, and can be of any type. 145 However, since \CFA has function pointers and they are allowed, virtual 146 members can be used to mimic virtual methods. 147 148 Each virtual type has a unique id. 149 This unique id and all the virtual members are combined 150 into a virtual table type. Each virtual type has a pointer to a virtual table 151 as a hidden field. 152 153 Up until this point the virtual system is similar to ones found in 154 object-orientated languages but this where \CFA diverges. Objects encapsulate a 155 single set of behaviours in each type, universally across the entire program, 156 and indeed all programs that use that type definition. In this sense the 157 types are ``closed" and cannot be altered. 158 159 In \CFA types do not encapsulate any behaviour. Traits are local and 160 types can begin to statify a trait, stop satifying a trait or satify the same 161 trait in a different way at any lexical location in the program. 162 In this sense they are ``open" as they can change at any time. This means it 163 is implossible to pick a single set of functions that repersent the type's 164 implementation across the program. 165 166 \CFA side-steps this issue by not having a single virtual table for each 167 type. A user can define virtual tables which are filled in at their 168 declaration and given a name. Anywhere that name is visible, even if it was 169 defined locally inside a function (although that means it will not have a 170 static lifetime), it can be used. 171 Specifically, a virtual type is ``bound" to a virtual table which 172 sets the virtual members for that object. The virtual members can be accessed 173 through the object. 74 174 75 175 While much of the virtual infrastructure is created, it is currently only used … … 83 183 \Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be 84 184 a pointer to a virtual type. 85 The cast dynamically checks if the @EXPRESSION@ type is the same or a sub type185 The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type 86 186 of @TYPE@, and if true, returns a pointer to the 87 187 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). … … 101 201 \end{cfa} 102 202 The trait is defined over two types, the exception type and the virtual table 103 type. This should be one-to-one ,each exception type has only one virtual203 type. This should be one-to-one: each exception type has only one virtual 104 204 table type and vice versa. The only assertion in the trait is 105 205 @get_exception_vtable@, which takes a pointer of the exception type and 106 206 returns a reference to the virtual table type instance. 107 207 208 % TODO: This section, and all references to get_exception_vtable, are 209 % out-of-data. Perhaps wait until the update is finished before rewriting it. 108 210 The function @get_exception_vtable@ is actually a constant function. 109 Re cardless of the value passed in (including the null pointer) it should211 Regardless of the value passed in (including the null pointer) it should 110 212 return a reference to the virtual table instance for that type. 111 213 The reason it is a function instead of a constant is that it make type … … 119 221 % similar system I know of (except Agda's I guess) so I took it out. 120 222 121 There are two more traits for exceptions @is_termination_exception@ and 122 @is_resumption_exception@. They are defined as follows: 123 223 There are two more traits for exceptions defined as follows: 124 224 \begin{cfa} 125 225 trait is_termination_exception( … … 133 233 }; 134 234 \end{cfa} 135 136 In other words they make sure that a given type and virtual type is an 137 exception and defines one of the two default handlers. These default handlers 138 are used in the main exception handling operations \see{Exception Handling} 139 and their use will be detailed there. 140 141 However all three of these traits can be trickly to use directly. 142 There is a bit of repetition required but 235 Both traits ensure a pair of types are an exception type and its virtual table 236 and defines one of the two default handlers. The default handlers are used 237 as fallbacks and are discussed in detail in \VRef{s:ExceptionHandling}. 238 239 However, all three of these traits can be tricky to use directly. 240 While there is a bit of repetition required, 143 241 the largest issue is that the virtual table type is mangled and not in a user 144 facing way. So the re are three macros that can be used to wrap these traits145 when you need to referto the names:242 facing way. So these three macros are provided to wrap these traits to 243 simplify referring to the names: 146 244 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. 147 245 148 All t ake one or two arguments. The first argument is the name of the149 exception type. Its unmangled and mangled form are passedto the trait.246 All three take one or two arguments. The first argument is the name of the 247 exception type. The macro passes its unmangled and mangled form to the trait. 150 248 The second (optional) argument is a parenthesized list of polymorphic 151 arguments. This argument should onlywith polymorphic exceptions and the152 list willbe passed to both types.153 In the current set-up the base name and the polymorphic arguments have to154 match so these macros can be used without losing flexability.249 arguments. This argument is only used with polymorphic exceptions and the 250 list is be passed to both types. 251 In the current set-up, the two types always have the same polymorphic 252 arguments so these macros can be used without losing flexibility. 155 253 156 254 For example consider a function that is polymorphic over types that have a … … 162 260 163 261 \section{Exception Handling} 164 \ CFA provides two kinds of exception handling, termination and resumption.165 These twin operations are the core of the exception handling mechanism and 166 are the reason for the features of exceptions.262 \label{s:ExceptionHandling} 263 \CFA provides two kinds of exception handling: termination and resumption. 264 These twin operations are the core of \CFA's exception handling mechanism. 167 265 This section will cover the general patterns shared by the two operations and 168 266 then go on to cover the details each individual operation. 169 267 170 Both operations follow the same set of steps to do their operation. They both 171 start with the user preforming a throw on an exception. 172 Then there is the search for a handler, if one is found than the exception 173 is caught and the handler is run. After that control returns to normal 174 execution. 175 268 Both operations follow the same set of steps. 269 Both start with the user preforming a raise on an exception. 270 Then the exception propogates up the stack. 271 If a handler is found the exception is caught and the handler is run. 272 After that control returns to normal execution. 176 273 If the search fails a default handler is run and then control 177 returns to normal execution immediately. That is where the default handlers 178 @defaultTermiationHandler@ and @defaultResumptionHandler@ are used. 274 returns to normal execution after the raise. 275 276 This general description covers what the two kinds have in common. 277 Differences include how propogation is preformed, where exception continues 278 after an exception is caught and handled and which default handler is run. 179 279 180 280 \subsection{Termination} 181 281 \label{s:Termination} 182 183 Termination handling is more familiar kind and used in most programming 282 Termination handling is the familiar kind and used in most programming 184 283 languages with exception handling. 185 It is dynamic, non-local goto. If a throw is successful then the stack will 186 be unwound and control will (usually) continue in a different function on 187 the call stack. They are commonly used when an error has occured and recovery 188 is impossible in the current function. 284 It is dynamic, non-local goto. If the raised exception is matched and 285 handled the stack is unwound and control will (usually) continue the function 286 on the call stack that defined the handler. 287 Termination is commonly used when an error has occurred and recovery is 288 impossible locally. 189 289 190 290 % (usually) Control can continue in the current function but then a different 191 291 % control flow construct should be used. 192 292 193 A termination throwis started with the @throw@ statement:293 A termination raise is started with the @throw@ statement: 194 294 \begin{cfa} 195 295 throw EXPRESSION; 196 296 \end{cfa} 197 297 The expression must return a reference to a termination exception, where the 198 termination exception is any type that satifies @is_termination_exception@ 199 at the call site. 200 Through \CFA's trait system the functions in the traits are passed into the 201 throw code. A new @defaultTerminationHandler@ can be defined in any scope to 298 termination exception is any type that satisfies the trait 299 @is_termination_exception@ at the call site. 300 Through \CFA's trait system the trait functions are implicity passed into the 301 throw code and the EHM. 302 A new @defaultTerminationHandler@ can be defined in any scope to 202 303 change the throw's behavior (see below). 203 304 204 The throw will copy the provided exception into managed memory. It is the 205 user's responcibility to ensure the original exception is cleaned up if the 206 stack is unwound (allocating it on the stack should be sufficient). 207 208 Then the exception system searches the stack using the copied exception. 209 It starts starts from the throw and proceeds to the base of the stack, 305 The throw will copy the provided exception into managed memory to ensure 306 the exception is not destroyed if the stack is unwound. 307 It is the user's responsibility to ensure the original exception is cleaned 308 up wheither the stack is unwound or not. Allocating it on the stack is 309 usually sufficient. 310 311 Then propogation starts with the search. \CFA uses a ``first match" rule so 312 matching is preformed with the copied exception as the search continues. 313 It starts from the throwing function and proceeds to the base of the stack, 210 314 from callee to caller. 211 315 At each stack frame, a check is made for resumption handlers defined by the … … 214 318 try { 215 319 GUARDED_BLOCK 216 } catch (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) {320 } catch (EXCEPTION_TYPE$\(_1\)$ * [NAME$\(_1\)$]) { 217 321 HANDLER_BLOCK$\(_1\)$ 218 } catch (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) {322 } catch (EXCEPTION_TYPE$\(_2\)$ * [NAME$\(_2\)$]) { 219 323 HANDLER_BLOCK$\(_2\)$ 220 324 } 221 325 \end{cfa} 222 When viewed on its own a try statement will simply exceute the statements in223 @GUARDED_BLOCK@ and when those are finished the try statement finishes.326 When viewed on its own, a try statement will simply execute the statements 327 in @GUARDED_BLOCK@ and when those are finished the try statement finishes. 224 328 225 329 However, while the guarded statements are being executed, including any 226 functions they invoke, all the handlers following the try block are now 227 or any functions invoked from those 228 statements, throws an exception, and the exception 229 is not handled by a try statement further up the stack, the termination 230 handlers are searched for a matching exception type from top to bottom. 231 232 Exception matching checks the representation of the thrown exception-type is 233 the same or a descendant type of the exception types in the handler clauses. If 234 it is the same of a descendent of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ is 330 invoked functions, all the handlers in the statement are now on the search 331 path. If a termination exception is thrown and not handled further up the 332 stack they will be matched against the exception. 333 334 Exception matching checks the handler in each catch clause in the order 335 they appear, top to bottom. If the representation of the thrown exception type 336 is the same or a descendant of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ 337 (if provided) is 235 338 bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$ 236 339 are executed. If control reaches the end of the handler, the exception is 237 340 freed and control continues after the try statement. 238 341 239 If no handler is found during the search then the default handler is run. 342 If no termination handler is found during the search then the default handler 343 (@defaultTerminationHandler@) is run. 240 344 Through \CFA's trait system the best match at the throw sight will be used. 241 345 This function is run and is passed the copied exception. After the default 242 346 handler is run control continues after the throw statement. 243 347 244 There is a global @defaultTerminationHandler@ that cancels the current stack 245 with the copied exception. However it is generic over all exception types so 246 new default handlers can be defined for different exception types and so 247 different exception types can have different default handlers. 348 There is a global @defaultTerminationHandler@ that is polymorphic over all 349 exception types. Since it is so general a more specific handler can be 350 defined and will be used for those types, effectively overriding the handler 351 for particular exception type. 352 The global default termination handler performs a cancellation 353 \see{\VRef{s:Cancellation}} on the current stack with the copied exception. 248 354 249 355 \subsection{Resumption} 250 356 \label{s:Resumption} 251 357 252 Resumption exception handling is a less common formthan termination but is253 just as old~\cite{Goodenough75} and is in some sense simpler.254 It is a dynamic, non-local function call. If the throw is successful a255 closure will be taken from up the stack and executed, after which the throwing 256 function will continue executing.257 These are most often used when an error occur ed and if the error is repaired358 Resumption exception handling is less common than termination but is 359 just as old~\cite{Goodenough75} and is simpler in many ways. 360 It is a dynamic, non-local function call. If the raised exception is 361 matched a closure will be taken from up the stack and executed, 362 after which the raising function will continue executing. 363 These are most often used when an error occurred and if the error is repaired 258 364 then the function can continue. 259 365 … … 262 368 throwResume EXPRESSION; 263 369 \end{cfa} 264 The semantics of the @throwResume@ statement are like the @throw@, but the 265 expression has return a reference a type that satifies the trait 266 @is_resumption_exception@. The assertions from this trait are available to 370 It works much the same way as the termination throw. 371 The expression must return a reference to a resumption exception, 372 where the resumption exception is any type that satisfies the trait 373 @is_resumption_exception@ at the call site. 374 The assertions from this trait are available to 267 375 the exception system while handling the exception. 268 376 269 At runtime, no copies are made. As the stack is not unwound the exception and 377 At run-time, no exception copy is made. 378 As the stack is not unwound the exception and 270 379 any values on the stack will remain in scope while the resumption is handled. 271 380 272 Then the exception system searches the stack using the provided exception. 273 It starts starts from the throw and proceeds to the base of the stack, 274 from callee to caller. 381 The EHM then begins propogation. The search starts from the raise in the 382 resuming function and proceeds to the base of the stack, from callee to caller. 275 383 At each stack frame, a check is made for resumption handlers defined by the 276 384 @catchResume@ clauses of a @try@ statement. … … 278 386 try { 279 387 GUARDED_BLOCK 280 } catchResume (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) {388 } catchResume (EXCEPTION_TYPE$\(_1\)$ * [NAME$\(_1\)$]) { 281 389 HANDLER_BLOCK$\(_1\)$ 282 } catchResume (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) {390 } catchResume (EXCEPTION_TYPE$\(_2\)$ * [NAME$\(_2\)$]) { 283 391 HANDLER_BLOCK$\(_2\)$ 284 392 } 285 393 \end{cfa} 286 If the handlers are not involved in a search this will simply execute the 287 @GUARDED_BLOCK@ and then continue to the next statement. 288 Its purpose is to add handlers onto the stack. 289 (Note, termination and resumption handlers may be intermixed in a @try@ 290 statement but the kind of throw must be the same as the handler for it to be 291 considered as a possible match.) 292 293 If a search for a resumption handler reaches a try block it will check each 294 @catchResume@ clause, top-to-bottom. 295 At each handler if the thrown exception is or is a child type of 296 @EXCEPTION_TYPE@$_i$ then the a pointer to the exception is bound to 297 @NAME@$_i$ and then @HANDLER_BLOCK@$_i$ is executed. After the block is 298 finished control will return to the @throwResume@ statement. 394 % I wonder if there would be some good central place for this. 395 Note that termination handlers and resumption handlers may be used together 396 in a single try statement, intermixing @catch@ and @catchResume@ freely. 397 Each type of handler will only interact with exceptions from the matching 398 type of raise. 399 When a try statement is executed it simply executes the statements in the 400 @GUARDED_BLOCK@ and then finishes. 401 402 However, while the guarded statements are being executed, including any 403 invoked functions, all the handlers in the statement are now on the search 404 path. If a resumption exception is reported and not handled further up the 405 stack they will be matched against the exception. 406 407 Exception matching checks the handler in each catch clause in the order 408 they appear, top to bottom. If the representation of the thrown exception type 409 is the same or a descendant of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ 410 (if provided) is bound to a pointer to the exception and the statements in 411 @HANDLER_BLOCK@$_i$ are executed. 412 If control reaches the end of the handler, execution continues after the 413 the raise statement that raised the handled exception. 299 414 300 415 Like termination, if no resumption handler is found, the default handler … … 302 417 call sight according to \CFA's overloading rules. The default handler is 303 418 passed the exception given to the throw. When the default handler finishes 304 execution continues after the throwstatement.419 execution continues after the raise statement. 305 420 306 421 There is a global @defaultResumptionHandler@ is polymorphic over all 307 422 termination exceptions and preforms a termination throw on the exception. 308 The @defaultTerminationHandler@ for that throwis matched at the original309 throwstatement (the resumption @throwResume@) and it can be customized by423 The @defaultTerminationHandler@ for that raise is matched at the original 424 raise statement (the resumption @throwResume@) and it can be customized by 310 425 introducing a new or better match as well. 311 426 312 % \subsubsection? 313 427 \subsubsection{Resumption Marking} 314 428 A key difference between resumption and termination is that resumption does 315 429 not unwind the stack. A side effect that is that when a handler is matched … … 331 445 search and match the handler in the @catchResume@ clause. This will be 332 446 call and placed on the stack on top of the try-block. The second throw then 333 throws and will sea ch the same try block and put call another instance of the447 throws and will search the same try block and put call another instance of the 334 448 same handler leading to an infinite loop. 335 449 … … 337 451 can form with multiple handlers and different exception types. 338 452 339 To prevent all of these cases we mask sections of the stack, or equvilantly 340 the try statements on the stack, so that the resumption seach skips over 341 them and continues with the next unmasked section of the stack. 342 343 A section of the stack is marked when it is searched to see if it contains 344 a handler for an exception and unmarked when that exception has been handled 345 or the search was completed without finding a handler. 346 347 % This might need a diagram. But it is an important part of the justification 348 % of the design of the traversal order. 349 \begin{verbatim} 350 throwResume2 ----------. 351 | | 352 generated from handler | 353 | | 354 handler | 355 | | 356 throwResume1 -----. : 357 | | : 358 try | : search skip 359 | | : 360 catchResume <----' : 361 | | 362 \end{verbatim} 363 364 The rules can be remembered as thinking about what would be searched in 365 termination. So when a throw happens in a handler; a termination handler 366 skips everything from the original throw to the original catch because that 367 part of the stack has been unwound, a resumption handler skips the same 368 section of stack because it has been masked. 369 A throw in a default handler will preform the same search as the original 370 throw because; for termination nothing has been unwound, for resumption 371 the mask will be the same. 372 373 The symmetry with termination is why this pattern was picked. Other patterns, 374 such as marking just the handlers that caught, also work but lack the 375 symmetry whih means there is more to remember. 453 To prevent all of these cases we mark try statements on the stack. 454 A try statement is marked when a match check is preformed with it and an 455 exception. The statement will be unmarked when the handling of that exception 456 is completed or the search completes without finding a handler. 457 While a try statement is marked its handlers are never matched, effectify 458 skipping over it to the next try statement. 459 460 \begin{center} 461 \input{stack-marking} 462 \end{center} 463 464 These rules mirror what happens with termination. 465 When a termination throw happens in a handler the search will not look at 466 any handlers from the original throw to the original catch because that 467 part of the stack has been unwound. 468 A resumption raise in the same situation wants to search the entire stack, 469 but it will not try to match the exception with try statements in the section 470 that would have been unwound as they are marked. 471 472 The symmetry between resumption termination is why this pattern was picked. 473 Other patterns, such as marking just the handlers that caught, also work but 474 lack the symmetry means there are less rules to remember. 376 475 377 476 \section{Conditional Catch} … … 379 478 condition to further control which exceptions they handle: 380 479 \begin{cfa} 381 catch (EXCEPTION_TYPE * NAME; CONDITION)480 catch (EXCEPTION_TYPE * [NAME] ; CONDITION) 382 481 \end{cfa} 383 482 First, the same semantics is used to match the exception type. Second, if the … … 387 486 matches. Otherwise, the exception search continues as if the exception type 388 487 did not match. 389 \begin{cfa} 390 try { 391 f1 = open( ... ); 392 f2 = open( ... ); 488 489 The condition matching allows finer matching by allowing the match to check 490 more kinds of information than just the exception type. 491 \begin{cfa} 492 try { 493 handle1 = open( f1, ... ); 494 handle2 = open( f2, ... ); 495 handle3 = open( f3, ... ); 393 496 ... 394 497 } catch( IOFailure * f ; fd( f ) == f1 ) { 395 // only handle IO failure for f1 396 } 397 \end{cfa} 398 Note, catching @IOFailure@, checking for @f1@ in the handler, and reraising the 399 exception if not @f1@ is different because the reraise does not examine any of 400 remaining handlers in the current try statement. 401 402 \section{Rethrowing} 403 \colour{red}{From Andrew: I recomend we talk about why the language doesn't 404 have rethrows/reraises instead.} 405 406 \label{s:Rethrowing} 498 // Only handle IO failure for f1. 499 } catch( IOFailure * f ; fd( f ) == f3 ) { 500 // Only handle IO failure for f3. 501 } 502 // Can't handle a failure relating to f2 here. 503 \end{cfa} 504 In this example the file that experianced the IO error is used to decide 505 which handler should be run, if any at all. 506 507 \begin{comment} 508 % I know I actually haven't got rid of them yet, but I'm going to try 509 % to write it as if I had and see if that makes sense: 510 \section{Reraising} 511 \label{s:Reraising} 407 512 Within the handler block or functions called from the handler block, it is 408 513 possible to reraise the most recently caught exception with @throw@ or … … 423 528 is part of an unwound stack frame. To prevent this problem, a new default 424 529 handler is generated that does a program-level abort. 530 \end{comment} 531 532 \subsection{Comparison with Reraising} 533 A more popular way to allow handlers to match in more detail is to reraise 534 the exception after it has been caught if it could not be handled here. 535 On the surface these two features seem interchangable. 536 537 If we used @throw;@ to start a termination reraise then these two statements 538 would have the same behaviour: 539 \begin{cfa} 540 try { 541 do_work_may_throw(); 542 } catch(exception_t * exc ; can_handle(exc)) { 543 handle(exc); 544 } 545 \end{cfa} 546 547 \begin{cfa} 548 try { 549 do_work_may_throw(); 550 } catch(exception_t * exc) { 551 if (can_handle(exc)) { 552 handle(exc); 553 } else { 554 throw; 555 } 556 } 557 \end{cfa} 558 If there are further handlers after this handler only the first version will 559 check them. If multiple handlers on a single try block could handle the same 560 exception the translations get more complex but they are equivilantly 561 powerful. 562 563 Until stack unwinding comes into the picture. In termination handling, a 564 conditional catch happens before the stack is unwound, but a reraise happens 565 afterwards. Normally this might only cause you to loose some debug 566 information you could get from a stack trace (and that can be side stepped 567 entirely by collecting information during the unwind). But for \CFA there is 568 another issue, if the exception isn't handled the default handler should be 569 run at the site of the original raise. 570 571 There are two problems with this: the site of the original raise doesn't 572 exist anymore and the default handler might not exist anymore. The site will 573 always be removed as part of the unwinding, often with the entirety of the 574 function it was in. The default handler could be a stack allocated nested 575 function removed during the unwind. 576 577 This means actually trying to pretend the catch didn't happening, continuing 578 the original raise instead of starting a new one, is infeasible. 579 That is the expected behaviour for most languages and we can't replicate 580 that behaviour. 425 581 426 582 \section{Finally Clauses} 583 \label{s:FinallyClauses} 427 584 Finally clauses are used to preform unconditional clean-up when leaving a 428 scope . They are placed at the end of a try statement:585 scope and are placed at the end of a try statement after any handler clauses: 429 586 \begin{cfa} 430 587 try { … … 442 599 443 600 Execution of the finally block should always finish, meaning control runs off 444 the end of the block. This requirement ensures always continues as if the 445 finally clause is not present, \ie finally is for cleanup not changing control 446 flow. Because of this requirement, local control flow out of the finally block 601 the end of the block. This requirement ensures control always continues as if 602 the finally clause is not present, \ie finally is for cleanup not changing 603 control flow. 604 Because of this requirement, local control flow out of the finally block 447 605 is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or 448 606 @return@ that causes control to leave the finally block. Other ways to leave 449 607 the finally block, such as a long jump or termination are much harder to check, 450 and at best requiring additional run-time overhead, and so are mearly608 and at best requiring additional run-time overhead, and so are only 451 609 discouraged. 452 610 453 Not all languages with exceptionshave finally clauses. Notably \Cpp does611 Not all languages with unwinding have finally clauses. Notably \Cpp does 454 612 without it as descructors serve a similar role. Although destructors and 455 613 finally clauses can be used in many of the same areas they have their own 456 614 use cases like top-level functions and lambda functions with closures. 457 615 Destructors take a bit more work to set up but are much easier to reuse while 458 finally clauses are good for once offs and can include local information. 616 finally clauses are good for one-off uses and 617 can easily include local information. 459 618 460 619 \section{Cancellation} 620 \label{s:Cancellation} 461 621 Cancellation is a stack-level abort, which can be thought of as as an 462 uncatchable termination. It unwinds the entire ty of thecurrent stack, and if622 uncatchable termination. It unwinds the entire current stack, and if 463 623 possible forwards the cancellation exception to a different stack. 464 624 … … 466 626 There is no special statement for starting a cancellation; instead the standard 467 627 library function @cancel_stack@ is called passing an exception. Unlike a 468 throw, this exception is not used in matching only to pass information about628 raise, this exception is not used in matching only to pass information about 469 629 the cause of the cancellation. 470 (This also means matching cannot fail so there is no default handler either.)471 472 After @cancel_stack@ is called the exception is copied into the exception473 handling mechanism's memory. Then the entirety ofthe current stack is630 (This also means matching cannot fail so there is no default handler.) 631 632 After @cancel_stack@ is called the exception is copied into the EHM's memory 633 and the current stack is 474 634 unwound. After that it depends one which stack is being cancelled. 475 635 \begin{description} 476 636 \item[Main Stack:] 477 637 The main stack is the one used by the program main at the start of execution, 478 and is the only stack in a sequential program. Even in a concurrent program 479 the main stack is only dependent on the environment that started the program. 480 Hence, when the main stack is cancelled there is nowhere else in the program 481 to notify. After the stack is unwound, there is a program-level abort. 638 and is the only stack in a sequential program. 639 After the main stack is unwound there is a program-level abort. 640 641 There are two reasons for this. The first is that it obviously had to do this 642 in a sequential program as there is nothing else to notify and the simplicity 643 of keeping the same behaviour in sequential and concurrent programs is good. 644 Also, even in concurrent programs there is no stack that an innate connection 645 to, so it would have be explicitly managed. 482 646 483 647 \item[Thread Stack:] 484 A thread stack is created for a @thread@ object or object that satisfies the 485 @is_thread@ trait. A thread only has two points of communication that must 486 happen: start and join. As the thread must be running to perform a 487 cancellation, it must occur after start and before join, so join is used 488 for communication here. 489 After the stack is unwound, the thread halts and waits for 490 another thread to join with it. The joining thread checks for a cancellation, 491 and if present, resumes exception @ThreadCancelled@. 492 493 There is a subtle difference between the explicit join (@join@ function) and 494 implicit join (from a destructor call). The explicit join takes the default 495 handler (@defaultResumptionHandler@) from its calling context, which is used if 496 the exception is not caught. The implicit join does a program abort instead. 497 498 This semantics is for safety. If an unwind is triggered while another unwind 499 is underway only one of them can proceed as they both want to ``consume'' the 500 stack. Letting both try to proceed leads to very undefined behaviour. 501 Both termination and cancellation involve unwinding and, since the default 502 @defaultResumptionHandler@ preforms a termination that could more easily 503 happen in an implicate join inside a destructor. So there is an error message 504 and an abort instead. 505 \todo{Perhaps have a more general disucssion of unwind collisions before 506 this point.} 507 508 The recommended way to avoid the abort is to handle the intial resumption 509 from the implicate join. If required you may put an explicate join inside a 510 finally clause to disable the check and use the local 511 @defaultResumptionHandler@ instead. 512 513 \item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object 514 or object that satisfies the @is_coroutine@ trait. A coroutine only knows of 515 two other coroutines, its starter and its last resumer. Of the two the last 516 resumer has the tightest coupling to the coroutine it activated and the most 517 up-to-date information. 518 519 Hence, cancellation of the active coroutine is forwarded to the last resumer 520 after the stack is unwound. When the resumer restarts, it resumes exception 521 @CoroutineCancelled@, which is polymorphic over the coroutine type and has a 522 pointer to the cancelled coroutine. 523 524 The resume function also has an assertion that the @defaultResumptionHandler@ 525 for the exception. So it will use the default handler like a regular throw. 648 A thread stack is created for a \CFA @thread@ object or object that satisfies 649 the @is_thread@ trait. 650 After a thread stack is unwound there exception is stored until another 651 thread attempts to join with it. Then the exception @ThreadCancelled@, 652 which stores a reference to the thread and to the exception passed to the 653 cancellation, is reported from the join. 654 There is one difference between an explicit join (with the @join@ function) 655 and an implicit join (from a destructor call). The explicit join takes the 656 default handler (@defaultResumptionHandler@) from its calling context while 657 the implicit join provides its own which does a program abort if the 658 @ThreadCancelled@ exception cannot be handled. 659 660 Communication is done at join because a thread only has to have to points of 661 communication with other threads: start and join. 662 Since a thread must be running to perform a cancellation (and cannot be 663 cancelled from another stack), the cancellation must be after start and 664 before the join. So join is the one that we will use. 665 666 % TODO: Find somewhere to discuss unwind collisions. 667 The difference between the explicit and implicit join is for safety and 668 debugging. It helps prevent unwinding collisions by avoiding throwing from 669 a destructor and prevents cascading the error across multiple threads if 670 the user is not equipped to deal with it. 671 Also you can always add an explicit join if that is the desired behaviour. 672 673 \item[Coroutine Stack:] 674 A coroutine stack is created for a @coroutine@ object or object that 675 satisfies the @is_coroutine@ trait. 676 After a coroutine stack is unwound control returns to the resume function 677 that most recently resumed it. The resume statement reports a 678 @CoroutineCancelled@ exception, which contains a references to the cancelled 679 coroutine and the exception used to cancel it. 680 The resume function also takes the @defaultResumptionHandler@ from the 681 caller's context and passes it to the internal report. 682 683 A coroutine knows of two other coroutines, its starter and its last resumer. 684 The starter has a much more distant connection while the last resumer just 685 (in terms of coroutine state) called resume on this coroutine, so the message 686 is passed to the latter. 526 687 \end{description} -
doc/theses/andrew_beach_MMath/future.tex
rfeacef9 r5407cdc 83 83 patterns to find the handler. 84 84 85 \section{Checked Exceptions} 86 Checked exceptions make exceptions part of a function's type by adding the 87 exception signature. An exception signature must declare all checked 88 exceptions that could propogate from the function (either because they were 89 raised inside the function or came from a sub-function). This improves safety 90 by making sure every checked exception is either handled or consciously 91 passed on. 92 93 However checked exceptions were never seriously considered for this project 94 for two reasons. The first is due to time constraints, even copying an 95 existing checked exception system would be pushing the remaining time and 96 trying to address the second problem would take even longer. The second 97 problem is that checked exceptions have some real usability trade-offs in 98 exchange for the increased safety. 99 100 These trade-offs are most problematic when trying to pass exceptions through 101 higher-order functions from the functions the user passed into the 102 higher-order function. There are no well known solutions to this problem 103 that were statifactory for \CFA (which carries some of C's flexability 104 over safety design) so one would have to be researched and developed. 105 106 Follow-up work might add checked exceptions to \CFA, possibly using 107 polymorphic exception signatures, a form of tunneling\cite{Zhang19} or 108 checked and unchecked raises. 109 85 110 \section{Zero-Cost Try} 86 111 \CFA does not have zero-cost try-statements because the compiler generates C -
doc/theses/andrew_beach_MMath/implement.tex
rfeacef9 r5407cdc 13 13 library. 14 14 15 \subsection{Virtual Type} 16 Virtual types only have one change to their structure, the addition of a 17 pointer to the virtual table. This is always the first field so that 18 if it is cast to a supertype the field's location is still known. 19 20 This field is set as part of all new generated constructors. 21 \todo{They only come as part exceptions and don't work.} 22 After the object is created the field is constant. 23 24 However it can be read from, internally it is just a regular field called 25 @virtual_table@. Dereferencing it gives the virtual table and access to the 26 type's virtual members. 27 15 28 \subsection{Virtual Table} 29 Every time a virtual type is defined the new virtual table type must also be 30 defined. 31 32 The unique instance is important because the address of the virtual table 33 instance is used as the identifier for the virtual type. So a pointer to the 34 virtual table and the ID for the virtual type are interchangable. 35 \todo{Unique instances might be going so we will have to talk about the new 36 system instead.} 37 38 The first step in putting it all together is to create the virtual table type. 39 The virtual table type is just a structure and can be described in terms of 40 its fields. The first field is always the parent type ID (or a pointer to 41 the parent virtual table) or 0 (the null pointer). 42 Next are other fields on the parent virtual table are repeated. 43 Finally are the fields used to store any new virtual members of the new 44 The virtual type 45 16 46 The virtual system is accessed through a private constant field inserted at the 17 47 beginning of every virtual type, called the virtual-table pointer. This field 18 48 points at a type's virtual table and is assigned during the object's 19 construction. 49 construction. The address of a virtual table acts as the unique identifier for 20 50 the virtual type, and the first field of a virtual table is a pointer to the 21 parent virtual-table or @0p@. 51 parent virtual-table or @0p@. The remaining fields are duplicated from the 22 52 parent tables in this type's inheritance chain, followed by any fields this type 23 introduces. Parent fields are duplicated so they can be changed ( \CC24 \lstinline[language=c++]|override|), so that references to the dispatched type53 introduces. Parent fields are duplicated so they can be changed (all virtual 54 members are overridable), so that references to the dispatched type 25 55 are replaced with the current virtual type. 26 \PAB{Can you create a simple diagram of the layout?}27 56 % These are always taken by pointer or reference. 57 58 % Simple ascii diragram: 59 \begin{verbatim} 60 parent_pointer \ 61 parent_field0 | 62 ... | Same layout as parent. 63 parent_fieldN / 64 child_field0 65 ... 66 child_fieldN 67 \end{verbatim} 68 \todo{Refine the diagram} 28 69 29 70 % For each virtual type, a virtual table is constructed. This is both a new type … … 34 75 A virtual table is created when the virtual type is created. The name of the 35 76 type is created by mangling the name of the base type. The name of the instance 36 is also generated by name mangling. 77 is also generated by name mangling. The fields are initialized automatically. 37 78 The parent field is initialized by getting the type of the parent field and 38 79 using that to calculate the mangled name of the parent's virtual table type. … … 67 108 \begin{sloppypar} 68 109 Coroutines and threads need instances of @CoroutineCancelled@ and 69 @ThreadCancelled@ respectively to use all of their functionality. 110 @ThreadCancelled@ respectively to use all of their functionality. When a new 70 111 data type is declared with @coroutine@ or @thread@ the forward declaration for 71 112 the instance is created as well. The definition of the virtual table is created … … 80 121 The function is 81 122 \begin{cfa} 82 void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent, 123 void * __cfa__virtual_cast( 124 struct __cfa__parent_vtable const * parent, 83 125 struct __cfa__parent_vtable const * const * child ); 84 }85 126 \end{cfa} 86 and it is implemented in the standard library. It takes a pointer to the target 87 type's virtual table and the object pointer being cast. The function performs a 88 linear search starting at the object's virtual-table and walking through the 89 the parent pointers, checking to if it or any of its ancestors are the same as 90 the target-type virtual table-pointer. 91 92 For the generated code, a forward declaration of the virtual works as follows. 93 There is a forward declaration of @__cfa__virtual_cast@ in every \CFA file so 94 it can just be used. The object argument is the expression being cast so that 95 is just placed in the argument list. 96 97 To build the target type parameter, the compiler creates a mapping from 98 concrete type-name -- so for polymorphic types the parameters are filled in -- 99 to virtual table address. Every virtual table declaration is added to the this 100 table; repeats are ignored unless they have conflicting definitions. Note, 101 these declarations do not have to be in scope, but they should usually be 102 introduced as part of the type definition. 103 104 \PAB{I do not understood all of \VRef{s:VirtualSystem}. I think you need to 105 write more to make it clear.} 106 127 and it is implemented in the standard library. The structure reperents the 128 head of a vtable which is the pointer to the parent virtual table. The 129 @parent@ points directly at the parent type virtual table while the @child@ 130 points at the object of the (possibe) child type. 131 132 In terms of the virtual cast expression, @parent@ comes from looking up the 133 type being cast to and @child@ is the result of the expression being cast. 134 Because the complier outputs C code, some type C type casts are also used. 135 The last bit of glue is an map that saves every virtual type the compiler 136 sees. This is used to check the type used in a virtual cast is a virtual 137 type and to get its virtual table. 138 (It also checks for conflicting definitions.) 139 140 Inside the function it is a simple conditional. If the type repersented by 141 @parent@ is or is an ancestor of the type repersented by @*child@ (it 142 requires one more level of derefence to pass through the object) then @child@ 143 is returned, otherwise the null pointer is returned. 144 145 The check itself is preformed is a simple linear search. If the child 146 virtual table or any of its ancestors (which are retreved through the first 147 field of every virtual table) are the same as the parent virtual table then 148 the cast succeeds. 107 149 108 150 \section{Exceptions} … … 121 163 stack. On function entry and return, unwinding is handled directly by the code 122 164 embedded in the function. Usually, the stack-frame size is known statically 123 based on parameter and local variable declarations. 165 based on parameter and local variable declarations. For dynamically-sized 124 166 local variables, a runtime computation is necessary to know the frame 125 167 size. Finally, a function's frame-size may change during execution as local … … 179 221 180 222 To use libunwind, each function must have a personality function and a Language 181 Specific Data Area (LSDA). 223 Specific Data Area (LSDA). The LSDA has the unique information for each 182 224 function to tell the personality function where a function is executing, its 183 current stack frame, and what handlers should be checked. 225 current stack frame, and what handlers should be checked. Theoretically, the 184 226 LSDA can contain any information but conventionally it is a table with entries 185 227 representing regions of the function and what has to be done there during … … 196 238 197 239 The GCC compilation flag @-fexceptions@ causes the generation of an LSDA and 198 attaches its personality function. \PAB{to what is it attached?} However, this 199 flag only handles the cleanup attribute 240 attaches its personality function. However, this 241 flag only handles the cleanup attribute: 242 \todo{Peter: What is attached? Andrew: It uses the .cfi\_personality directive 243 and that's all I know.} 200 244 \begin{cfa} 201 245 void clean_up( int * var ) { ... } 202 int avar __attribute__(( __cleanup(clean_up) ));246 int avar __attribute__(( cleanup(clean_up) )); 203 247 \end{cfa} 204 which is used on a variable and specifies a function, \eg @clean_up@, run when 205 the variable goes out of scope. The function is passed a pointer to the object 206 so it can be used to mimic destructors. However, this feature cannot be used to 207 mimic @try@ statements. 248 which is used on a variable and specifies a function, in this case @clean_up@, 249 run when the variable goes out of scope. 250 The function is passed a pointer to the object being removed from the stack 251 so it can be used to mimic destructors. 252 However, this feature cannot be used to mimic @try@ statements as it cannot 253 control the unwinding. 208 254 209 255 \subsection{Personality Functions} 210 Personality functions have a complex interface specified by libunwind. 256 Personality functions have a complex interface specified by libunwind. This 211 257 section covers some of the important parts of the interface. 212 258 213 A personality function performs four tasks, although not all have to be214 present.259 A personality function can preform different actions depending on how it is 260 called. 215 261 \begin{lstlisting}[language=C,{moredelim=**[is][\color{red}]{@}{@}}] 216 262 typedef _Unwind_Reason_Code (*@_Unwind_Personality_Fn@) ( … … 225 271 \item 226 272 @_UA_SEARCH_PHASE@ specifies a search phase and tells the personality function 227 to check for handlers. 273 to check for handlers. If there is a handler in a stack frame, as defined by 228 274 the language, the personality function returns @_URC_HANDLER_FOUND@; otherwise 229 275 it return @_URC_CONTINUE_UNWIND@. … … 296 342 \end{cfa} 297 343 It also unwinds the stack but it does not use the search phase. Instead another 298 function, the stop function, is used to stop searching. 344 function, the stop function, is used to stop searching. The exception is the 299 345 same as the one passed to raise exception. The extra arguments are the stop 300 346 function and the stop parameter. The stop function has a similar interface as a … … 318 364 319 365 \begin{sloppypar} 320 Its arguments are the same as the paired personality function. 366 Its arguments are the same as the paired personality function. The actions 321 367 @_UA_CLEANUP_PHASE@ and @_UA_FORCE_UNWIND@ are always set when it is 322 368 called. Beyond the libunwind standard, both GCC and Clang add an extra action … … 343 389 strong symbol replacing the sequential version. 344 390 345 % The version of the function defined in @libcfa@ is very simple. It returns a 346 % pointer to a global static variable. With only one stack this global instance 347 % is associated with the only stack. 348 349 For coroutines, @this_exception_context@ accesses the exception context stored 350 at the base of the stack. For threads, @this_exception_context@ uses the 351 concurrency library to access the current stack of the thread or coroutine 352 being executed by the thread, and then accesses the exception context stored at 353 the base of this stack. 391 The sequential @this_exception_context@ returns a hard-coded pointer to the 392 global execption context. 393 The concurrent version adds the exception context to the data stored at the 394 base of each stack. When @this_exception_context@ is called it retrieves the 395 active stack and returns the address of the context saved there. 354 396 355 397 \section{Termination} … … 369 411 per-exception storage. 370 412 371 Exceptions are stored in variable-sized blocks. \PAB{Show a memory layout 372 figure.} The first component is a fixed sized data structure that contains the 413 [Quick ASCII diagram to get started.] 414 \begin{verbatim} 415 Fixed Header | _Unwind_Exception <- pointer target 416 | 417 | Cforall storage 418 | 419 Variable Body | the exception <- fixed offset 420 V ... 421 \end{verbatim} 422 423 Exceptions are stored in variable-sized blocks. 424 The first component is a fixed sized data structure that contains the 373 425 information for libunwind and the exception system. The second component is an 374 426 area of memory big enough to store the exception. Macros with pointer arthritic … … 388 440 exception type. The size and copy function are used immediately to copy an 389 441 exception into managed memory. After the exception is handled the free function 390 is used to clean up the exception and then the entire node is passed to free. 442 is used to clean up the exception and then the entire node is passed to free 443 so the memory can be given back to the heap. 391 444 392 445 \subsection{Try Statements and Catch Clauses} … … 399 452 library. The contents of a try block and the termination handlers are converted 400 453 into functions. These are then passed to the try terminate function and it 401 calls them. This approach puts a try statement in its own functions so that no 402 function has to deal with both termination handlers and destructors. \PAB{I do 403 not understand the previous sentence.} 404 405 This function has some custom embedded assembly that defines \emph{its} 406 personality function and LSDA. The assembly is created with handcrafted C @asm@ 407 statements, which is why there is only one version of it. The personality 408 function is structured so that it can be expanded, but currently it only 409 handles this one function. Notably, it does not handle any destructors so the 410 function is constructed so that it does need to run it. \PAB{I do not 411 understand the previous sentence.} 454 calls them. 455 Because this function is known and fixed (and not an arbitrary function that 456 happens to contain a try statement) this means the LSDA can be generated ahead 457 of time. 458 459 Both the LSDA and the personality function are set ahead of time using 460 embedded assembly. This is handcrafted using C @asm@ statements and contains 461 enough information for the single try statement the function repersents. 412 462 413 463 The three functions passed to try terminate are: … … 419 469 420 470 \item[match function:] This function is called during the search phase and 421 decides if a catch clause matches the termination exception. 471 decides if a catch clause matches the termination exception. It is constructed 422 472 from the conditional part of each handler and runs each check, top to bottom, 423 473 in turn, first checking to see if the exception type matches and then if the … … 428 478 \item[handler function:] This function handles the exception. It takes a 429 479 pointer to the exception and the handler's id and returns nothing. It is called 430 after the cleanup phase. 480 after the cleanup phase. It is constructed by stitching together the bodies of 431 481 each handler and dispatches to the selected handler. 432 482 \end{description} … … 434 484 can be used to create closures, functions that can refer to the state of other 435 485 functions on the stack. This approach allows the functions to refer to all the 436 variables in scope for the function containing the @try@ statement. 486 variables in scope for the function containing the @try@ statement. These 437 487 nested functions and all other functions besides @__cfaehm_try_terminate@ in 438 488 \CFA use the GCC personality function and the @-fexceptions@ flag to generate … … 455 505 handler that matches. If no handler matches then the function returns 456 506 false. Otherwise the matching handler is run; if it completes successfully, the 457 function returns true. Re resume, through the @throwResume;@ statement, cause458 the function to return true.507 function returns true. Rethrowing, through the @throwResume;@ statement, 508 causes the function to return true. 459 509 460 510 % Recursive Resumption Stuff: … … 482 532 providing zero-cost enter/exit using the LSDA. Unfortunately, there is no way 483 533 to return from a libunwind search without installing a handler or raising an 484 error. 534 error. Although workarounds might be possible, they are beyond the scope of 485 535 this thesis. The current resumption implementation has simplicity in its 486 536 favour. … … 503 553 504 554 Cancellation also uses libunwind to do its stack traversal and unwinding, 505 however it uses a different primary function @_Unwind_ForcedUnwind@. 555 however it uses a different primary function @_Unwind_ForcedUnwind@. Details 506 556 of its interface can be found in the \VRef{s:ForcedUnwind}. 507 557 … … 511 561 its main coroutine and the coroutine it is currently executing. 512 562 513 The first check is if the current thread's main and current coroutine do not 514 match, implying a coroutine cancellation; otherwise, it is a thread 515 cancellation. Otherwise it is a main thread cancellation. \PAB{Previous 516 sentence does not make sense.} 563 So if the active thread's main and current coroutine are the same. If they 564 are then the current stack is a thread stack, otherwise it is a coroutine 565 stack. If it is a thread stack then an equality check with the stored main 566 thread pointer and current thread pointer is enough to tell if the current 567 thread is the main thread or not. 517 568 518 569 However, if the threading library is not linked, the sequential execution is on -
doc/theses/andrew_beach_MMath/uw-ethesis.tex
rfeacef9 r5407cdc 74 74 % ====================================================================== 75 75 % D O C U M E N T P R E A M B L E 76 % Specify the document class, default style attributes, page dimensions, etc. 77 % For hyperlinked PDF, suitable for viewing on a computer, use this: 78 \documentclass[letterpaper,12pt,titlepage,oneside,final]{book} 79 80 % For PDF, suitable for double-sided printing, change the PrintVersion 81 % variable below to "true" and use this \documentclass line instead of the 82 % one above: 83 %\documentclass[letterpaper,12pt,titlepage,openright,twoside,final]{book} 84 85 \usepackage{etoolbox} 76 \RequirePackage{etoolbox} 77 78 % Control if this for print (set true) or will stay digital (default). 79 % Print is two sided, digital uses more colours. 80 \newtoggle{printversion} 81 %\toggletrue{printversion} 82 83 \iftoggle{printversion}{% 84 \documentclass[letterpaper,12pt,titlepage,openright,twoside,final]{book} 85 }{% 86 \documentclass[letterpaper,12pt,titlepage,oneside,final]{book} 87 } 86 88 87 89 % Some LaTeX commands I define for my own nomenclature. … … 94 96 % Anything defined here may be redefined by packages added below... 95 97 96 % This package allows if-then-else control structures. 97 \usepackage{ifthen} 98 \newboolean{PrintVersion} 99 \setboolean{PrintVersion}{false} 100 % CHANGE THIS VALUE TO "true" as necessary, to improve printed results for 101 % hard copies by overriding some options of the hyperref package, called below. 102 103 %\usepackage{nomencl} % For a nomenclature (optional; available from ctan.org) 98 % For a nomenclature (optional; available from ctan.org) 99 %\usepackage{nomencl} 104 100 % Lots of math symbols and environments 105 101 \usepackage{amsmath,amssymb,amstext} 106 % For including graphics N.B. pdftex graphics driver 107 \usepackage[pdftex]{graphicx} 102 % For including graphics (must match graphics driver) 103 \usepackage{epic,eepic} 104 \usepackage{graphicx} 108 105 % Removes large sections of the document. 109 106 \usepackage{comment} 110 107 % Adds todos (Must be included after comment.) 111 108 \usepackage{todonotes} 112 113 109 114 110 % Hyperlinks make it very easy to navigate an electronic document. … … 117 113 % Use the "hyperref" package 118 114 % N.B. HYPERREF MUST BE THE LAST PACKAGE LOADED; ADD ADDITIONAL PKGS ABOVE 119 \usepackage[pdftex,pagebackref=true]{hyperref} % with basic options 120 %\usepackage[pdftex,pagebackref=true]{hyperref} 115 \usepackage[pagebackref=true]{hyperref} 121 116 % N.B. pagebackref=true provides links back from the References to the body 122 117 % text. This can cause trouble for printing. … … 128 123 pdffitwindow=false, % window fit to page when opened 129 124 pdfstartview={FitH}, % fits the width of the page to the window 130 % pdftitle={uWaterloo\ LaTeX\ Thesis\ Template}, % title: CHANGE THIS TEXT!131 % pdfauthor={Author}, % author: CHANGE THIS TEXT! and uncomment this line132 % pdfsubject={Subject}, % subject: CHANGE THIS TEXT! and uncomment this line133 % pdfkeywords={keyword1} {key2} {key3}, % optional list of keywords134 125 pdfnewwindow=true, % links in new window 135 126 colorlinks=true, % false: boxed links; true: colored links 136 linkcolor=blue, % color of internal links137 citecolor=green, % color of links to bibliography138 filecolor=magenta, % color of file links139 urlcolor=cyan % color of external links140 127 } 141 % for improved print quality, change some hyperref options 142 \ifthenelse{\boolean{PrintVersion}}{ 143 \hypersetup{ % override some previously defined hyperref options 144 % colorlinks,% 145 citecolor=black,% 146 filecolor=black,% 147 linkcolor=black,% 148 urlcolor=black} 149 }{} % end of ifthenelse (no else) 128 \iftoggle{printversion}{ 129 \hypersetup{ 130 citecolor=black, % colour of links to bibliography 131 filecolor=black, % colour of file links 132 linkcolor=black, % colour of internal links 133 urlcolor=black, % colour of external links 134 } 135 }{ % Digital Version 136 \hypersetup{ 137 citecolor=green, 138 filecolor=magenta, 139 linkcolor=blue, 140 urlcolor=cyan, 141 } 142 } 143 144 \hypersetup{ 145 pdftitle={Exception Handling in Cforall}, 146 pdfauthor={Andrew James Beach}, 147 pdfsubject={Computer Science}, 148 pdfkeywords={programming languages} {exceptions} 149 {language design} {language implementation}, 150 } 150 151 151 152 % Exception to the rule of hyperref being the last add-on package … … 217 218 \pdfstringdefDisableCommands{\def\Cpp{C++}} 218 219 220 % Wrappers for inline code snippits. 221 \newrobustcmd*\codeCFA[1]{\lstinline[language=CFA]{#1}} 222 \newrobustcmd*\codeC[1]{\lstinline[language=C]{#1}} 223 \newrobustcmd*\codeCpp[1]{\lstinline[language=C++]{#1}} 224 \newrobustcmd*\codePy[1]{\lstinline[language=Python]{#1}} 225 219 226 % Colour text, formatted in LaTeX style instead of TeX style. 220 227 \newcommand*\colour[2]{{\color{#1}#2}} -
doc/theses/mubeen_zulfiqar_MMath/uw-ethesis.bib
rfeacef9 r5407cdc 6 6 Alexander Samarin", 7 7 title = "The \LaTeX\ Companion", 8 year = "1994",8 year = "1994", 9 9 publisher = "Addison-Wesley", 10 address = "Reading, Massachusetts"10 address = "Reading, Massachusetts" 11 11 } 12 12 … … 23 23 title = "\LaTeX\ --- A Document Preparation System", 24 24 edition = "Second", 25 year = "1994",26 publisher = 25 year = "1994", 26 publisher = "Addison-Wesley", 27 27 address = "Reading, Massachusetts" 28 28 } -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/links.hpp
rfeacef9 r5407cdc 117 117 } 118 118 119 long long ts() const {119 unsigned long long ts() const { 120 120 return before._links.ts; 121 121 } -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list.hpp
rfeacef9 r5407cdc 39 39 while( __builtin_expect(ll.exchange(true),false) ) { 40 40 while(ll.load(std::memory_order_relaxed)) 41 asm volatile("pause");41 Pause(); 42 42 } 43 43 /* paranoid */ assert(ll); … … 93 93 && ready.compare_exchange_weak(copy, n + 1) ) 94 94 break; 95 asm volatile("pause");95 Pause(); 96 96 } 97 97 … … 133 133 // Step 1 : make sure no writer are in the middle of the critical section 134 134 while(lock.load(std::memory_order_relaxed)) 135 asm volatile("pause");135 Pause(); 136 136 137 137 // Fence needed because we don't want to start trying to acquire the lock … … 195 195 // to simply lock their own lock and enter. 196 196 while(lock.load(std::memory_order_relaxed)) 197 asm volatile("pause");197 Pause(); 198 198 199 199 // Step 2 : lock per-proc lock … … 204 204 for(uint_fast32_t i = 0; i < s; i++) { 205 205 while(data[i].lock.load(std::memory_order_relaxed)) 206 asm volatile("pause");206 Pause(); 207 207 } 208 208 -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/processor_list_good.cpp
rfeacef9 r5407cdc 21 21 target = (target - (target % total)) + total; 22 22 while(waiting < target) 23 asm volatile("pause");23 Pause(); 24 24 25 25 assert(waiting < (1ul << 60)); -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/randbit.cpp
rfeacef9 r5407cdc 123 123 target = (target - (target % total)) + total; 124 124 while(waiting < target) 125 asm volatile("pause");125 Pause(); 126 126 127 127 assert(waiting < (1ul << 60)); -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list.cpp
rfeacef9 r5407cdc 206 206 std::cout << "Total ops : " << ops << "(" << global.in << "i, " << global.out << "o, " << global.empty << "e)\n"; 207 207 #ifndef NO_STATS 208 LIST_VARIANT<Node>::stats_print(std::cout );208 LIST_VARIANT<Node>::stats_print(std::cout, duration); 209 209 #endif 210 210 } … … 368 368 369 369 for(Node * & node : nodes) { 370 node = list.pop(); 371 assert(node); 370 node = nullptr; 371 while(!node) { 372 node = list.pop(); 373 } 372 374 local.crc_out += node->value; 373 375 local.out++; … … 691 693 692 694 for(const auto & n : nodes) { 693 local.valmax = max(local.valmax, size_t(n.value));694 local.valmin = min(local.valmin, size_t(n.value));695 local.valmax = std::max(local.valmax, size_t(n.value)); 696 local.valmin = std::min(local.valmin, size_t(n.value)); 695 697 } 696 698 … … 773 775 try { 774 776 arg = optarg = argv[optind]; 775 nnodes = st oul(optarg, &len);777 nnodes = std::stoul(optarg, &len); 776 778 if(len != arg.size()) { throw std::invalid_argument(""); } 777 779 } catch(std::invalid_argument &) { … … 792 794 try { 793 795 arg = optarg = argv[optind]; 794 nnodes = st oul(optarg, &len);796 nnodes = std::stoul(optarg, &len); 795 797 if(len != arg.size()) { throw std::invalid_argument(""); } 796 798 } catch(std::invalid_argument &) { … … 812 814 try { 813 815 arg = optarg = argv[optind]; 814 nnodes = st oul(optarg, &len);816 nnodes = std::stoul(optarg, &len); 815 817 if(len != arg.size()) { throw std::invalid_argument(""); } 816 818 nslots = nnodes; … … 823 825 try { 824 826 arg = optarg = argv[optind]; 825 nnodes = st oul(optarg, &len);827 nnodes = std::stoul(optarg, &len); 826 828 if(len != arg.size()) { throw std::invalid_argument(""); } 827 829 } catch(std::invalid_argument &) { … … 831 833 try { 832 834 arg = optarg = argv[optind + 1]; 833 nslots = st oul(optarg, &len);835 nslots = std::stoul(optarg, &len); 834 836 if(len != arg.size()) { throw std::invalid_argument(""); } 835 837 } catch(std::invalid_argument &) { … … 884 886 case 'd': 885 887 try { 886 duration = st od(optarg, &len);888 duration = std::stod(optarg, &len); 887 889 if(len != arg.size()) { throw std::invalid_argument(""); } 888 890 } catch(std::invalid_argument &) { … … 893 895 case 't': 894 896 try { 895 nthreads = st oul(optarg, &len);897 nthreads = std::stoul(optarg, &len); 896 898 if(len != arg.size()) { throw std::invalid_argument(""); } 897 899 } catch(std::invalid_argument &) { … … 902 904 case 'q': 903 905 try { 904 nqueues = st oul(optarg, &len);906 nqueues = std::stoul(optarg, &len); 905 907 if(len != arg.size()) { throw std::invalid_argument(""); } 906 908 } catch(std::invalid_argument &) { -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzi-packed.hpp
rfeacef9 r5407cdc 168 168 for(int i = 0; i < width; i++) { 169 169 int idx = i % hwdith; 170 std::cout << i << " -> " << idx + width << std::endl;171 170 leafs[i].parent = &nodes[ idx ]; 172 171 } … … 174 173 for(int i = 0; i < root; i++) { 175 174 int idx = (i / 2) + hwdith; 176 std::cout << i + width << " -> " << idx + width << std::endl;177 175 nodes[i].parent = &nodes[ idx ]; 178 176 } -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/snzi.hpp
rfeacef9 r5407cdc 159 159 std::cout << "SNZI: " << depth << "x" << width << "(" << mask - 1 << ") " << (sizeof(snzi_t::node) * (root + 1)) << " bytes" << std::endl; 160 160 for(int i = 0; i < root; i++) { 161 std::cout << i << " -> " << (i / base) + width << std::endl;162 161 nodes[i].parent = &nodes[(i / base) + width]; 163 162 } -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/utils.hpp
rfeacef9 r5407cdc 11 11 #include <sys/sysinfo.h> 12 12 13 #include <x86intrin.h> 14 15 // Barrier from 16 class barrier_t { 17 public: 18 barrier_t(size_t total) 19 : waiting(0) 20 , total(total) 21 {} 22 23 void wait(unsigned) { 24 size_t target = waiting++; 25 target = (target - (target % total)) + total; 26 while(waiting < target) 27 asm volatile("pause"); 28 29 assert(waiting < (1ul << 60)); 30 } 31 32 private: 33 std::atomic<size_t> waiting; 34 size_t total; 35 }; 13 // #include <x86intrin.h> 36 14 37 15 // class Random { … … 102 80 }; 103 81 104 static inline long long rdtscl(void) { 105 unsigned int lo, hi; 106 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 107 return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 108 } 82 static inline long long int rdtscl(void) { 83 #if defined( __i386 ) || defined( __x86_64 ) 84 unsigned int lo, hi; 85 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 86 return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); 87 #elif defined( __aarch64__ ) || defined( __arm__ ) 88 // https://github.com/google/benchmark/blob/v1.1.0/src/cycleclock.h#L116 89 long long int virtual_timer_value; 90 asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); 91 return virtual_timer_value; 92 #else 93 #error unsupported hardware architecture 94 #endif 95 } 96 97 #if defined( __i386 ) || defined( __x86_64 ) 98 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 99 #elif defined( __ARM_ARCH ) 100 #define Pause() __asm__ __volatile__ ( "YIELD" : : : ) 101 #else 102 #error unsupported architecture 103 #endif 109 104 110 105 static inline void affinity(int tid) { … … 195 190 } 196 191 192 // Barrier from 193 class barrier_t { 194 public: 195 barrier_t(size_t total) 196 : waiting(0) 197 , total(total) 198 {} 199 200 void wait(unsigned) { 201 size_t target = waiting++; 202 target = (target - (target % total)) + total; 203 while(waiting < target) 204 Pause(); 205 206 assert(waiting < (1ul << 60)); 207 } 208 209 private: 210 std::atomic<size_t> waiting; 211 size_t total; 212 }; 213 197 214 struct spinlock_t { 198 215 std::atomic_bool ll = { false }; … … 201 218 while( __builtin_expect(ll.exchange(true),false) ) { 202 219 while(ll.load(std::memory_order_relaxed)) 203 asm volatile("pause");220 Pause(); 204 221 } 205 222 } -
doc/theses/thierry_delisle_PhD/code/readyQ_proto/work_stealing.hpp
rfeacef9 r5407cdc 6 6 #include <memory> 7 7 #include <mutex> 8 #include <thread> 8 9 #include <type_traits> 9 10 … … 11 12 #include "utils.hpp" 12 13 #include "links.hpp" 14 #include "links2.hpp" 13 15 #include "snzi.hpp" 14 16 17 // #include <x86intrin.h> 18 15 19 using namespace std; 20 21 static const long long lim = 2000; 22 static const unsigned nqueues = 2; 23 24 struct __attribute__((aligned(128))) timestamp_t { 25 volatile unsigned long long val = 0; 26 }; 27 28 template<typename node_t> 29 struct __attribute__((aligned(128))) localQ_t { 30 #ifdef NO_MPSC 31 intrusive_queue_t<node_t> list; 32 33 inline auto ts() { return list.ts(); } 34 inline auto lock() { return list.lock.lock(); } 35 inline auto try_lock() { return list.lock.try_lock(); } 36 inline auto unlock() { return list.lock.unlock(); } 37 38 inline auto push( node_t * node ) { return list.push( node ); } 39 inline auto pop() { return list.pop(); } 40 #else 41 mpsc_queue<node_t> queue = {}; 42 spinlock_t _lock = {}; 43 44 inline auto ts() { auto h = queue.head(); return h ? h->_links.ts : 0ull; } 45 inline auto lock() { return _lock.lock(); } 46 inline auto try_lock() { return _lock.try_lock(); } 47 inline auto unlock() { return _lock.unlock(); } 48 49 inline auto push( node_t * node ) { return queue.push( node ); } 50 inline auto pop() { return queue.pop(); } 51 #endif 52 53 54 }; 16 55 17 56 template<typename node_t> … … 25 64 26 65 work_stealing(unsigned _numThreads, unsigned) 27 : numThreads(_numThreads) 28 , lists(new intrusive_queue_t<node_t>[numThreads]) 29 , snzi( std::log2( numThreads / 2 ), 2 ) 66 : numThreads(_numThreads * nqueues) 67 , lists(new localQ_t<node_t>[numThreads]) 68 // , lists(new intrusive_queue_t<node_t>[numThreads]) 69 , times(new timestamp_t[numThreads]) 70 // , snzi( std::log2( numThreads / 2 ), 2 ) 30 71 31 72 { … … 40 81 __attribute__((noinline, hot)) void push(node_t * node) { 41 82 node->_links.ts = rdtscl(); 42 if( node->_links.hint > numThreads ) { 43 node->_links.hint = tls.rng.next() % numThreads; 44 tls.stat.push.nhint++; 83 // node->_links.ts = 1; 84 85 auto & list = *({ 86 unsigned i; 87 #ifdef NO_MPSC 88 do { 89 #endif 90 tls.stats.push.attempt++; 91 // unsigned r = tls.rng1.next(); 92 unsigned r = tls.it++; 93 if(tls.my_queue == outside) { 94 i = r % numThreads; 95 } else { 96 i = tls.my_queue + (r % nqueues); 97 } 98 #ifdef NO_MPSC 99 } while(!lists[i].try_lock()); 100 #endif 101 &lists[i]; 102 }); 103 104 list.push( node ); 105 #ifdef NO_MPSC 106 list.unlock(); 107 #endif 108 // tls.rng2.set_raw_state( tls.rng1.get_raw_state()); 109 // count++; 110 tls.stats.push.success++; 111 } 112 113 __attribute__((noinline, hot)) node_t * pop() { 114 if(tls.my_queue != outside) { 115 // if( tls.myfriend == outside ) { 116 // auto r = tls.rng1.next(); 117 // tls.myfriend = r % numThreads; 118 // // assert(lists[(tls.it % nqueues) + tls.my_queue].ts() >= lists[((tls.it + 1) % nqueues) + tls.my_queue].ts()); 119 // tls.mytime = std::min(lists[(tls.it % nqueues) + tls.my_queue].ts(), lists[((tls.it + 1) % nqueues) + tls.my_queue].ts()); 120 // // times[tls.myfriend].val = 0; 121 // // lists[tls.myfriend].val = 0; 122 // } 123 // // else if(times[tls.myfriend].val == 0) { 124 // // else if(lists[tls.myfriend].val == 0) { 125 // else if(times[tls.myfriend].val < tls.mytime) { 126 // // else if(times[tls.myfriend].val < lists[(tls.it % nqueues) + tls.my_queue].ts()) { 127 // node_t * n = try_pop(tls.myfriend, tls.stats.pop.help); 128 // tls.stats.help++; 129 // tls.myfriend = outside; 130 // if(n) return n; 131 // } 132 // if( tls.myfriend == outside ) { 133 // auto r = tls.rng1.next(); 134 // tls.myfriend = r % numThreads; 135 // tls.mytime = lists[((tls.it + 1) % nqueues) + tls.my_queue].ts(); 136 // } 137 // else { 138 // if(times[tls.myfriend].val + 1000 < tls.mytime) { 139 // node_t * n = try_pop(tls.myfriend, tls.stats.pop.help); 140 // tls.stats.help++; 141 // if(n) return n; 142 // } 143 // tls.myfriend = outside; 144 // } 145 146 node_t * n = local(); 147 if(n) return n; 45 148 } 46 149 47 unsigned i = node->_links.hint; 48 auto & list = lists[i]; 49 list.lock.lock(); 50 51 if(list.push( node )) { 52 snzi.arrive(i); 150 // try steal 151 for(int i = 0; i < 25; i++) { 152 node_t * n = steal(); 153 if(n) return n; 53 154 } 54 155 55 list.lock.unlock(); 56 } 57 58 __attribute__((noinline, hot)) node_t * pop() { 59 node_t * node; 60 while(true) { 61 if(!snzi.query()) { 62 return nullptr; 63 } 64 65 { 66 unsigned i = tls.my_queue; 67 auto & list = lists[i]; 68 if( list.ts() != 0 ) { 69 list.lock.lock(); 70 if((node = try_pop(i))) { 71 tls.stat.pop.local.success++; 72 break; 73 } 74 else { 75 tls.stat.pop.local.elock++; 76 } 77 } 78 else { 79 tls.stat.pop.local.espec++; 80 } 81 } 82 83 tls.stat.pop.steal.tried++; 84 85 int i = tls.rng.next() % numThreads; 86 auto & list = lists[i]; 87 if( list.ts() == 0 ) { 88 tls.stat.pop.steal.empty++; 89 continue; 90 } 91 92 if( !list.lock.try_lock() ) { 93 tls.stat.pop.steal.locked++; 94 continue; 95 } 96 97 if((node = try_pop(i))) { 98 tls.stat.pop.steal.success++; 99 break; 156 return search(); 157 } 158 159 private: 160 inline node_t * local() { 161 unsigned i = (--tls.it % nqueues) + tls.my_queue; 162 node_t * n = try_pop(i, tls.stats.pop.local); 163 if(n) return n; 164 i = (--tls.it % nqueues) + tls.my_queue; 165 return try_pop(i, tls.stats.pop.local); 166 } 167 168 inline node_t * steal() { 169 unsigned i = tls.rng2.prev() % numThreads; 170 return try_pop(i, tls.stats.pop.steal); 171 } 172 173 inline node_t * search() { 174 unsigned offset = tls.rng2.prev(); 175 for(unsigned i = 0; i < numThreads; i++) { 176 unsigned idx = (offset + i) % numThreads; 177 node_t * thrd = try_pop(idx, tls.stats.pop.search); 178 if(thrd) { 179 return thrd; 100 180 } 101 181 } 102 182 103 #if defined(READ) 104 const unsigned f = READ; 105 if(0 == (tls.it % f)) { 106 unsigned i = tls.it / f; 107 lists[i % numThreads].ts(); 108 } 109 // lists[tls.it].ts(); 110 tls.it++; 111 #endif 112 113 114 return node; 115 } 116 117 private: 118 node_t * try_pop(unsigned i) { 183 return nullptr; 184 } 185 186 private: 187 struct attempt_stat_t { 188 std::size_t attempt = { 0 }; 189 std::size_t elock = { 0 }; 190 std::size_t eempty = { 0 }; 191 std::size_t espec = { 0 }; 192 std::size_t success = { 0 }; 193 }; 194 195 node_t * try_pop(unsigned i, attempt_stat_t & stat) { 196 assert(i < numThreads); 119 197 auto & list = lists[i]; 198 stat.attempt++; 199 200 // If the list is empty, don't try 201 if(list.ts() == 0) { stat.espec++; return nullptr; } 202 203 // If we can't get the lock, move on 204 if( !list.try_lock() ) { stat.elock++; return nullptr; } 120 205 121 206 // If list is empty, unlock and retry 122 207 if( list.ts() == 0 ) { 123 list.lock.unlock(); 208 list.unlock(); 209 stat.eempty++; 124 210 return nullptr; 125 211 } 126 212 127 // Actually pop the list 128 node_t * node; 129 bool emptied; 130 std::tie(node, emptied) = list.pop(); 131 assert(node); 132 133 if(emptied) { 134 snzi.depart(i); 135 } 136 137 // Unlock and return 138 list.lock.unlock(); 139 return node; 213 auto node = list.pop(); 214 list.unlock(); 215 stat.success++; 216 #ifdef NO_MPSC 217 // times[i].val = 1; 218 times[i].val = node.first->_links.ts; 219 // lists[i].val = node.first->_links.ts; 220 return node.first; 221 #else 222 times[i].val = node->_links.ts; 223 return node; 224 #endif 140 225 } 141 226 … … 144 229 145 230 static std::atomic_uint32_t ticket; 231 static const unsigned outside = 0xFFFFFFFF; 232 233 static inline unsigned calc_preferred() { 234 unsigned t = ticket++; 235 if(t == 0) return outside; 236 unsigned i = (t - 1) * nqueues; 237 return i; 238 } 239 146 240 static __attribute__((aligned(128))) thread_local struct TLS { 147 Random rng = { int(rdtscl()) }; 148 unsigned my_queue = ticket++; 241 Random rng1 = { unsigned(std::hash<std::thread::id>{}(std::this_thread::get_id()) ^ rdtscl()) }; 242 Random rng2 = { unsigned(std::hash<std::thread::id>{}(std::this_thread::get_id()) ^ rdtscl()) }; 243 unsigned it = 0; 244 unsigned my_queue = calc_preferred(); 245 unsigned myfriend = outside; 246 unsigned long long int mytime = 0; 149 247 #if defined(READ) 150 248 unsigned it = 0; … … 152 250 struct { 153 251 struct { 154 std::size_t nhint = { 0 }; 252 std::size_t attempt = { 0 }; 253 std::size_t success = { 0 }; 155 254 } push; 156 255 struct { 157 struct { 158 std::size_t success = { 0 }; 159 std::size_t espec = { 0 }; 160 std::size_t elock = { 0 }; 161 } local; 162 struct { 163 std::size_t tried = { 0 }; 164 std::size_t locked = { 0 }; 165 std::size_t empty = { 0 }; 166 std::size_t success = { 0 }; 167 } steal; 256 attempt_stat_t help; 257 attempt_stat_t local; 258 attempt_stat_t steal; 259 attempt_stat_t search; 168 260 } pop; 169 } stat; 261 std::size_t help = { 0 }; 262 } stats; 170 263 } tls; 171 264 172 265 private: 173 266 const unsigned numThreads; 174 std::unique_ptr<intrusive_queue_t<node_t> []> lists; 175 __attribute__((aligned(64))) snzi_t snzi; 267 std::unique_ptr<localQ_t<node_t> []> lists; 268 // std::unique_ptr<intrusive_queue_t<node_t> []> lists; 269 std::unique_ptr<timestamp_t []> times; 270 __attribute__((aligned(128))) std::atomic_size_t count; 176 271 177 272 #ifndef NO_STATS … … 179 274 static struct GlobalStats { 180 275 struct { 181 std::atomic_size_t nhint = { 0 }; 276 std::atomic_size_t attempt = { 0 }; 277 std::atomic_size_t success = { 0 }; 182 278 } push; 183 279 struct { 184 280 struct { 281 std::atomic_size_t attempt = { 0 }; 282 std::atomic_size_t elock = { 0 }; 283 std::atomic_size_t eempty = { 0 }; 284 std::atomic_size_t espec = { 0 }; 185 285 std::atomic_size_t success = { 0 }; 186 std::atomic_size_t espec = { 0 }; 187 std::atomic_size_t elock = { 0 }; 286 } help; 287 struct { 288 std::atomic_size_t attempt = { 0 }; 289 std::atomic_size_t elock = { 0 }; 290 std::atomic_size_t eempty = { 0 }; 291 std::atomic_size_t espec = { 0 }; 292 std::atomic_size_t success = { 0 }; 188 293 } local; 189 294 struct { 190 std::atomic_size_t tried = { 0 }; 191 std::atomic_size_t locked = { 0 }; 192 std::atomic_size_t empty = { 0 }; 295 std::atomic_size_t attempt = { 0 }; 296 std::atomic_size_t elock = { 0 }; 297 std::atomic_size_t eempty = { 0 }; 298 std::atomic_size_t espec = { 0 }; 193 299 std::atomic_size_t success = { 0 }; 194 300 } steal; 301 struct { 302 std::atomic_size_t attempt = { 0 }; 303 std::atomic_size_t elock = { 0 }; 304 std::atomic_size_t eempty = { 0 }; 305 std::atomic_size_t espec = { 0 }; 306 std::atomic_size_t success = { 0 }; 307 } search; 195 308 } pop; 309 std::atomic_size_t help = { 0 }; 196 310 } global_stats; 197 311 198 312 public: 199 313 static void stats_tls_tally() { 200 global_stats.push.nhint += tls.stat.push.nhint; 201 global_stats.pop.local.success += tls.stat.pop.local.success; 202 global_stats.pop.local.espec += tls.stat.pop.local.espec ; 203 global_stats.pop.local.elock += tls.stat.pop.local.elock ; 204 global_stats.pop.steal.tried += tls.stat.pop.steal.tried ; 205 global_stats.pop.steal.locked += tls.stat.pop.steal.locked ; 206 global_stats.pop.steal.empty += tls.stat.pop.steal.empty ; 207 global_stats.pop.steal.success += tls.stat.pop.steal.success; 208 } 209 210 static void stats_print(std::ostream & os ) { 314 global_stats.push.attempt += tls.stats.push.attempt; 315 global_stats.push.success += tls.stats.push.success; 316 global_stats.pop.help .attempt += tls.stats.pop.help .attempt; 317 global_stats.pop.help .elock += tls.stats.pop.help .elock ; 318 global_stats.pop.help .eempty += tls.stats.pop.help .eempty ; 319 global_stats.pop.help .espec += tls.stats.pop.help .espec ; 320 global_stats.pop.help .success += tls.stats.pop.help .success; 321 global_stats.pop.local .attempt += tls.stats.pop.local .attempt; 322 global_stats.pop.local .elock += tls.stats.pop.local .elock ; 323 global_stats.pop.local .eempty += tls.stats.pop.local .eempty ; 324 global_stats.pop.local .espec += tls.stats.pop.local .espec ; 325 global_stats.pop.local .success += tls.stats.pop.local .success; 326 global_stats.pop.steal .attempt += tls.stats.pop.steal .attempt; 327 global_stats.pop.steal .elock += tls.stats.pop.steal .elock ; 328 global_stats.pop.steal .eempty += tls.stats.pop.steal .eempty ; 329 global_stats.pop.steal .espec += tls.stats.pop.steal .espec ; 330 global_stats.pop.steal .success += tls.stats.pop.steal .success; 331 global_stats.pop.search.attempt += tls.stats.pop.search.attempt; 332 global_stats.pop.search.elock += tls.stats.pop.search.elock ; 333 global_stats.pop.search.eempty += tls.stats.pop.search.eempty ; 334 global_stats.pop.search.espec += tls.stats.pop.search.espec ; 335 global_stats.pop.search.success += tls.stats.pop.search.success; 336 global_stats.help += tls.stats.help; 337 } 338 339 static void stats_print(std::ostream & os, double duration ) { 211 340 std::cout << "----- Work Stealing Stats -----" << std::endl; 212 341 213 double stealSucc = double(global_stats.pop.steal.success) / global_stats.pop.steal.tried; 214 os << "Push to new Q : " << std::setw(15) << global_stats.push.nhint << "\n"; 215 os << "Local Pop : " << std::setw(15) << global_stats.pop.local.success << "\n"; 216 os << "Steal Pop : " << std::setw(15) << global_stats.pop.steal.success << "(" << global_stats.pop.local.espec << "s, " << global_stats.pop.local.elock << "l)\n"; 217 os << "Steal Success : " << std::setw(15) << stealSucc << "(" << global_stats.pop.steal.tried << " tries)\n"; 218 os << "Steal Fails : " << std::setw(15) << global_stats.pop.steal.empty << "e, " << global_stats.pop.steal.locked << "l\n"; 342 double push_suc = (100.0 * double(global_stats.push.success) / global_stats.push.attempt); 343 double push_len = double(global_stats.push.attempt ) / global_stats.push.success; 344 os << "Push Pick : " << push_suc << " %, len " << push_len << " (" << global_stats.push.attempt << " / " << global_stats.push.success << ")\n"; 345 346 double hlp_suc = (100.0 * double(global_stats.pop.help.success) / global_stats.pop.help.attempt); 347 double hlp_len = double(global_stats.pop.help.attempt ) / global_stats.pop.help.success; 348 os << "Help : " << hlp_suc << " %, len " << hlp_len << " (" << global_stats.pop.help.attempt << " / " << global_stats.pop.help.success << ")\n"; 349 os << "Help Fail : " << global_stats.pop.help.espec << "s, " << global_stats.pop.help.eempty << "e, " << global_stats.pop.help.elock << "l\n"; 350 351 double pop_suc = (100.0 * double(global_stats.pop.local.success) / global_stats.pop.local.attempt); 352 double pop_len = double(global_stats.pop.local.attempt ) / global_stats.pop.local.success; 353 os << "Local : " << pop_suc << " %, len " << pop_len << " (" << global_stats.pop.local.attempt << " / " << global_stats.pop.local.success << ")\n"; 354 os << "Local Fail : " << global_stats.pop.local.espec << "s, " << global_stats.pop.local.eempty << "e, " << global_stats.pop.local.elock << "l\n"; 355 356 double stl_suc = (100.0 * double(global_stats.pop.steal.success) / global_stats.pop.steal.attempt); 357 double stl_len = double(global_stats.pop.steal.attempt ) / global_stats.pop.steal.success; 358 os << "Steal : " << stl_suc << " %, len " << stl_len << " (" << global_stats.pop.steal.attempt << " / " << global_stats.pop.steal.success << ")\n"; 359 os << "Steal Fail : " << global_stats.pop.steal.espec << "s, " << global_stats.pop.steal.eempty << "e, " << global_stats.pop.steal.elock << "l\n"; 360 361 double srh_suc = (100.0 * double(global_stats.pop.search.success) / global_stats.pop.search.attempt); 362 double srh_len = double(global_stats.pop.search.attempt ) / global_stats.pop.search.success; 363 os << "Search : " << srh_suc << " %, len " << srh_len << " (" << global_stats.pop.search.attempt << " / " << global_stats.pop.search.success << ")\n"; 364 os << "Search Fail : " << global_stats.pop.search.espec << "s, " << global_stats.pop.search.eempty << "e, " << global_stats.pop.search.elock << "l\n"; 365 os << "Helps : " << std::setw(15) << std::scientific << global_stats.help / duration << "/sec (" << global_stats.help << ")\n"; 219 366 } 220 367 private:
Note: See TracChangeset
for help on using the changeset viewer.