[382edbe] | 1 | \chapter{\CFA{} Existing Features} |
---|
[553f8abe] | 2 | \label{c:existing} |
---|
[f28fdee] | 3 | |
---|
[382edbe] | 4 | \CFA is an open-source project extending ISO C with |
---|
[6c79bef] | 5 | modern safety and productivity features, while still ensuring backwards |
---|
| 6 | compatibility with C and its programmers. \CFA is designed to have an |
---|
| 7 | orthogonal feature-set based closely on the C programming paradigm |
---|
| 8 | (non-object-oriented) and these features can be added incrementally to an |
---|
| 9 | existing C code-base allowing programmers to learn \CFA on an as-needed basis. |
---|
| 10 | |
---|
[382edbe] | 11 | Only those \CFA features pertaining to this thesis are discussed. |
---|
[fa7dbf1] | 12 | % Also, only new features of \CFA will be discussed, |
---|
| 13 | A familiarity with |
---|
[382edbe] | 14 | C or C-like languages is assumed. |
---|
[6c79bef] | 15 | |
---|
[9af0fe2d] | 16 | \section{Overloading and \lstinline{extern}} |
---|
[6c79bef] | 17 | \CFA has extensive overloading, allowing multiple definitions of the same name |
---|
[67c6a47] | 18 | to be defined~\cite{Moss18}. |
---|
[fa7dbf1] | 19 | \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] |
---|
| 20 | char @i@; int @i@; double @i@; |
---|
| 21 | int @f@(); double @f@(); |
---|
| 22 | void @g@( int ); void @g@( double ); |
---|
| 23 | \end{lstlisting} |
---|
[6c79bef] | 24 | This feature requires name mangling so the assembly symbols are unique for |
---|
| 25 | different overloads. For compatibility with names in C, there is also a syntax |
---|
| 26 | to disable name mangling. These unmangled names cannot be overloaded but act as |
---|
| 27 | the interface between C and \CFA code. The syntax for disabling/enabling |
---|
| 28 | mangling is: |
---|
[f28fdee] | 29 | \begin{cfa} |
---|
[edc6ea2] | 30 | // name mangling on by default |
---|
[6c79bef] | 31 | int i; // _X1ii_1 |
---|
[21f2e92] | 32 | extern "C" { // disables name mangling |
---|
[6c79bef] | 33 | int j; // j |
---|
[21f2e92] | 34 | extern "Cforall" { // enables name mangling |
---|
[6c79bef] | 35 | int k; // _X1ki_1 |
---|
| 36 | } |
---|
[edc6ea2] | 37 | // revert to no name mangling |
---|
[6e7b969] | 38 | } |
---|
[edc6ea2] | 39 | // revert to name mangling |
---|
[6c79bef] | 40 | \end{cfa} |
---|
| 41 | Both forms of @extern@ affect all the declarations within their nested lexical |
---|
| 42 | scope and transition back to the previous mangling state when the lexical scope |
---|
| 43 | ends. |
---|
| 44 | |
---|
| 45 | \section{Reference Type} |
---|
[03c0e44] | 46 | \CFA adds a reference type to C as an auto-dereferencing pointer. |
---|
| 47 | They work very similarly to pointers. |
---|
[382edbe] | 48 | Reference-types are written the same way as a pointer-type but each |
---|
[03c0e44] | 49 | asterisk (@*@) is replaced with a ampersand (@&@); |
---|
[21f2e92] | 50 | this includes cv-qualifiers and multiple levels of reference. |
---|
[03c0e44] | 51 | |
---|
[382edbe] | 52 | Generally, references act like pointers with an implicate dereferencing |
---|
| 53 | operation added to each use of the variable. |
---|
| 54 | These automatic dereferences may be disabled with the address-of operator |
---|
| 55 | (@&@). |
---|
[21f2e92] | 56 | |
---|
[382edbe] | 57 | % Check to see if these are generating errors. |
---|
| 58 | \begin{minipage}{0,5\textwidth} |
---|
[03c0e44] | 59 | With references: |
---|
| 60 | \begin{cfa} |
---|
| 61 | int i, j; |
---|
| 62 | int & ri = i; |
---|
| 63 | int && rri = ri; |
---|
| 64 | rri = 3; |
---|
[fa7dbf1] | 65 | &ri = &j; // rebindable |
---|
[03c0e44] | 66 | ri = 5; |
---|
| 67 | \end{cfa} |
---|
| 68 | \end{minipage} |
---|
[382edbe] | 69 | \begin{minipage}{0,5\textwidth} |
---|
[03c0e44] | 70 | With pointers: |
---|
[6c79bef] | 71 | \begin{cfa} |
---|
| 72 | int i, j; |
---|
[03c0e44] | 73 | int * pi = &i |
---|
| 74 | int ** ppi = π |
---|
| 75 | **ppi = 3; |
---|
[21f2e92] | 76 | pi = &j; |
---|
[03c0e44] | 77 | *pi = 5; |
---|
[f28fdee] | 78 | \end{cfa} |
---|
[03c0e44] | 79 | \end{minipage} |
---|
[6e7b969] | 80 | |
---|
[fa7dbf1] | 81 | References are intended for pointer situations where dereferencing is the common usage, |
---|
| 82 | \ie the value is more important than the pointer. |
---|
[382edbe] | 83 | Mutable references may be assigned to by converting them to a pointer |
---|
| 84 | with a @&@ and then assigning a pointer to them, as in @&ri = &j;@ above |
---|
[6e7b969] | 85 | |
---|
[382edbe] | 86 | \section{Operators} |
---|
[6c79bef] | 87 | |
---|
[fa7dbf1] | 88 | \CFA implements operator overloading by providing special names, where |
---|
| 89 | operator usages are translated into function calls using these names. |
---|
| 90 | An operator name is created by taking the operator symbols and joining them with |
---|
[1e567ab] | 91 | @?@s to show where the arguments go. |
---|
[382edbe] | 92 | For example, |
---|
[fa7dbf1] | 93 | infixed multiplication is @?*?@, while prefix dereference is @*?@. |
---|
[edc6ea2] | 94 | This syntax make it easy to tell the difference between prefix operations |
---|
| 95 | (such as @++?@) and post-fix operations (@?++@). |
---|
[fa7dbf1] | 96 | For example, plus and equality operators are defined for a point type. |
---|
[382edbe] | 97 | \begin{cfa} |
---|
[6071efc] | 98 | point ?+?(point a, point b) { return point{a.x + b.x, a.y + b.y}; } |
---|
[fa7dbf1] | 99 | int ?==?(point a, point b) { return a.x == b.x && a.y == b.y; } |
---|
[382edbe] | 100 | { |
---|
| 101 | assert(point{1, 2} + point{3, 4} == point{4, 6}); |
---|
| 102 | } |
---|
| 103 | \end{cfa} |
---|
[fa7dbf1] | 104 | Note these special names are not limited to builtin |
---|
| 105 | operators, and hence, may be used with arbitrary types. |
---|
| 106 | \begin{cfa} |
---|
| 107 | double ?+?( int x, point y ); // arbitrary types |
---|
| 108 | \end{cfa} |
---|
| 109 | % Some ``near misses", that are that do not match an operator form but looks like |
---|
| 110 | % it may have been supposed to, will generate warning but otherwise they are |
---|
| 111 | % left alone. |
---|
| 112 | Because operators are never part of the type definition they may be added |
---|
| 113 | at any time, including on built-in types. |
---|
[382edbe] | 114 | |
---|
| 115 | %\subsection{Constructors and Destructors} |
---|
| 116 | |
---|
[fa7dbf1] | 117 | \CFA also provides constructors and destructors as operators, which means they |
---|
| 118 | are functions with special operator names rather than type names in \Cpp. |
---|
| 119 | While constructors and destructions are normally called implicitly by the compiler, |
---|
| 120 | the special operator names, allow explicit calls. |
---|
| 121 | |
---|
| 122 | % Placement new means that this is actually equivalent to C++. |
---|
[382edbe] | 123 | |
---|
[21f2e92] | 124 | The special name for a constructor is @?{}@, which comes from the |
---|
[382edbe] | 125 | initialization syntax in C, \eg @Example e = { ... }@. |
---|
[fa7dbf1] | 126 | \CFA generates a constructor call each time a variable is declared, |
---|
| 127 | passing the initialization arguments to the constructor. |
---|
[edc6ea2] | 128 | \begin{cfa} |
---|
[21f2e92] | 129 | struct Example { ... }; |
---|
| 130 | void ?{}(Example & this) { ... } |
---|
| 131 | void ?{}(Example & this, char first, int num) { ... } |
---|
[fa7dbf1] | 132 | Example a; // implicit constructor calls |
---|
| 133 | Example b = {}; |
---|
| 134 | Example c = {'a', 2}; |
---|
| 135 | \end{cfa} |
---|
| 136 | Both @a@ and @b@ are initialized with the first constructor, |
---|
| 137 | while @c@ is initialized with the second. |
---|
| 138 | Constructor calls can be replaced with C initialization using special operator \lstinline{@=}. |
---|
| 139 | \begin{cfa} |
---|
| 140 | Example d @= {42}; |
---|
[edc6ea2] | 141 | \end{cfa} |
---|
[6c79bef] | 142 | % I don't like the \^{} symbol but $^\wedge$ isn't better. |
---|
[fa7dbf1] | 143 | Similarly, destructors use the special name @^?{}@ (the @^@ has no special |
---|
[382edbe] | 144 | meaning). |
---|
[fa7dbf1] | 145 | % These are a normally called implicitly called on a variable when it goes out |
---|
| 146 | % of scope. They can be called explicitly as well. |
---|
[6c79bef] | 147 | \begin{cfa} |
---|
[21f2e92] | 148 | void ^?{}(Example & this) { ... } |
---|
[6c79bef] | 149 | { |
---|
[fa7dbf1] | 150 | Example e; // implicit constructor call |
---|
| 151 | ^?{}(e); // explicit destructor call |
---|
| 152 | ?{}(e); // explicit constructor call |
---|
| 153 | } // implicit destructor call |
---|
[6c79bef] | 154 | \end{cfa} |
---|
[edc6ea2] | 155 | |
---|
[fa7dbf1] | 156 | Whenever a type is defined, \CFA creates a default zero-argument |
---|
[edc6ea2] | 157 | constructor, a copy constructor, a series of argument-per-field constructors |
---|
| 158 | and a destructor. All user constructors are defined after this. |
---|
[6e7b969] | 159 | |
---|
| 160 | \section{Polymorphism} |
---|
[6c79bef] | 161 | \CFA uses parametric polymorphism to create functions and types that are |
---|
| 162 | defined over multiple types. \CFA polymorphic declarations serve the same role |
---|
[29c9b23] | 163 | as \Cpp templates or Java generics. The ``parametric'' means the polymorphism is |
---|
[6c79bef] | 164 | accomplished by passing argument operations to associate \emph{parameters} at |
---|
| 165 | the call site, and these parameters are used in the function to differentiate |
---|
| 166 | among the types the function operates on. |
---|
| 167 | |
---|
| 168 | Polymorphic declarations start with a universal @forall@ clause that goes |
---|
| 169 | before the standard (monomorphic) declaration. These declarations have the same |
---|
| 170 | syntax except they may use the universal type names introduced by the @forall@ |
---|
| 171 | clause. For example, the following is a polymorphic identity function that |
---|
| 172 | works on any type @T@: |
---|
| 173 | \begin{cfa} |
---|
[edc6ea2] | 174 | forall( T ) T identity( T val ) { return val; } |
---|
| 175 | int forty_two = identity( 42 ); |
---|
| 176 | char capital_a = identity( 'A' ); |
---|
[6c79bef] | 177 | \end{cfa} |
---|
[382edbe] | 178 | Each use of a polymorphic declaration resolves its polymorphic parameters |
---|
[edc6ea2] | 179 | (in this case, just @T@) to concrete types (@int@ in the first use and @char@ |
---|
| 180 | in the second). |
---|
[6e7b969] | 181 | |
---|
[6c79bef] | 182 | To allow a polymorphic function to be separately compiled, the type @T@ must be |
---|
| 183 | constrained by the operations used on @T@ in the function body. The @forall@ |
---|
[382edbe] | 184 | clause is augmented with a list of polymorphic variables (local type names) |
---|
[6c79bef] | 185 | and assertions (constraints), which represent the required operations on those |
---|
| 186 | types used in a function, \eg: |
---|
[f28fdee] | 187 | \begin{cfa} |
---|
[382edbe] | 188 | forall( T | { void do_once(T); } ) |
---|
[6c79bef] | 189 | void do_twice(T value) { |
---|
| 190 | do_once(value); |
---|
| 191 | do_once(value); |
---|
[6e7b969] | 192 | } |
---|
[f28fdee] | 193 | \end{cfa} |
---|
[6c79bef] | 194 | |
---|
| 195 | A polymorphic function can be used in the same way as a normal function. The |
---|
| 196 | polymorphic variables are filled in with concrete types and the assertions are |
---|
| 197 | checked. An assertion is checked by verifying each assertion operation (with |
---|
| 198 | all the variables replaced with the concrete types from the arguments) is |
---|
| 199 | defined at a call site. |
---|
[edc6ea2] | 200 | \begin{cfa} |
---|
| 201 | void do_once(int i) { ... } |
---|
| 202 | int i; |
---|
| 203 | do_twice(i); |
---|
| 204 | \end{cfa} |
---|
| 205 | Any object with a type fulfilling the assertion may be passed as an argument to |
---|
| 206 | a @do_twice@ call. |
---|
[6c79bef] | 207 | |
---|
| 208 | Note, a function named @do_once@ is not required in the scope of @do_twice@ to |
---|
[29c9b23] | 209 | compile it, unlike \Cpp template expansion. Furthermore, call-site inferencing |
---|
[fa7dbf1] | 210 | allows local replacement of the specific parametric functions needs for a |
---|
[6c79bef] | 211 | call. |
---|
[f28fdee] | 212 | \begin{cfa} |
---|
[edc6ea2] | 213 | void do_once(double y) { ... } |
---|
[6e7b969] | 214 | int quadruple(int x) { |
---|
[382edbe] | 215 | void do_once(int & y) { y = y * 2; } |
---|
[21f2e92] | 216 | do_twice(x); |
---|
[6c79bef] | 217 | return x; |
---|
[6e7b969] | 218 | } |
---|
[f28fdee] | 219 | \end{cfa} |
---|
[6c79bef] | 220 | Specifically, the complier deduces that @do_twice@'s T is an integer from the |
---|
[21f2e92] | 221 | argument @x@. It then looks for the most specific definition matching the |
---|
[6c79bef] | 222 | assertion, which is the nested integral @do_once@ defined within the |
---|
| 223 | function. The matched assertion function is then passed as a function pointer |
---|
[21f2e92] | 224 | to @do_twice@ and called within it. |
---|
[382edbe] | 225 | The global definition of @do_once@ is ignored, however if quadruple took a |
---|
[fa7dbf1] | 226 | @double@ argument, then the global definition would be used instead as it |
---|
| 227 | is a better match. |
---|
[382edbe] | 228 | % Aaron's thesis might be a good reference here. |
---|
[6c79bef] | 229 | |
---|
| 230 | To avoid typing long lists of assertions, constraints can be collect into |
---|
[fa7dbf1] | 231 | convenient package called a @trait@, which can then be used in an assertion |
---|
[6c79bef] | 232 | instead of the individual constraints. |
---|
[f28fdee] | 233 | \begin{cfa} |
---|
[6c79bef] | 234 | trait done_once(T) { |
---|
| 235 | void do_once(T); |
---|
[6e7b969] | 236 | } |
---|
[f28fdee] | 237 | \end{cfa} |
---|
[6c79bef] | 238 | and the @forall@ list in the previous example is replaced with the trait. |
---|
[f28fdee] | 239 | \begin{cfa} |
---|
[edc6ea2] | 240 | forall(dtype T | done_once(T)) |
---|
[f28fdee] | 241 | \end{cfa} |
---|
[6c79bef] | 242 | In general, a trait can contain an arbitrary number of assertions, both |
---|
| 243 | functions and variables, and are usually used to create a shorthand for, and |
---|
| 244 | give descriptive names to, common groupings of assertions describing a certain |
---|
| 245 | functionality, like @sumable@, @listable@, \etc. |
---|
| 246 | |
---|
[fa7dbf1] | 247 | Polymorphic structures and unions are defined by qualifying an aggregate type |
---|
[6c79bef] | 248 | with @forall@. The type variables work the same except they are used in field |
---|
| 249 | declarations instead of parameters, returns, and local variable declarations. |
---|
[f28fdee] | 250 | \begin{cfa} |
---|
[edc6ea2] | 251 | forall(dtype T) |
---|
[6e7b969] | 252 | struct node { |
---|
[9b0bb79] | 253 | node(T) * next; |
---|
[edc6ea2] | 254 | T * data; |
---|
[6e7b969] | 255 | } |
---|
[edc6ea2] | 256 | node(int) inode; |
---|
[f28fdee] | 257 | \end{cfa} |
---|
[edc6ea2] | 258 | The generic type @node(T)@ is an example of a polymorphic type usage. Like \Cpp |
---|
| 259 | template usage, a polymorphic type usage must specify a type parameter. |
---|
[6e7b969] | 260 | |
---|
[6c79bef] | 261 | There are many other polymorphism features in \CFA but these are the ones used |
---|
| 262 | by the exception system. |
---|
[6e7b969] | 263 | |
---|
[67c6a47] | 264 | \section{Control Flow} |
---|
| 265 | \CFA has a number of advanced control-flow features: @generator@, @coroutine@, @monitor@, @mutex@ parameters, and @thread@. |
---|
| 266 | The two features that interact with |
---|
| 267 | the exception system are @coroutine@ and @thread@; they and their supporting |
---|
[6c79bef] | 268 | constructs are described here. |
---|
| 269 | |
---|
| 270 | \subsection{Coroutine} |
---|
| 271 | A coroutine is a type with associated functions, where the functions are not |
---|
| 272 | required to finish execution when control is handed back to the caller. Instead |
---|
| 273 | they may suspend execution at any time and be resumed later at the point of |
---|
| 274 | last suspension. (Generators are stackless and coroutines are stackful.) These |
---|
| 275 | types are not concurrent but share some similarities along with common |
---|
| 276 | underpinnings, so they are combined with the \CFA threading library. Further |
---|
| 277 | discussion in this section only refers to the coroutine because generators are |
---|
| 278 | similar. |
---|
| 279 | |
---|
| 280 | In \CFA, a coroutine is created using the @coroutine@ keyword, which is an |
---|
| 281 | aggregate type like @struct,@ except the structure is implicitly modified by |
---|
| 282 | the compiler to satisfy the @is_coroutine@ trait; hence, a coroutine is |
---|
| 283 | restricted by the type system to types that provide this special trait. The |
---|
| 284 | coroutine structure acts as the interface between callers and the coroutine, |
---|
| 285 | and its fields are used to pass information in and out of coroutine interface |
---|
| 286 | functions. |
---|
| 287 | |
---|
| 288 | Here is a simple example where a single field is used to pass (communicate) the |
---|
| 289 | next number in a sequence. |
---|
[f28fdee] | 290 | \begin{cfa} |
---|
[6e7b969] | 291 | coroutine CountUp { |
---|
[9b0bb79] | 292 | unsigned int next; |
---|
[fa7dbf1] | 293 | }; |
---|
[6c79bef] | 294 | CountUp countup; |
---|
[fa7dbf1] | 295 | for (10) sout | resume(countup).next; // print 10 values |
---|
[f28fdee] | 296 | \end{cfa} |
---|
[67c6a47] | 297 | Each coroutine has a @main@ function, which takes a reference to a coroutine |
---|
[6c79bef] | 298 | object and returns @void@. |
---|
[382edbe] | 299 | %[numbers=left] Why numbers on this one? |
---|
[fa7dbf1] | 300 | \begin{cfa}[numbers=left,numberstyle=\scriptsize\sf] |
---|
[edc6ea2] | 301 | void main(CountUp & this) { |
---|
[fa7dbf1] | 302 | for (unsigned int up = 0;; ++up) { |
---|
| 303 | this.next = up; |
---|
[edc6ea2] | 304 | suspend;$\label{suspend}$ |
---|
[6c79bef] | 305 | } |
---|
[6e7b969] | 306 | } |
---|
[f28fdee] | 307 | \end{cfa} |
---|
[6c79bef] | 308 | In this function, or functions called by this function (helper functions), the |
---|
[fa7dbf1] | 309 | @suspend@ statement is used to return execution to the coroutine's resumer |
---|
| 310 | without terminating the coroutine's function(s). |
---|
[6c79bef] | 311 | |
---|
| 312 | A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@. |
---|
| 313 | The first resume calls the @main@ function at the top. Thereafter, resume calls |
---|
| 314 | continue a coroutine in the last suspended function after the @suspend@ |
---|
| 315 | statement, in this case @main@ line~\ref{suspend}. The @resume@ function takes |
---|
| 316 | a reference to the coroutine structure and returns the same reference. The |
---|
| 317 | return value allows easy access to communication variables defined in the |
---|
| 318 | coroutine object. For example, the @next@ value for coroutine object @countup@ |
---|
| 319 | is both generated and collected in the single expression: |
---|
| 320 | @resume(countup).next@. |
---|
[6e7b969] | 321 | |
---|
[67c6a47] | 322 | \subsection{Monitor and Mutex Parameter} |
---|
[6c79bef] | 323 | Concurrency does not guarantee ordering; without ordering results are |
---|
| 324 | non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ |
---|
| 325 | (mutual exclusion) parameters. A monitor is another kind of aggregate, where |
---|
| 326 | the compiler implicitly inserts a lock and instances are compatible with |
---|
| 327 | @mutex@ parameters. |
---|
| 328 | |
---|
| 329 | A function that requires deterministic (ordered) execution, acquires mutual |
---|
| 330 | exclusion on a monitor object by qualifying an object reference parameter with |
---|
| 331 | @mutex@. |
---|
[fa7dbf1] | 332 | \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] |
---|
| 333 | void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB); |
---|
| 334 | \end{lstlisting} |
---|
[6c79bef] | 335 | When the function is called, it implicitly acquires the monitor lock for all of |
---|
| 336 | the mutex parameters without deadlock. This semantics means all functions with |
---|
| 337 | the same mutex type(s) are part of a critical section for objects of that type |
---|
| 338 | and only one runs at a time. |
---|
[6e7b969] | 339 | |
---|
[67c6a47] | 340 | \subsection{Thread} |
---|
[6c79bef] | 341 | Functions, generators, and coroutines are sequential so there is only a single |
---|
| 342 | (but potentially sophisticated) execution path in a program. Threads introduce |
---|
| 343 | multiple execution paths that continue independently. |
---|
[6e7b969] | 344 | |
---|
[6c79bef] | 345 | For threads to work safely with objects requires mutual exclusion using |
---|
| 346 | monitors and mutex parameters. For threads to work safely with other threads, |
---|
| 347 | also requires mutual exclusion in the form of a communication rendezvous, which |
---|
[67c6a47] | 348 | also supports internal synchronization as for mutex objects. For exceptions, |
---|
| 349 | only two basic thread operations are important: fork and join. |
---|
[6e7b969] | 350 | |
---|
[6c79bef] | 351 | Threads are created like coroutines with an associated @main@ function: |
---|
[f28fdee] | 352 | \begin{cfa} |
---|
[6e7b969] | 353 | thread StringWorker { |
---|
[6c79bef] | 354 | const char * input; |
---|
| 355 | int result; |
---|
[6e7b969] | 356 | }; |
---|
| 357 | void main(StringWorker & this) { |
---|
[6c79bef] | 358 | const char * localCopy = this.input; |
---|
| 359 | // ... do some work, perhaps hashing the string ... |
---|
| 360 | this.result = result; |
---|
[6e7b969] | 361 | } |
---|
[6c79bef] | 362 | { |
---|
| 363 | StringWorker stringworker; // fork thread running in "main" |
---|
[fa7dbf1] | 364 | } // implicitly join with thread / wait for completion |
---|
[f28fdee] | 365 | \end{cfa} |
---|
[6c79bef] | 366 | The thread main is where a new thread starts execution after a fork operation |
---|
| 367 | and then the thread continues executing until it is finished. If another thread |
---|
| 368 | joins with an executing thread, it waits until the executing main completes |
---|
| 369 | execution. In other words, everything a thread does is between a fork and join. |
---|
| 370 | |
---|
| 371 | From the outside, this behaviour is accomplished through creation and |
---|
| 372 | destruction of a thread object. Implicitly, fork happens after a thread |
---|
| 373 | object's constructor is run and join happens before the destructor runs. Join |
---|
| 374 | can also be specified explicitly using the @join@ function to wait for a |
---|
| 375 | thread's completion independently from its deallocation (\ie destructor |
---|
| 376 | call). If @join@ is called explicitly, the destructor does not implicitly join. |
---|