Changes in / [4520b77e:a065f1f]
- Files:
-
- 2 added
- 24 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/bibliography/pl.bib
r4520b77e ra065f1f 188 188 Unstructured {\it sends\/} and {\it accepts\/} are forbidden. To 189 189 this the mechanisms of {\it delegation\/} and {\it delay queues\/} 190 are added to enable switching and triggering of activities. 191 Concurrent subactivities and atomic actions are provided for 190 are added to enable switching and triggering of activities. 191 Concurrent subactivities and atomic actions are provided for 192 192 compactness and simplicity. We show how solutions to many important 193 193 concurrent problems [sic], such as pipelining, constraint management 194 and ``administration'' can be compactly expressed using these 194 and ``administration'' can be compactly expressed using these 195 195 mechanisms. 196 196 }, … … 529 529 like ``c is a collection with element type e'', but how such things 530 530 are used isn't explained. 531 531 532 532 For each descriptive class used in a parameter list, an implicit 533 533 parameter is created that is passed a vector of procedures. … … 1172 1172 @techreport{Prokopec11, 1173 1173 keywords = {ctrie, concurrent map}, 1174 contributer = {a3moss@uwaterloo.ca}, 1174 contributer = {a3moss@uwaterloo.ca}, 1175 1175 title = {Cache-aware lock-free concurrent hash tries}, 1176 1176 author = {Prokopec, Aleksandar and Bagwell, Phil and Odersky, Martin}, … … 1500 1500 year = 2001, 1501 1501 url = {http://citeseer.ist.psu.edu/berger01composing.html} 1502 } 1502 } 1503 1503 1504 1504 @article{Andrews83, … … 1545 1545 We give a rationale for our decisions and compare Concurrent C 1546 1546 extensions with the concurrent programming facilities in Ada. 1547 Concurrent C has been implemented on the UNIX system running on a 1547 Concurrent C has been implemented on the UNIX system running on a 1548 1548 single processor. A distributed version of Concurrent C is being 1549 1549 implemented. … … 1814 1814 keywords = {objects, concurrency}, 1815 1815 contributer = {gjditchfield@plg}, 1816 author = {P. A. Buhr and G. J. Ditchfield and B. M. Younger and C. R. Zarnke}, 1816 author = {P. A. Buhr and G. J. Ditchfield and B. M. Younger and C. R. Zarnke}, 1817 1817 title = {Concurrency in {C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}}}, 1818 1818 institution = {Department of Computer Science, University of Waterloo}, … … 2044 2044 series = {Lecture Notes in Computer Science, Ed. by G. Goos and J. Hartmanis} 2045 2045 } 2046 2046 2047 2047 @article{Wang71, 2048 2048 keywords = {coroutines}, … … 2056 2056 pages = {425-449}, 2057 2057 } 2058 2058 2059 2059 @article{Castagna95, 2060 2060 keywords = {type-systems, covariance, contravariance}, … … 2390 2390 year = 1996, 2391 2391 } 2392 2392 2393 2393 @article{Richardson93, 2394 2394 keywords = {C++, persistence, database}, … … 2473 2473 publisher = {ACM}, 2474 2474 address = {New York, NY, USA}, 2475 } 2475 } 2476 2476 2477 2477 @article{design, … … 2700 2700 publisher = {ACM}, 2701 2701 address = {New York, NY, USA}, 2702 } 2702 } 2703 2703 2704 2704 @book{Eiffel, … … 3357 3357 publisher = {ACM}, 3358 3358 address = {New York, NY, USA}, 3359 } 3359 } 3360 3360 3361 3361 @manual{Fortran95, … … 3740 3740 keywords = {processes, distributed computing}, 3741 3741 contributer = {pabuhr@plg}, 3742 author = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini}, 3742 author = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini}, 3743 3743 title = {Hermes: A Language for Distributed Computing}, 3744 3744 institution = {IBM T. J. Watson Research Center}, … … 3751 3751 keywords = {processes, distributed computing}, 3752 3752 contributer = {pabuhr@plg}, 3753 author = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini}, 3753 author = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini}, 3754 3754 title = {Hermes: A Language for Distributed Computing}, 3755 3755 publisher = {Prentice-Hall}, … … 4302 4302 pages = {85-103} 4303 4303 } 4304 4304 4305 4305 @article{Murer96, 4306 4306 keywords = {interators, generators, cursors}, … … 4330 4330 4331 4331 % J 4332 4332 4333 4333 @book{Java, 4334 4334 keywords = {Java}, … … 4627 4627 publisher = {ACM}, 4628 4628 address = {New York, NY, USA}, 4629 } 4629 } 4630 4630 4631 4631 @article{Dice15, … … 4978 4978 number = 31 4979 4979 } 4980 4980 4981 4981 @article{Dueck90, 4982 4982 keywords = {attribute grammars}, … … 5107 5107 keywords = {multiple inheritance}, 5108 5108 contributer = {pabuhr@plg}, 5109 author = {Harry Bretthauer and Thomas Christaller and J\"{u}rgen Kopp}, 5109 author = {Harry Bretthauer and Thomas Christaller and J\"{u}rgen Kopp}, 5110 5110 title = {Multiple vs. Single Inheritance in Object-oriented Programming Languages. What do we really want?}, 5111 5111 institution = {Gesellschaft F\"{u}r Mathematik und Datenverarbeitung mbH}, … … 5650 5650 publisher = {ACM}, 5651 5651 address = {New York, NY, USA}, 5652 } 5652 } 5653 5653 5654 5654 @book{Deitel04, … … 5827 5827 year = 1980, 5828 5828 month = nov, volume = 15, number = 11, pages = {47-56}, 5829 note = {Proceedings of the ACM-SIGPLAN Symposium on the {Ada} Programming Language}, 5829 note = {Proceedings of the ACM-SIGPLAN Symposium on the {Ada} Programming Language}, 5830 5830 comment = { 5831 5831 The two-pass (bottom-up, then top-down) algorithm, with a proof … … 5957 5957 Given a base typed lambda calculus with function types, type 5958 5958 abstractions, and a recursive expression \(\mbox{fix } x:t.e\), 5959 then type inference for the partially typed language 5959 then type inference for the partially typed language 5960 5960 \begin{eqnarray} 5961 5961 \lambda x:\tau.e &\Rightarrow& \lambda x.e \\ … … 6603 6603 manner. The paper also discusses efficient composition of 6604 6604 sequences of asynchronous calls to different locations in a 6605 network. 6605 network. 6606 6606 } 6607 6607 } … … 6616 6616 volume = 32, number = 4, pages = {305-311}, 6617 6617 abstract = { 6618 6618 6619 6619 } 6620 6620 } … … 6934 6934 partitioning switch statements into dense tables. It also 6935 6935 implements target-independent function tracing and expression-level 6936 profiling. 6936 profiling. 6937 6937 } 6938 6938 } … … 7150 7150 publisher = {ACM}, 7151 7151 address = {New York, NY, USA}, 7152 } 7152 } 7153 7153 7154 7154 @inproceedings{Leissa14, … … 7268 7268 keywords = {Smalltalk, abstract class, protocol}, 7269 7269 contributer = {gjditchfield@plg}, 7270 author = {A. Goldberg and D. Robson}, 7270 author = {A. Goldberg and D. Robson}, 7271 7271 title = {Smalltalk-80: The Language and its Implementation}, 7272 7272 publisher = {Addison-Wesley}, … … 7845 7845 title = {Thread (computing)}, 7846 7846 author = {{Threading Model}}, 7847 howpublished= {\href{https://en.wikipedia.org/wiki/Thread_(computing)}{https://\-en.wikipedia.org/\-wiki/\-Thread\_ (computing)}},7847 howpublished= {\href{https://en.wikipedia.org/wiki/Thread_(computing)}{https://\-en.wikipedia.org/\-wiki/\-Thread\_\-(computing)}}, 7848 7848 } 7849 7849 … … 7889 7889 note = {Lecture Notes in Computer Science, v. 19}, 7890 7890 abstract = { 7891 7891 7892 7892 } 7893 7893 } … … 8008 8008 publisher = {USENIX Association}, 8009 8009 address = {Berkeley, CA, USA}, 8010 } 8010 } 8011 8011 8012 8012 @article{Leroy00, … … 8354 8354 author = {Bjarne Stroustrup}, 8355 8355 title = {What is ``Object-Oriented Programming''?}, 8356 booktitle = {Proceedings of the First European Conference on Object Oriented Programming}, 8356 booktitle = {Proceedings of the First European Conference on Object Oriented Programming}, 8357 8357 month = jun, 8358 8358 year = 1987 … … 8396 8396 publisher = {ACM}, 8397 8397 address = {New York, NY, USA}, 8398 } 8398 } 8399 8399 8400 8400 % X -
doc/theses/thierry_delisle_PhD/thesis/local.bib
r4520b77e ra065f1f 583 583 title={Per-entity load tracking}, 584 584 author={Corbet, Jonathan}, 585 journal={LWN article, available at: https://lwn.net/Articles/531853},585 journal={LWN article, available at: {\href{https://lwn.net/Articles/531853}{https://\-lwn.net/\-Articles/\-531853}}}, 586 586 year={2013} 587 587 } … … 717 717 title = {Scheduling Benchmarks}, 718 718 author = {Thierry Delisle}, 719 howpublished = {\href{https://github.com/cforall/SchedulingBenchmarks_PhD22}{https://\-github.com/\-cforall/\-Scheduling Benchmarks\_\-PhD22}},719 howpublished = {\href{https://github.com/cforall/SchedulingBenchmarks_PhD22}{https://\-github.com/\-cforall/\-Scheduling\-Benchmarks\_\-PhD22}}, 720 720 } 721 721 … … 832 832 title = "eventfd(2) Linux User's Manual", 833 833 year = "2019", 834 month = "M Arch",834 month = "March", 835 835 } 836 836 … … 1060 1060 year = "2020", 1061 1061 month = "June", 1062 howpublished = "\href{https://xkcd.com/2318/} ",1062 howpublished = "\href{https://xkcd.com/2318/}{https://\-xkcd.com/\-2318/}", 1063 1063 note = "[Online; accessed 10-June-2020]" 1064 1064 } … … 1069 1069 year = "2011", 1070 1070 month = "June", 1071 howpublished = "\href{https://xkcd.com/908/} ",1071 howpublished = "\href{https://xkcd.com/908/}{https://\-xkcd.com/\-908/}", 1072 1072 note = "[Online; accessed 25-August-2022]" 1073 1073 } -
doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex
r4520b77e ra065f1f 187 187 Looking at the left column on AMD, Figures~\ref{fig:cycle:nasus:ops} and \ref{fig:cycle:nasus:ns} all 4 runtimes achieve very similar throughput and scalability. 188 188 However, as the number of \procs grows higher, the results on AMD show notably more variability than on Intel. 189 The different performance improvements and plateaus are due to cache topology and appear at the expected :\proc counts of 64, 128 and 192, for the same reasons as on Intel.189 The different performance improvements and plateaus are due to cache topology and appear at the expected \proc counts of 64, 128 and 192, for the same reasons as on Intel. 190 190 Looking next at the right column on AMD, Figures~\ref{fig:cycle:nasus:low:ops} and \ref{fig:cycle:nasus:low:ns}, Tokio and Go have the same throughput performance, while \CFA is slightly slower. 191 191 This result is different than on Intel, where Tokio behaved like \CFA rather than behaving like Go. … … 491 491 \section{Locality} 492 492 493 As mentioned in the churn benchmark, when \glslink{atsched}{unparking} a \at, it is possible to either \unpark to the local or remote ready-queue.\footnote{494 It is also possible to \unpark to a third unrelated ready-queue, but without additional knowledge about the situation, it is likely to degrade performance.}493 As mentioned in the churn benchmark, when \glslink{atsched}{unparking} a \at, it is possible to either \unpark to the local or remote sub-queue.\footnote{ 494 It is also possible to \unpark to a third unrelated sub-queue, but without additional knowledge about the situation, it is likely to degrade performance.} 495 495 The locality experiment includes two variations of the churn benchmark, where a data array is added. 496 496 In both variations, before @V@ing the semaphore, each \at calls a @work@ function which increments random cells inside the data array. … … 720 720 721 721 In both variations, the experiment effectively measures how long it takes for all \ats to run once after a given synchronization point. 722 In an ideal scenario where the scheduler is strictly FIFO, every thread would run once after the synchronization and therefore the delay between leaders would be given by, $ (CSL + SL) / (NP - 1)$,723 where $CSL$ is the context-switch latency, $SL$ is the cost for enqueueing and dequeuing a \at, and $NP$ is the number of \procs.722 In an ideal scenario where the scheduler is strictly FIFO, every thread would run once after the synchronization and therefore the delay between leaders would be given by, $NT(CSL + SL) / (NP - 1)$, 723 where $CSL$ is the context-switch latency, $SL$ is the cost for enqueueing and dequeuing a \at, $NT$ is the number of \ats, and $NP$ is the number of \procs. 724 724 However, if the scheduler allows \ats to run many times before other \ats can run once, this delay increases. 725 725 The semaphore version is an approximation of strictly FIFO scheduling, where none of the \ats \emph{attempt} to run more than once. … … 734 734 735 735 \begin{table} 736 \caption[Transfer Benchmark on Intel and AMD]{Transfer Benchmark on Intel and AMD\smallskip\newline Average measurement of how long it takes for all \ats to acknowledge the leader \at.737 DNC stands for ``did not complete'', meaning that after 5 seconds of a new leader being decided, some \ats still had not acknowledged the new leader.}738 \label{fig:transfer:res}739 736 \setlength{\extrarowheight}{2pt} 740 737 \setlength{\tabcolsep}{5pt} … … 753 750 \end{tabular} 754 751 \end{centering} 752 \caption[Transfer Benchmark on Intel and AMD]{Transfer Benchmark on Intel and AMD\smallskip\newline Average measurement of how long it takes for all \ats to acknowledge the leader \at. 753 DNC stands for ``did not complete'', meaning that after 5 seconds of a new leader being decided, some \ats still had not acknowledged the new leader.} 754 \label{fig:transfer:res} 755 755 \end{table} 756 756 … … 768 768 769 769 Looking at the next two columns, the results for the yield variation on Intel, the story is very different. 770 \CFA achieves better latencies, presumably due to nosynchronization with the yield.770 \CFA achieves better latencies, presumably due to the lack of synchronization with the yield. 771 771 Go does complete the experiment, but with drastically higher latency: 772 772 latency at 2 \procs is $350\times$ higher than \CFA and $70\times$ higher at 192 \procs. 773 This difference is because Go has a classic work-stealing scheduler, but it adds coarse-grain preemption 774 , which interrupts the spinning leader after a period. 773 This difference is because Go has a classic work-stealing scheduler, but it adds coarse-grain preemption, which interrupts the spinning leader after a period. 775 774 Neither Libfibre nor Tokio complete the experiment. 776 775 Both runtimes also use classical work-stealing scheduling without preemption, and therefore, none of the work queues are ever emptied so no load balancing occurs. -
doc/theses/thierry_delisle_PhD/thesis/text/io.tex
r4520b77e ra065f1f 25 25 \paragraph{\lstinline{select}} is the oldest of these options, and takes as input a contiguous array of bits, where each bit represents a file descriptor of interest. 26 26 Hence, the array length must be as long as the largest FD currently of interest. 27 On return, it outputs the set in place to identify which of the file descriptors changed state.27 On return, it outputs the set motified in place to identify which of the file descriptors changed state. 28 28 This destructive change means selecting in a loop requires re-initializing the array for each iteration. 29 29 Another limit of @select@ is that calls from different \glspl{kthrd} sharing FDs are independent. … … 35 35 \paragraph{\lstinline{poll}} is the next oldest option, and takes as input an array of structures containing the FD numbers rather than their position in an array of bits, allowing a more compact input for interest sets that contain widely spaced FDs. 36 36 For small interest sets with densely packed FDs, the @select@ bit mask can take less storage, and hence, copy less information into the kernel. 37 Furthermore, @poll@ is non-destructive, so the array of structures does not have to be re-initialized on every call.37 However, @poll@ is non-destructive, so the array of structures does not have to be re-initialized on every call. 38 38 Like @select@, @poll@ suffers from the limitation that the interest set cannot be changed by other \glspl{kthrd}, while a manager thread is blocked in @poll@. 39 39 … … 314 314 A simple approach to polling is to allocate a \at per @io_uring@ instance and simply let the poller \ats poll their respective instances when scheduled. 315 315 316 With the pool of S EQinstances approach, the big advantage is that it is fairly flexible.316 With the pool of SQE instances approach, the big advantage is that it is fairly flexible. 317 317 It does not impose restrictions on what \ats submitting \io operations can and cannot do between allocations and submissions. 318 318 It also can gracefully handle running out of resources, SQEs or the kernel returning @EBUSY@. … … 320 320 The routing and allocation algorithm needs to keep track of which ring instances have available SQEs, block incoming requests if no instance is available, prevent barging if \ats are already queued up waiting for SQEs and handle SQEs being freed. 321 321 The submission side needs to safely append SQEs to the ring buffer, correctly handle chains, make sure no SQE is dropped or left pending forever, notify the allocation side when SQEs can be reused, and handle the kernel returning @EBUSY@. 322 Compared to the private-instance approach, all this synchronization has a significant cost this synchronization is entirely overhead.322 Compared to the private-instance approach, all this synchronization has a significant cost and this synchronization is entirely overhead. 323 323 324 324 \subsubsection{Instance borrowing} -
doc/theses/thierry_delisle_PhD/thesis/text/practice.tex
r4520b77e ra065f1f 101 101 This leaves too many \procs when there are not enough \ats for all the \procs to be useful. 102 102 These idle \procs cannot be removed because their lifetime is controlled by the application, and only the application knows when the number of \ats may increase or decrease. 103 While idle \procs can spin until work appears, this approach wastes energy, unnecessarily produces heat and prevents other applications from using the processor.103 While idle \procs can spin until work appears, this approach wastes energy, unnecessarily produces heat and prevents other applications from using the \gls{hthrd}. 104 104 Therefore, idle \procs are put into an idle state, called \newterm{Idle-Sleep}, where the \gls{kthrd} is blocked until the scheduler deems it is needed. 105 105 … … 107 107 First, a data structure needs to keep track of all \procs that are in idle sleep. 108 108 Because idle sleep is spurious, this data structure has strict performance requirements, in addition to strict correctness requirements. 109 Next, some mechanism is needed to block \glspl{kthrd}, \eg @pthread_cond_wait@ o na pthread semaphore.109 Next, some mechanism is needed to block \glspl{kthrd}, \eg @pthread_cond_wait@ or a pthread semaphore. 110 110 The complexity here is to support \at \glslink{atblock}{parking} and \glslink{atsched}{unparking}, user-level locking, timers, \io operations, and all other \CFA features with minimal complexity. 111 111 Finally, the scheduler needs a heuristic to determine when to block and unblock an appropriate number of \procs. -
libcfa/src/bits/locks.hfa
r4520b77e ra065f1f 13 13 // Created On : Tue Oct 31 15:14:38 2017 14 14 // Last Modified By : Peter A. Buhr 15 // Last Modified On : Wed Aug 12 14:18:07 202016 // Update Count : 1 315 // Last Modified On : Mon Sep 19 18:51:53 2022 16 // Update Count : 17 17 17 // 18 18 … … 60 60 61 61 disable_interrupts(); 62 for ( unsigned int i = 1;; i += 1) {62 for ( i; 1 ~ @ ) { 63 63 if ( (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0) ) break; 64 64 #ifndef NOEXPBACK 65 65 // exponential spin 66 for ( volatile unsigned int s = 0; s < spin; s += 1) Pause();66 for ( volatile unsigned int s; 0 ~ spin ) Pause(); 67 67 68 68 // slowly increase by powers of 2 -
libcfa/src/concurrency/kernel/cluster.hfa
r4520b77e ra065f1f 63 63 } 64 64 } 65 return (max + 2 * max) / 2;65 return 8 * max; 66 66 } 67 67 -
libcfa/src/concurrency/kernel/fwd.hfa
r4520b77e ra065f1f 179 179 // Similar to a binary semaphore with a 'one shot' semantic 180 180 // is expected to be discarded after each party call their side 181 enum(struct thread$ *) { oneshot_ARMED = 0p, oneshot_FULFILLED = 1p }; 181 182 struct oneshot { 182 183 // Internal state : 183 // 0p : is initial state (wait will block)184 // 1p : fulfilled (wait won't block)184 // armed : initial state, wait will block 185 // fulfilled : wait won't block 185 186 // any thread : a thread is currently waiting 186 187 struct thread$ * volatile ptr; … … 189 190 static inline { 190 191 void ?{}(oneshot & this) { 191 this.ptr = 0p;192 this.ptr = oneshot_ARMED; 192 193 } 193 194 … … 199 200 for() { 200 201 struct thread$ * expected = this.ptr; 201 if(expected == 1p) return false;202 if(expected == oneshot_FULFILLED) return false; 202 203 if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 203 204 park(); 204 /* paranoid */ verify( this.ptr == 1p);205 /* paranoid */ verify( this.ptr == oneshot_FULFILLED ); 205 206 return true; 206 207 } … … 211 212 // return true if a thread was unparked 212 213 thread$ * post(oneshot & this, bool do_unpark = true) { 213 struct thread$ * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);214 if( got == 0p || got == 1p) return 0p;214 struct thread$ * got = __atomic_exchange_n( &this.ptr, oneshot_FULFILLED, __ATOMIC_SEQ_CST); 215 if( got == oneshot_ARMED || got == oneshot_FULFILLED ) return 0p; 215 216 if(do_unpark) unpark( got ); 216 217 return got; … … 223 224 // thread on "any of" [a given set of] futures. 224 225 // does not support multiple threads waiting on the same future 226 enum(struct oneshot *) { future_ARMED = 0p, future_FULFILLED = 1p, future_PROGRESS = 2p, future_ABANDONED = 3p }; 225 227 struct future_t { 226 228 // Internal state : 227 // 0p : is initial state (wait will block)228 // 1p : fulfilled (wait won't block)229 // 2p : in progress ()230 // 3p : abandoned, server should delete229 // armed : initial state, wait will block 230 // fulfilled : result is ready, wait won't block 231 // progress : someone else is in the process of fulfilling this 232 // abandoned : client no longer cares, server should delete 231 233 // any oneshot : a context has been setup to wait, a thread could wait on it 232 234 struct oneshot * volatile ptr; … … 235 237 static inline { 236 238 void ?{}(future_t & this) { 237 this.ptr = 0p;239 this.ptr = future_ARMED; 238 240 } 239 241 … … 242 244 void reset(future_t & this) { 243 245 // needs to be in 0p or 1p 244 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);246 __atomic_exchange_n( &this.ptr, future_ARMED, __ATOMIC_SEQ_CST); 245 247 } 246 248 247 249 // check if the future is available 248 250 bool available( future_t & this ) { 249 while( this.ptr == 2p) Pause();250 return this.ptr == 1p;251 while( this.ptr == future_PROGRESS ) Pause(); 252 return this.ptr == future_FULFILLED; 251 253 } 252 254 … … 254 256 // intented to be use by wait, wait_any, waitfor, etc. rather than used directly 255 257 bool setup( future_t & this, oneshot & wait_ctx ) { 256 /* paranoid */ verify( wait_ctx.ptr == 0p || wait_ctx.ptr == 1p);258 /* paranoid */ verify( wait_ctx.ptr == oneshot_ARMED || wait_ctx.ptr == oneshot_FULFILLED ); 257 259 // The future needs to set the wait context 258 260 for() { 259 261 struct oneshot * expected = this.ptr; 260 262 // Is the future already fulfilled? 261 if(expected == 1p) return false; // Yes, just return false (didn't block)263 if(expected == future_FULFILLED) return false; // Yes, just return false (didn't block) 262 264 263 265 // The future is not fulfilled, try to setup the wait context … … 277 279 278 280 // attempt to remove the context so it doesn't get consumed. 279 if(__atomic_compare_exchange_n( &this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {281 if(__atomic_compare_exchange_n( &this.ptr, &expected, future_ARMED, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 280 282 // we still have the original context, then no one else saw it 281 283 return false; 282 284 } 283 285 284 // expected == 0p: future was never actually setup, just return285 if( expected == 0p) return false;286 287 // expected == 1p: the future is ready and the context was fully consumed286 // expected == ARMED: future was never actually setup, just return 287 if( expected == future_ARMED ) return false; 288 289 // expected == FULFILLED: the future is ready and the context was fully consumed 288 290 // the server won't use the pointer again 289 291 // It is safe to delete (which could happen after the return) 290 if( expected == 1p) return true;291 292 // expected == 2p: the future is ready but the context hasn't fully been consumed292 if( expected == future_FULFILLED ) return true; 293 294 // expected == PROGRESS: the future is ready but the context hasn't fully been consumed 293 295 // spin until it is safe to move on 294 if( expected == 2p) {295 while( this.ptr != 1p) Pause();296 /* paranoid */ verify( this.ptr == 1p);296 if( expected == future_PROGRESS ) { 297 while( this.ptr != future_FULFILLED ) Pause(); 298 /* paranoid */ verify( this.ptr == future_FULFILLED ); 297 299 return true; 298 300 } … … 305 307 // Mark the future as abandoned, meaning it will be deleted by the server 306 308 bool abandon( future_t & this ) { 307 /* paranoid */ verify( this.ptr != 3p);309 /* paranoid */ verify( this.ptr != future_ABANDONED ); 308 310 309 311 // Mark the future as abandonned 310 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);312 struct oneshot * got = __atomic_exchange_n( &this.ptr, future_ABANDONED, __ATOMIC_SEQ_CST); 311 313 312 314 // If the future isn't already fulfilled, let the server delete it 313 if( got == 0p) return false;314 315 // got == 2p: the future is ready but the context hasn't fully been consumed315 if( got == future_ARMED ) return false; 316 317 // got == PROGRESS: the future is ready but the context hasn't fully been consumed 316 318 // spin until it is safe to move on 317 if( got == 2p) {318 while( this.ptr != 1p) Pause();319 got = 1p;319 if( got == future_PROGRESS ) { 320 while( this.ptr != future_FULFILLED ) Pause(); 321 got = future_FULFILLED; 320 322 } 321 323 322 324 // The future is completed delete it now 323 /* paranoid */ verify( this.ptr != 1p);325 /* paranoid */ verify( this.ptr != future_FULFILLED ); 324 326 free( &this ); 325 327 return true; … … 336 338 #pragma GCC diagnostic ignored "-Wfree-nonheap-object" 337 339 #endif 338 if( expected == 3p) { free( &this ); return 0p; }340 if( expected == future_ABANDONED ) { free( &this ); return 0p; } 339 341 #if defined(__GNUC__) && __GNUC__ >= 7 340 342 #pragma GCC diagnostic pop 341 343 #endif 342 344 343 /* paranoid */ verify( expected != 1p); // Future is already fulfilled, should not happen344 /* paranoid */ verify( expected != 2p); // Future is bein fulfilled by someone else, this is even less supported then the previous case.345 /* paranoid */ verify( expected != future_FULFILLED ); // Future is already fulfilled, should not happen 346 /* paranoid */ verify( expected != future_PROGRESS ); // Future is bein fulfilled by someone else, this is even less supported then the previous case. 345 347 346 348 // If there is a wait context, we need to consume it and mark it as consumed after 347 349 // If there is no context then we can skip the in progress phase 348 struct oneshot * want = expected == 0p ? 1p : 2p;350 struct oneshot * want = expected == future_ARMED ? future_FULFILLED : future_PROGRESS; 349 351 if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { 350 if( expected == 0p) { return 0p; }352 if( expected == future_ARMED ) { return 0p; } 351 353 thread$ * ret = post( *expected, do_unpark ); 352 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);354 __atomic_store_n( &this.ptr, future_FULFILLED, __ATOMIC_SEQ_CST); 353 355 return ret; 354 356 } … … 366 368 367 369 // Wait for the future to tru 368 while( this.ptr == 2p) Pause();370 while( this.ptr == future_PROGRESS ) Pause(); 369 371 // Make sure the state makes sense 370 372 // Should be fulfilled, could be in progress but it's out of date if so … … 372 374 // and the oneshot should not be needed any more 373 375 __attribute__((unused)) struct oneshot * was = this.ptr; 374 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was );376 /* paranoid */ verifyf( was == future_FULFILLED, "Expected this.ptr to be 1p, was %p\n", was ); 375 377 376 378 // Mark the future as fulfilled, to be consistent -
libcfa/src/concurrency/monitor.hfa
r4520b77e ra065f1f 60 60 void ^?{}( monitor_dtor_guard_t & this ); 61 61 62 /* 62 63 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } ) 63 64 void delete( T * th ) { … … 65 66 free( th ); 66 67 } 68 */ 67 69 68 70 static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } ) -
libcfa/src/iostream.cfa
r4520b77e ra065f1f 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Aug 25 18:05:49202213 // Update Count : 135 412 // Last Modified On : Sat Aug 27 15:04:15 2022 13 // Update Count : 1358 14 14 // 15 15 … … 765 765 fmtuc.flags.pc = f.flags.pc; 766 766 fmtuc.flags.nobsdp = f.flags.nobsdp; 767 for ( unsigned int i = 0; f.val[i] != '\0'; i += 1) {767 for ( i; 0 ~ @ : @; f.val[i] != '\0' ) { 768 768 fmtuc.val = f.val[i]; 769 769 // os | fmtuc | nonl; … … 931 931 if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) { // take first 39 characters, ignore remaining 932 932 ullli = 0; 933 for ( unsigned int i = 0; s[i] != '\0'; i += 1) {933 for ( i; 0 ~ @ : @; s[i] != '\0' ) { 934 934 ullli = ullli * 10 + s[i] - '0'; 935 935 } // for -
src/AST/Print.cpp
r4520b77e ra065f1f 90 90 91 91 static constexpr auto Qualifiers = make_array<const char*>( 92 "const", "restrict", "volatile", " lvalue", "mutex", "_Atomic"92 "const", "restrict", "volatile", "mutex", "_Atomic" 93 93 ); 94 94 }; … … 1635 1635 constexpr array<const char*, 3> Printer::Names::FuncSpecifiers; 1636 1636 constexpr array<const char*, 6> Printer::Names::StorageClasses; 1637 constexpr array<const char*, 6> Printer::Names::Qualifiers;1637 constexpr array<const char*, 5> Printer::Names::Qualifiers; 1638 1638 } -
src/AST/Type.hpp
r4520b77e ra065f1f 412 412 std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; } 413 413 bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; } 414 415 414 }; 416 415 -
src/AST/TypeEnvironment.hpp
r4520b77e ra065f1f 56 56 struct AssertCompare { 57 57 bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const { 58 auto kind1 = ast::SymbolTable::getSpecialFunctionKind(d1->var->name); 59 auto kind2 = ast::SymbolTable::getSpecialFunctionKind(d2->var->name); 60 // heuristics optimization: force special functions to go last 61 if (kind1 > kind2) return true; 62 else if (kind1 < kind2) return false; 63 58 64 int cmp = d1->var->name.compare( d2->var->name ); 59 65 return cmp < 0 || ( cmp == 0 && d1->result < d2->result ); -
src/AST/module.mk
r4520b77e ra065f1f 24 24 AST/Copy.cpp \ 25 25 AST/Copy.hpp \ 26 AST/Create.cpp \ 27 AST/Create.hpp \ 26 28 AST/CVQualifiers.hpp \ 27 29 AST/Decl.cpp \ -
src/CodeGen/CodeGenerator.cc
r4520b77e ra065f1f 494 494 assert( false ); 495 495 } // switch 496 } else if( varExpr->get_var()->get_linkage() == LinkageSpec::BuiltinCFA && varExpr->get_var()->get_name() == "intptr" ) { 497 // THIS is a hack to make it a constant until a proper constexpr solution is created 498 output << "((void*)"; 499 std::list< Expression* >::iterator arg = applicationExpr->get_args().begin(); 500 (*arg++)->accept( *visitor ); 501 output << ")"; 496 502 } else { 497 503 varExpr->accept( *visitor ); -
src/Concurrency/Waitfor.cc
r4520b77e ra065f1f 402 402 403 403 clause.target.function = nullptr; 404 clause.target.arguments. empty();404 clause.target.arguments.clear(); 405 405 clause.condition = nullptr; 406 406 } -
src/GenPoly/InstantiateGenericNew.cpp
r4520b77e ra065f1f 22 22 23 23 #include "AST/Copy.hpp" // for deepCopy 24 #include "AST/Create.hpp" // for asForward 24 25 #include "AST/Pass.hpp" // for Pass, WithGuard, WithShortCi... 25 26 #include "AST/TranslationUnit.hpp" // for TranslationUnit … … 255 256 void stripInstParams( ast::BaseInstType * inst ) { 256 257 inst->params.clear(); 257 }258 259 // TODO: I think this should become a generic helper.260 template<typename Aggr>261 Aggr * asForward( Aggr const * decl ) {262 if ( !decl->body ) {263 return nullptr;264 }265 Aggr * mut = ast::deepCopy( decl );266 mut->body = false;267 mut->members.clear();268 return mut;269 258 } 270 259 … … 553 542 // Forward declare before recursion. (TODO: Only when needed, #199.) 554 543 insert( inst, typeSubs, newDecl ); 555 if ( AggrDecl const * forwardDecl = as Forward( newDecl ) ) {544 if ( AggrDecl const * forwardDecl = ast::asForward( newDecl ) ) { 556 545 declsToAddBefore.push_back( forwardDecl ); 557 546 } -
src/GenPoly/Lvalue2.cc
r4520b77e ra065f1f 23 23 } 24 24 25 26 25 } -
src/ResolvExpr/CommonType.cc
r4520b77e ra065f1f 28 28 #include "Unify.h" // for unifyExact, WidenMode 29 29 #include "typeops.h" // for isFtype 30 #include "Tuples/Tuples.h" 30 31 31 32 // #define DEBUG … … 675 676 ast::TypeEnvironment & tenv; 676 677 const ast::OpenVarSet & open; 678 ast::AssertionSet & need; 679 ast::AssertionSet & have; 677 680 public: 678 681 static size_t traceId; … … 681 684 CommonType_new( 682 685 const ast::Type * t2, WidenMode w, const ast::SymbolTable & st, 683 ast::TypeEnvironment & env, const ast::OpenVarSet & o ) 684 : type2( t2 ), widen( w ), symtab( st ), tenv( env ), open( o ), result() {} 686 ast::TypeEnvironment & env, const ast::OpenVarSet & o, 687 ast::AssertionSet & need, ast::AssertionSet & have ) 688 : type2( t2 ), widen( w ), symtab( st ), tenv( env ), open( o ), need (need), have (have) ,result() {} 685 689 686 690 void previsit( const ast::Node * ) { visit_children = false; } … … 753 757 bool tryResolveWithTypedEnum( const ast::Type * type1 ) { 754 758 if (auto enumInst = dynamic_cast<const ast::EnumInstType *> (type2) ) { 755 ast::AssertionSet have, need; // unused756 759 ast::OpenVarSet newOpen{ open }; 757 760 if (enumInst->base->base … … 792 795 reset_qualifiers( t2 ); 793 796 794 ast::AssertionSet have, need;795 797 ast::OpenVarSet newOpen{ open }; 796 798 if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) { … … 803 805 } 804 806 } 807 else if ( isFtype (t1) && isFtype (t2) ) { 808 auto f1 = t1.as<ast::FunctionType>(); 809 if (!f1) return; 810 auto f2 = t2.strict_as<ast::FunctionType>(); 811 812 assertf(f1->returns.size() <= 1, "Function return should not be a list"); 813 assertf(f2->returns.size() <= 1, "Function return should not be a list"); 814 815 if ( 816 ( f1->params.size() != f2->params.size() || f1->returns.size() != f2->returns.size() ) 817 && ! f1->isTtype() 818 && ! f2->isTtype() 819 ) return; 820 821 auto params1 = flattenList( f1->params, tenv ); 822 auto params2 = flattenList( f2->params, tenv ); 823 824 auto crnt1 = params1.begin(); 825 auto crnt2 = params2.begin(); 826 auto end1 = params1.end(); 827 auto end2 = params2.end(); 828 829 while (crnt1 != end1 && crnt2 != end2 ) { 830 const ast::Type * arg1 = *crnt1; 831 const ast::Type * arg2 = *crnt2; 832 833 bool isTuple1 = Tuples::isTtype( t1 ); 834 bool isTuple2 = Tuples::isTtype( t2 ); 835 836 // assumes here that ttype *must* be last parameter 837 if ( isTuple1 && ! isTuple2 ) { 838 // combine remainder of list2, then unify 839 if (unifyExact( 840 arg1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open, 841 noWiden(), symtab )) { 842 break; 843 844 } 845 else return; 846 } else if ( ! isTuple1 && isTuple2 ) { 847 // combine remainder of list1, then unify 848 if (unifyExact( 849 tupleFromTypes( crnt1, end1 ), arg2, tenv, need, have, open, 850 noWiden(), symtab )) { 851 break; 852 853 } 854 else return; 855 } 856 857 // allow qualifiers of pointer and reference base to become more specific 858 if (auto ref1 = dynamic_cast<const ast::ReferenceType *> (arg1)) { 859 if (auto ref2 = dynamic_cast<const ast::ReferenceType *> (arg2)) { 860 ast::ptr<ast::Type> base1 = ref1->base; 861 ast::ptr<ast::Type> base2 = ref2->base; 862 863 // xxx - assume LHS is always the target type 864 865 if ( ! ((widen.second && ref2->qualifiers.is_mutex) 866 || (ref1->qualifiers.is_mutex == ref2->qualifiers.is_mutex ))) return; 867 868 if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) { 869 870 reset_qualifiers(base1); 871 reset_qualifiers(base2); 872 873 if ( ! unifyExact( 874 base1, base2, tenv, need, have, open, noWiden(), symtab ) 875 ) return; 876 } 877 } 878 else return; 879 } 880 else if (auto ptr1 = dynamic_cast<const ast::PointerType *> (arg1)) { 881 if (auto ptr2 = dynamic_cast<const ast::PointerType *> (arg2)) { 882 ast::ptr<ast::Type> base1 = ptr1->base; 883 ast::ptr<ast::Type> base2 = ptr2->base; 884 885 // xxx - assume LHS is always the target type 886 // a function accepting const can always be called by non-const arg 887 888 if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) { 889 890 reset_qualifiers(base1); 891 reset_qualifiers(base2); 892 893 if ( ! unifyExact( 894 base1, base2, tenv, need, have, open, noWiden(), symtab ) 895 ) return; 896 } 897 } 898 else return; 899 900 } 901 else if (! unifyExact( 902 arg1, arg2, tenv, need, have, open, noWiden(), symtab )) return; 903 904 ++crnt1; ++crnt2; 905 } 906 if ( crnt1 != end1 ) { 907 // try unifying empty tuple with ttype 908 const ast::Type * t1 = *crnt1; 909 if (! Tuples::isTtype( t1 ) ) return; 910 if (! unifyExact( 911 t1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open, 912 noWiden(), symtab )) return; 913 } else if ( crnt2 != end2 ) { 914 // try unifying empty tuple with ttype 915 const ast::Type * t2 = *crnt2; 916 if (! Tuples::isTtype( t2 ) ) return; 917 if (! unifyExact( 918 tupleFromTypes( crnt1, end1 ), t2, tenv, need, have, open, 919 noWiden(), symtab )) return; 920 } 921 if ((f1->returns.size() == 0 && f2->returns.size() == 0) 922 || (f1->returns.size() == 1 && f2->returns.size() == 1 && unifyExact(f1->returns[0], f2->returns[0], tenv, need, have, open, noWiden(), symtab))) { 923 result = pointer; 924 925 for (auto & assn : f1->assertions) { 926 auto i = need.find(assn); 927 if (i != need.end()) i->second.isUsed = true; 928 auto j = have.find(assn); 929 if (j != have.end()) j->second.isUsed = true; 930 } 931 932 for (auto & assn : f2->assertions) { 933 auto i = need.find(assn); 934 if (i != need.end()) i->second.isUsed = true; 935 auto j = have.find(assn); 936 if (j != have.end()) j->second.isUsed = true; 937 } 938 939 } 940 } // if ftype 941 805 942 } 806 943 } else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) { … … 839 976 reset_qualifiers( t2 ); 840 977 841 ast::AssertionSet have, need;842 978 ast::OpenVarSet newOpen{ open }; 843 979 if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) { … … 857 993 // xxx - does unifying a ref with typed enumInst makes sense? 858 994 if (!dynamic_cast<const ast::EnumInstType *>(type2)) 859 result = commonType( type2, ref, widen, symtab, tenv, open);995 result = commonType( type2, ref, tenv, need, have, open, widen, symtab ); 860 996 } 861 997 } … … 877 1013 // xxx - is this already handled by unify? 878 1014 if (!dynamic_cast<const ast::EnumInstType *>(type2)) 879 result = commonType( type2, enumInst, widen, symtab, tenv, open);1015 result = commonType( type2, enumInst, tenv, need, have, open, widen, symtab); 880 1016 } 881 1017 … … 895 1031 reset_qualifiers( t2 ); 896 1032 897 ast::AssertionSet have, need;898 1033 ast::OpenVarSet newOpen{ open }; 899 1034 if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) { … … 999 1134 ast::ptr< ast::Type > commonType( 1000 1135 const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2, 1001 WidenMode widen, const ast::SymbolTable & symtab, ast::TypeEnvironment & env,1002 const ast::OpenVarSet & open 1136 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 1137 const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab 1003 1138 ) { 1004 1139 unsigned depth1 = type1->referenceDepth(); … … 1036 1171 } 1037 1172 // otherwise both are reference types of the same depth and this is handled by the visitor 1038 ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open };1173 ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open, need, have }; 1039 1174 type1->accept( visitor ); 1040 1175 ast::ptr< ast::Type > result = visitor.core.result; … … 1047 1182 if ( type->base ) { 1048 1183 ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers; 1049 ast::AssertionSet have, need;1050 1184 ast::OpenVarSet newOpen{ open }; 1051 1185 -
src/ResolvExpr/ConversionCost.cc
r4520b77e ra065f1f 22 22 #include "ResolvExpr/Cost.h" // for Cost 23 23 #include "ResolvExpr/TypeEnvironment.h" // for EqvClass, TypeEnvironment 24 #include "ResolvExpr/Unify.h" 24 25 #include "SymTab/Indexer.h" // for Indexer 25 26 #include "SynTree/Declaration.h" // for TypeDecl, NamedTypeDecl 26 27 #include "SynTree/Type.h" // for Type, BasicType, TypeInstType 27 28 #include "typeops.h" // for typesCompatibleIgnoreQualifiers 29 28 30 29 31 namespace ResolvExpr { … … 657 659 cost = Cost::safe; 658 660 } 659 } else { 661 } 662 /* 663 else if ( const ast::FunctionType * dstFunc = dstAsPtr->base.as<ast::FunctionType>()) { 664 if (const ast::FunctionType * srcFunc = pointerType->base.as<ast::FunctionType>()) { 665 if (dstFunc->params.empty() && dstFunc->isVarArgs ) { 666 cost = Cost::unsafe; // assign any function to variadic fptr 667 } 668 } 669 else { 670 ast::AssertionSet need, have; // unused 671 ast::OpenVarSet open; 672 env.extractOpenVars(open); 673 ast::TypeEnvironment tenv = env; 674 if ( unify(dstAsPtr->base, pointerType->base, tenv, need, have, open, symtab) ) { 675 cost = Cost::safe; 676 } 677 } 678 // else infinity 679 } 680 */ 681 else { 660 682 int assignResult = ptrsAssignable( pointerType->base, dstAsPtr->base, env ); 661 683 if ( 0 < assignResult && tq1 <= tq2 ) { -
src/ResolvExpr/SatisfyAssertions.cpp
r4520b77e ra065f1f 36 36 #include "AST/SymbolTable.hpp" 37 37 #include "AST/TypeEnvironment.hpp" 38 #include "FindOpenVars.h" 38 39 #include "Common/FilterCombos.h" 39 40 #include "Common/Indenter.h" … … 161 162 162 163 /// Satisfy a single assertion 163 bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat 164 bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool allowConversion = false, bool skipUnbound = false) { 164 165 // skip unused assertions 165 166 if ( ! assn.second.isUsed ) return true; … … 180 181 if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer; 181 182 else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams); 183 else if (skipUnbound) return false; 182 184 183 185 candidates = sat.symtab.specialLookupId(kind, otypeKey); … … 205 207 206 208 // only keep candidates which unify 207 if ( unify( toType, adjType, newEnv, newNeed, have, newOpen, sat.symtab ) ) { 208 // set up binding slot for recursive assertions 209 ast::UniqueId crntResnSlot = 0; 210 if ( ! newNeed.empty() ) { 211 crntResnSlot = ++globalResnSlot; 212 for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; } 213 } 214 215 matches.emplace_back( 216 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ), 217 std::move( newOpen ), crntResnSlot ); 209 210 ast::OpenVarSet closed; 211 findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed ); 212 findOpenVars( adjType, newOpen, closed, newNeed, have, FirstOpen ); 213 if ( allowConversion ) { 214 if ( auto c = commonType( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true}, sat.symtab ) ) { 215 // set up binding slot for recursive assertions 216 ast::UniqueId crntResnSlot = 0; 217 if ( ! newNeed.empty() ) { 218 crntResnSlot = ++globalResnSlot; 219 for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; } 220 } 221 222 matches.emplace_back( 223 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ), 224 std::move( newOpen ), crntResnSlot ); 225 } 226 } 227 else { 228 if ( unifyExact( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true}, sat.symtab ) ) { 229 // set up binding slot for recursive assertions 230 ast::UniqueId crntResnSlot = 0; 231 if ( ! newNeed.empty() ) { 232 crntResnSlot = ++globalResnSlot; 233 for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; } 234 } 235 236 matches.emplace_back( 237 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ), 238 std::move( newOpen ), crntResnSlot ); 239 } 218 240 } 219 241 } … … 413 435 // for each current mutually-compatible set of assertions 414 436 for ( SatState & sat : sats ) { 437 bool allowConversion = false; 415 438 // stop this branch if a better option is already found 416 439 auto it = thresholds.find( pruneKey( *sat.cand ) ); … … 418 441 419 442 // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen. 443 420 444 for (unsigned resetCount = 0; ; ++resetCount) { 421 445 ast::AssertionList next; … … 424 448 for ( auto & assn : sat.need ) { 425 449 // fail early if any assertion is not satisfiable 426 if ( ! satisfyAssertion( assn, sat ) ) {450 if ( ! satisfyAssertion( assn, sat, allowConversion, !next.empty() ) ) { 427 451 next.emplace_back(assn); 428 452 // goto nextSat; … … 433 457 // fail if nothing resolves 434 458 else if (next.size() == sat.need.size()) { 435 Indenter tabs{ 3 }; 436 std::ostringstream ss; 437 ss << tabs << "Unsatisfiable alternative:\n"; 438 print( ss, *sat.cand, ++tabs ); 439 ss << (tabs-1) << "Could not satisfy assertion:\n"; 440 ast::print( ss, next[0].first, tabs ); 441 442 errors.emplace_back( ss.str() ); 443 goto nextSat; 444 } 459 if (allowConversion) { 460 Indenter tabs{ 3 }; 461 std::ostringstream ss; 462 ss << tabs << "Unsatisfiable alternative:\n"; 463 print( ss, *sat.cand, ++tabs ); 464 ss << (tabs-1) << "Could not satisfy assertion:\n"; 465 ast::print( ss, next[0].first, tabs ); 466 467 errors.emplace_back( ss.str() ); 468 goto nextSat; 469 } 470 471 else { 472 allowConversion = true; 473 continue; 474 } 475 } 476 allowConversion = false; 445 477 sat.need = std::move(next); 446 478 } -
src/ResolvExpr/Unify.cc
r4520b77e ra065f1f 707 707 } 708 708 709 namespace { 710 /// Replaces ttype variables with their bound types. 711 /// If this isn't done when satifying ttype assertions, then argument lists can have 712 /// different size and structure when they should be compatible. 713 struct TtypeExpander_new : public ast::WithShortCircuiting, public ast::PureVisitor { 714 ast::TypeEnvironment & tenv; 715 716 TtypeExpander_new( ast::TypeEnvironment & env ) : tenv( env ) {} 717 718 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 719 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) { 720 // expand ttype parameter into its actual type 721 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) { 722 return clz->bound; 723 } 724 } 725 return typeInst; 726 } 727 }; 728 } 729 730 std::vector< ast::ptr< ast::Type > > flattenList( 731 const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env 732 ) { 733 std::vector< ast::ptr< ast::Type > > dst; 734 dst.reserve( src.size() ); 735 for ( const auto & d : src ) { 736 ast::Pass<TtypeExpander_new> expander{ env }; 737 // TtypeExpander pass is impure (may mutate nodes in place) 738 // need to make nodes shared to prevent accidental mutation 739 ast::ptr<ast::Type> dc = d->accept(expander); 740 auto types = flatten( dc ); 741 for ( ast::ptr< ast::Type > & t : types ) { 742 // outermost const, volatile, _Atomic qualifiers in parameters should not play 743 // a role in the unification of function types, since they do not determine 744 // whether a function is callable. 745 // NOTE: **must** consider at least mutex qualifier, since functions can be 746 // overloaded on outermost mutex and a mutex function has different 747 // requirements than a non-mutex function 748 remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic ); 749 dst.emplace_back( t ); 750 } 751 } 752 return dst; 753 } 754 709 755 class Unify_new final : public ast::WithShortCircuiting { 710 756 const ast::Type * type2; … … 778 824 779 825 private: 780 /// Replaces ttype variables with their bound types.781 /// If this isn't done when satifying ttype assertions, then argument lists can have782 /// different size and structure when they should be compatible.783 struct TtypeExpander_new : public ast::WithShortCircuiting, public ast::PureVisitor {784 ast::TypeEnvironment & tenv;785 786 TtypeExpander_new( ast::TypeEnvironment & env ) : tenv( env ) {}787 788 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {789 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {790 // expand ttype parameter into its actual type791 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {792 return clz->bound;793 }794 }795 return typeInst;796 }797 };798 799 /// returns flattened version of `src`800 static std::vector< ast::ptr< ast::Type > > flattenList(801 const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env802 ) {803 std::vector< ast::ptr< ast::Type > > dst;804 dst.reserve( src.size() );805 for ( const auto & d : src ) {806 ast::Pass<TtypeExpander_new> expander{ env };807 // TtypeExpander pass is impure (may mutate nodes in place)808 // need to make nodes shared to prevent accidental mutation809 ast::ptr<ast::Type> dc = d->accept(expander);810 auto types = flatten( dc );811 for ( ast::ptr< ast::Type > & t : types ) {812 // outermost const, volatile, _Atomic qualifiers in parameters should not play813 // a role in the unification of function types, since they do not determine814 // whether a function is callable.815 // NOTE: **must** consider at least mutex qualifier, since functions can be816 // overloaded on outermost mutex and a mutex function has different817 // requirements than a non-mutex function818 remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );819 dst.emplace_back( t );820 }821 }822 return dst;823 }824 825 /// Creates a tuple type based on a list of DeclWithType826 template< typename Iter >827 static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {828 std::vector< ast::ptr< ast::Type > > types;829 while ( crnt != end ) {830 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure831 // that this results in a flat tuple832 flatten( *crnt, types );833 834 ++crnt;835 }836 837 return new ast::TupleType{ std::move(types) };838 }839 826 840 827 template< typename Iter > … … 1048 1035 private: 1049 1036 /// Creates a tuple type based on a list of Type 1050 static const ast::Type * tupleFromTypes( 1051 const std::vector< ast::ptr< ast::Type > > & tys 1052 ) { 1053 std::vector< ast::ptr< ast::Type > > out; 1054 for ( const ast::Type * ty : tys ) { 1055 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 1056 // that this results in a flat tuple 1057 flatten( ty, out ); 1058 } 1059 1060 return new ast::TupleType{ std::move(out) }; 1061 } 1037 1062 1038 1063 1039 static bool unifyList( … … 1231 1207 } 1232 1208 1233 } else if ( ( common = commonType( t1, t2, widen, symtab, env, open ))) {1209 } else if ( common = commonType( t1, t2, env, need, have, open, widen, symtab )) { 1234 1210 // no exact unification, but common type 1235 1211 auto c = shallowCopy(common.get()); -
src/ResolvExpr/typeops.h
r4520b77e ra065f1f 138 138 Type * commonType( Type * type1, Type * type2, bool widenFirst, bool widenSecond, const SymTab::Indexer & indexer, TypeEnvironment & env, const OpenVarSet & openVars ); 139 139 ast::ptr< ast::Type > commonType( 140 const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2, WidenMode widen, 141 const ast::SymbolTable & symtab, ast::TypeEnvironment & env, const ast::OpenVarSet & open ); 140 const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2, 141 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 142 const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab 143 ); 144 // in Unify.cc 145 std::vector< ast::ptr< ast::Type > > flattenList( 146 const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env 147 ); 142 148 143 149 // in PolyCost.cc … … 181 187 182 188 /// flatten tuple type into existing list of types 183 staticinline void flatten(189 inline void flatten( 184 190 const ast::Type * type, std::vector< ast::ptr< ast::Type > > & out 185 191 ) { … … 194 200 195 201 /// flatten tuple type into list of types 196 staticinline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) {202 inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) { 197 203 std::vector< ast::ptr< ast::Type > > out; 198 204 out.reserve( type->size() ); … … 200 206 return out; 201 207 } 208 209 template< typename Iter > 210 const ast::Type * tupleFromTypes( Iter crnt, Iter end ) { 211 std::vector< ast::ptr< ast::Type > > types; 212 while ( crnt != end ) { 213 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 214 // that this results in a flat tuple 215 flatten( *crnt, types ); 216 217 ++crnt; 218 } 219 220 221 return new ast::TupleType{ std::move(types) }; 222 } 223 224 inline const ast::Type * tupleFromTypes( 225 const std::vector< ast::ptr< ast::Type > > & tys 226 ) { 227 return tupleFromTypes( tys.begin(), tys.end() ); 228 } 229 230 202 231 203 232 // in TypeEnvironment.cc -
tests/concurrent/.expect/ctor-check.txt
r4520b77e ra065f1f 2 2 ?{}: function 3 3 ... with parameters 4 this: lvaluereference to instance of struct Empty with body4 this: mutex reference to instance of struct Empty with body 5 5 ... returning nothing 6 6 with body
Note: See TracChangeset
for help on using the changeset viewer.