├── .gitignore ├── LICENSE ├── README.md ├── examples ├── Callbacks │ └── Callbacks.ino ├── MultipleBlinks │ └── MultipleBlinks.ino ├── blinkbuttonandweb │ ├── blinkbuttonandweb.ino │ ├── blinkbuttonandweb.sln │ ├── blinkbuttonandweb.vcxproj │ └── blinkbuttonandweb.vcxproj.filters ├── mutex │ └── mutex.ino └── portable │ └── portable.cpp ├── library.json ├── library.properties └── src ├── BasicCoopTask.cpp ├── BasicCoopTask.h ├── CoopMutex.h ├── CoopSemaphore.cpp ├── CoopSemaphore.h ├── CoopTask.h ├── CoopTaskBase.cpp └── CoopTaskBase.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #Ignore thumbnails created by Windows 3 | Thumbs.db 4 | #Ignore files built by Visual Studio 5 | *.obj 6 | *.exe 7 | *.pdb 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.cache 20 | *.ilk 21 | *.log 22 | [Bb]in 23 | [Dd]ebug*/ 24 | *.lib 25 | *.sbr 26 | obj/ 27 | [Rr]elease*/ 28 | _ReSharper*/ 29 | [Tt]est[Rr]esult* 30 | .vs/ 31 | #Nuget packages folder 32 | packages/ 33 | __vm/ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoopTask 2 | 3 | An all-C++ implementation of a cooperative multitasking layer for ESP8266/ESP32, 4 | Arduino boards, Linux, and Windows x86 and x86_64 5 | 6 | During regular development it's built and tested on the ESP MCUs and 7 | Arduino Pro/Pro Mini. 8 | 9 | Tasks in this scheduler are stackful coroutines. They act almost the same as 10 | the main ``setup()``/``loop()`` code in Arduino sketches, but there can be many of them 11 | simultaneously on the same device. It's even powerful enough to run the 12 | ESP8266 and ESP32 WebServer in a CoopTask. 13 | 14 | Use the normal global delay() function to suspend execution of a task for the 15 | given number of milliseconds, use yield() to give up the CPU, both return after 16 | other cooperative tasks have run awhile. 17 | 18 | A simple blink task can be written just like this: 19 | 20 | ``` 21 | #include 22 | 23 | int loopBlink() 24 | { 25 | // like setup(): 26 | pinMode(LED_BUILTIN, OUTPUT); 27 | 28 | // like loop(): 29 | for (;;) 30 | { 31 | digitalWrite(LED_BUILTIN, LOW); 32 | delay(2000); 33 | digitalWrite(LED_BUILTIN, HIGH); 34 | delay(3000); 35 | } 36 | 37 | // tasks can return or exit() and leave an exit code 38 | return 0; 39 | } 40 | 41 | CoopTask<>* taskBlink; 42 | 43 | void setup() 44 | { 45 | Serial.begin(115200); 46 | delay(500); 47 | 48 | #if defined(ESP8266) || defined(ESP32) 49 | taskBlink = createCoopTask(F("Blink"), loopBlink, 0x240); 50 | #else 51 | taskBlink = createCoopTask(F("Blink"), loopBlink, 0x40); 52 | #endif 53 | if (!taskBlink) Serial.println("CoopTask Blink not created"); 54 | } 55 | 56 | void loop() 57 | { 58 | runCoopTasks(); 59 | } 60 | ``` 61 | 62 | The ``runCoopTasks()`` scheduling helper has two optional callback arguments. 63 | 64 | The first, ``reaper``, gets called each time a task exits. Retrieving the 65 | exit code or deleting the CoopTask object would typically be performed in a 66 | task reaper function. 67 | 68 | The second callback, ``onDelay``, is called after each scheduling rountrip with the 69 | total minimum delay (can be zero) of all managed tasks. A use scenario for this 70 | is to put the MCU into a power saving sleep mode for the given duration. 71 | 72 | ## Using Arduino or Linux default loop stack space for CoopTask 73 | Given that CoopTasks are scheduled from the Arduino default ``loop()`` or the 74 | ``main()`` function on Linux, any code in these functions is non-cooperative. 75 | This is great for incompatible sketches or libraries, but otherwise puts the 76 | memory of that main stack to waste. It is therefore good practice to allocate 77 | the local stack for a single, infinitely running, CoopTask on the main stack. 78 | Reserve enough stack to remain for ``loop()`` internals. In its most simple 79 | form, borrowing from the example above, where taskBlink meets the requirement 80 | of never returning, a CoopTask that uses the default stack space is created 81 | like so: 82 | 83 | ``` 84 | #if defined(ESP8266) || defined(ESP32) 85 | taskBlink = createCoopTask>( 86 | F("Blink"), loopBlink, 0x240); 87 | #else 88 | taskBlink = createCoopTask>( 89 | F("Blink"), loopBlink, 0x40); 90 | #endif 91 | ``` 92 | 93 | ## ESP8266 Core For Arduino specifics 94 | ESP8266 Core For Arduino release 2.6.0 and later include all support for this 95 | release of CoopTask. 96 | 97 | ## Arduino-ESP32 specifics 98 | The ESP32 runs the Arduino API on top of the FreeRTOS real-time operating system. 99 | This OS has all the capabilities for real-time programming and offers prioritized, 100 | preemptive multitasking. The purpose of CoopTask on the other hand is to take 101 | the complexity out of multi-threaded/tasked programming, and offers a cooperative 102 | multi-tasking scheme instead. 103 | 104 | Arduino-ESP32 has the necessary support for CoopTask beginning with 105 | commit-ish c2b3f2d, dated Oct 4 2019, in Github master branch post release 1.4.0. 106 | 107 | For Arduino sketches, and the libraries used in these, that never use the global 108 | Arduino ``delay()``, don't make use of FreeRTOS ``vTaskDelay()``, and implement 109 | delays only ever using the CoopTask metaphor ``CoopTaskBase::delay()``, CoopTask 110 | would not require anything specific for the ESP32. 111 | 112 | If the convenient Arduino ``delay()`` does get used, or there is any chance that 113 | the FreeRTOS ``vTaskDelay()`` gets used, though, on the ESP32 it is necessary to 114 | prevent unsolicited preemptive concurrency and control the CPU time for the 115 | idle task. 116 | 117 | This is being taken care of by CoopTask when using the ``runCoopTasks()`` 118 | scheduling helper in the Sketch ``loop()`` function. 119 | -------------------------------------------------------------------------------- /examples/Callbacks/Callbacks.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined(ARDUINO_attiny) 8 | #define LED_BUILTIN 1 9 | 10 | struct DummySerial { 11 | void print(const __FlashStringHelper* s = nullptr) {} 12 | void println(const __FlashStringHelper* s = nullptr) {} 13 | void println(long unsigned int) {} 14 | void flush() {} 15 | }; 16 | DummySerial Serial; 17 | #endif 18 | 19 | CoopTask* blinkTask = nullptr; 20 | CoopTask* switchTask = nullptr; 21 | 22 | void blinkFunction() 23 | { 24 | pinMode(LED_BUILTIN, OUTPUT); 25 | digitalWrite(LED_BUILTIN, HIGH); 26 | for (;;) 27 | { 28 | yield(); // A 29 | digitalWrite(LED_BUILTIN, LOW); 30 | delay(5000); // B 31 | digitalWrite(LED_BUILTIN, HIGH); 32 | CoopTask::sleep(); // D 33 | } 34 | } 35 | 36 | void switchFunction() 37 | { 38 | for (;;) 39 | { 40 | yield(); // A 41 | Serial.println(F("Switch on")); 42 | delay(100); // B 43 | Serial.println(F("Switch off")); 44 | CoopTask::sleep(); // C 45 | } 46 | } 47 | 48 | bool delayCb(uint32_t ms) 49 | { 50 | Serial.print(F("delayDb, ms = ")); 51 | Serial.println(ms); 52 | delay(ms); 53 | return true; 54 | } 55 | 56 | bool sleepCb() 57 | { 58 | Serial.println(F("sleepCb")); 59 | Serial.flush(); 60 | delay(10000); 61 | if (blinkTask) blinkTask->wakeup(); 62 | if (switchTask) switchTask->wakeup(); 63 | return true; 64 | } 65 | 66 | void setup() 67 | { 68 | #if !defined(ARDUINO_attiny) 69 | Serial.begin(74880); 70 | while (!Serial); 71 | delay(100); 72 | Serial.println(); 73 | Serial.println(F("runTasks callback test")); 74 | #endif 75 | 76 | runCoopTasks(nullptr, delayCb, sleepCb); 77 | Serial.println(F("no tasks yet, sleepCb()?")); 78 | 79 | blinkTask = new CoopTask(F("blink"), blinkFunction); 80 | switchTask = new CoopTask(F("switch"), switchFunction); 81 | blinkTask->scheduleTask(); 82 | switchTask->scheduleTask(); 83 | } 84 | 85 | // Add the main program code into the continuous loop() function 86 | void loop() 87 | { 88 | runCoopTasks(nullptr, delayCb, sleepCb); 89 | Serial.println(F("A - both tasks yielded, no Cb?")); 90 | yield(); 91 | runCoopTasks(nullptr, delayCb, sleepCb); 92 | Serial.println(F("B - both tasks delayed, delayCb(100)?")); 93 | yield(); 94 | runCoopTasks(nullptr, delayCb, sleepCb); 95 | Serial.println(F("C - blink task delayed, switch task sleeping, delayCb(4900)?")); 96 | yield(); 97 | runCoopTasks(nullptr, delayCb, sleepCb); 98 | Serial.println(F("D - both tasks sleeping, sleepCb()?")); 99 | } 100 | -------------------------------------------------------------------------------- /examples/MultipleBlinks/MultipleBlinks.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Multiple Blinks 3 | 4 | Ported to CoopTask from the version that 5 | demonstrates the use of the Scheduler library for the Arduino Due. 6 | CoopTask works on Arduino AVR (including ATtiny), ESP8266, ESP32, ARM Linux and PC OSs. 7 | 8 | created 8 Oct 2012 9 | by Cristian Maglie 10 | Modified by 11 | Scott Fitzgerald 19 Oct 2012 12 | Ported to CoopTask by 13 | Dirk O. Kaar 22 Dec 2019 14 | 15 | This example code is in the public domain 16 | 17 | http://www.arduino.cc/en/Tutorial/MultipleBlinks 18 | */ 19 | 20 | // Include CoopTask since we want to manage multiple tasks. 21 | #include 22 | #include 23 | 24 | // ATtiny85 max. working memory utilization, AVR 1.8.2 toolchain: 25 | // "Minimum Memory Usage: 357 bytes (70% of a 512 byte maximum)" 26 | 27 | #if defined(ARDUINO_attiny) 28 | #define LED_BUILTIN 1 29 | #endif 30 | 31 | #if defined(ARDUINO_AVR_MICRO) 32 | #define STACKSIZE_8BIT 92 33 | #else 34 | #define STACKSIZE_8BIT 40 35 | #endif 36 | 37 | CoopSemaphore taskSema(1, 1); 38 | int taskToken = 1; 39 | 40 | // Task no.1: blink LED with 1 second delay. 41 | void loop1() { 42 | for (;;) // explicitly run forever without returning 43 | { 44 | taskSema.wait(); 45 | if (1 != taskToken) 46 | { 47 | taskSema.post(); 48 | yield(); 49 | continue; 50 | } 51 | for (int i = 0; i < 3; ++i) 52 | { 53 | digitalWrite(LED_BUILTIN, HIGH); 54 | 55 | // IMPORTANT: 56 | // When multiple tasks are running 'delay' passes control to 57 | // other tasks while waiting and guarantees they get executed. 58 | delay(1000); 59 | 60 | digitalWrite(LED_BUILTIN, LOW); 61 | delay(1000); 62 | } 63 | taskToken = 2; 64 | taskSema.post(); 65 | } 66 | } 67 | 68 | // Task no.2: blink LED with 0.25 second delay. 69 | void loop2() { 70 | for (;;) // explicitly run forever without returning 71 | { 72 | taskSema.wait(); 73 | if (2 != taskToken) 74 | { 75 | taskSema.post(); 76 | yield(); 77 | continue; 78 | } 79 | for (int i = 0; i < 6; ++i) 80 | { 81 | digitalWrite(LED_BUILTIN, HIGH); 82 | 83 | // IMPORTANT: 84 | // When multiple tasks are running 'delay' passes control to 85 | // other tasks while waiting and guarantees they get executed. 86 | delay(250); 87 | 88 | digitalWrite(LED_BUILTIN, LOW); 89 | delay(250); 90 | } 91 | taskToken = 3; 92 | taskSema.post(); 93 | } 94 | } 95 | 96 | // Task no.3: blink LED with 0.05 second delay. 97 | void loop3() { 98 | for (;;) // explicitly run forever without returning 99 | { 100 | taskSema.wait(); 101 | if (3 != taskToken) 102 | { 103 | taskSema.post(); 104 | yield(); 105 | continue; 106 | } 107 | for (int i = 0; i < 6; ++i) 108 | { 109 | digitalWrite(LED_BUILTIN, HIGH); 110 | 111 | // IMPORTANT: 112 | // When multiple tasks are running 'delay' passes control to 113 | // other tasks while waiting and guarantees they get executed. 114 | delay(50); 115 | 116 | digitalWrite(LED_BUILTIN, LOW); 117 | delay(50); 118 | } 119 | taskToken = 1; 120 | taskSema.post(); 121 | } 122 | } 123 | 124 | BasicCoopTask= 4 ? 800 : STACKSIZE_8BIT>> task1("l1", loop1); 125 | BasicCoopTask= 4 ? 800 : STACKSIZE_8BIT>> task2("l2", loop2); 126 | BasicCoopTask= 4 ? 800 : STACKSIZE_8BIT>> task3("l3", loop3, sizeof(unsigned) >= 4 ? 800 : STACKSIZE_8BIT); 127 | 128 | void setup() { 129 | //Serial.begin(9600); 130 | // Setup the 3 pins as OUTPUT 131 | pinMode(LED_BUILTIN, OUTPUT); 132 | 133 | // Add "loop1", "loop2" and "loop3" to CoopTask scheduling. 134 | // "loop" is always started by default, and is not under the control of CoopTask. 135 | task1.scheduleTask(); 136 | task2.scheduleTask(); 137 | task3.scheduleTask(); 138 | } 139 | 140 | void loop() { 141 | // loops forever by default 142 | runCoopTasks(); 143 | } 144 | -------------------------------------------------------------------------------- /examples/blinkbuttonandweb/blinkbuttonandweb.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if defined(ESP8266) 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | ESP8266WebServer server(80); 13 | #elif defined(ESP32) 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | WebServer server(80); 20 | #endif 21 | 22 | #if !defined(ESP8266) && !defined(ESP32) 23 | #define IRAM_ATTR 24 | #endif 25 | 26 | #if defined(ESP8266) 27 | constexpr auto LEDON = LOW; 28 | constexpr auto LEDOFF = HIGH; 29 | #else 30 | constexpr auto LEDON = HIGH; 31 | constexpr auto LEDOFF = LOW; 32 | #endif 33 | 34 | #if defined(ESP32) 35 | #define BUTTON1 17 36 | //#define BUTTON1 GPIO_NUM_27 37 | #elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) 38 | #define BUTTON1 D3 39 | #else 40 | #define BUTTON1 0 41 | #endif 42 | 43 | #define USE_BUILTIN_TASK_SCHEDULER 44 | 45 | // enter your WiFi configuration below 46 | static const char AP_SSID[] PROGMEM = "SSID"; // your router's SSID here 47 | static const char AP_PASS[] PROGMEM = "PSK"; // your router's password here 48 | static const char MDNSNAME[] PROGMEM = "esp"; // your device's MDNS host name 49 | 50 | CoopMutex serialMutex; 51 | 52 | CoopSemaphore blinkSema(0); 53 | 54 | #if defined(ESP8266) || defined(ESP32) 55 | class Button { 56 | protected: 57 | CoopSemaphore& reportSema; 58 | public: 59 | Button(uint8_t reqPin, CoopSemaphore& _reportSema) : reportSema(_reportSema), pushSema(0), PIN(reqPin) { 60 | pinMode(PIN, INPUT_PULLUP); 61 | attachInterruptArg(PIN, Button::buttonIsr_static, this, FALLING); 62 | }; 63 | ~Button() { 64 | detachInterrupt(PIN); 65 | } 66 | 67 | CoopSemaphore pushSema; 68 | 69 | void IRAM_ATTR buttonIsr() { 70 | numberKeyPresses += 1; 71 | pressed = true; 72 | pushSema.post(); 73 | reportSema.post(); 74 | } 75 | 76 | static void IRAM_ATTR buttonIsr_static(void* const self) { 77 | reinterpret_cast(self)->buttonIsr(); 78 | } 79 | 80 | unsigned testResetPressed() { 81 | if (pressed) { 82 | CoopMutexLock serialLock(serialMutex); 83 | Serial.printf_P(PSTR("Button on pin %u has been pressed %u times\n"), PIN, numberKeyPresses); 84 | pressed = false; 85 | } 86 | return numberKeyPresses; 87 | } 88 | 89 | private: 90 | const uint8_t PIN; 91 | volatile unsigned numberKeyPresses = 0; 92 | volatile bool pressed = false; 93 | }; 94 | #endif 95 | 96 | void loopBlink() noexcept 97 | { 98 | for (;;) 99 | { 100 | digitalWrite(LED_BUILTIN, LEDOFF); 101 | blinkSema.wait(1000); 102 | digitalWrite(LED_BUILTIN, LEDON); 103 | CoopTask<>::delay(4000); 104 | } 105 | } 106 | 107 | #if defined(ESP8266) || defined(ESP32) 108 | Button* button1; 109 | 110 | void loopButton() noexcept 111 | { 112 | int count = 0; 113 | for (;;) 114 | { 115 | if (!button1->pushSema.wait()) 116 | { 117 | CoopMutexLock serialLock(serialMutex); 118 | Serial.println(F("loopButton: wait failed")); 119 | yield(); 120 | continue; 121 | } 122 | else 123 | { 124 | ++count; 125 | } 126 | { 127 | CoopMutexLock serialLock(serialMutex); 128 | Serial.print(F("loopButton: count = ")); 129 | Serial.println(count); 130 | } 131 | if (nullptr != button1 && 8000 < button1->testResetPressed()) { 132 | delete button1; 133 | button1 = nullptr; 134 | CoopTask<>::exit(); 135 | } 136 | yield(); 137 | } 138 | } 139 | 140 | void handleRoot() { 141 | server.send(200, F("text/plain"), F("hello from esp8266!")); 142 | } 143 | 144 | void handleNotFound() { 145 | String message = F("File Not Found\n\n"); 146 | message += F("URI: "); 147 | message += server.uri(); 148 | message += F("\nMethod: "); 149 | message += (server.method() == HTTP_GET) ? F("GET") : F("POST"); 150 | message += F("\nArguments: "); 151 | message += server.args(); 152 | message += "\n"; 153 | for (uint8_t i = 0; i < server.args(); i++) { 154 | message += ' ' + server.argName(i) + F(": ") + server.arg(i) + '\n'; 155 | } 156 | server.send(404, F("text/plain"), message); 157 | } 158 | #endif 159 | 160 | #if defined(ESP8266) || defined(ESP32) 161 | CoopTask* taskButton; 162 | #endif 163 | CoopTask>* taskBlink; 164 | CoopTask* taskText; 165 | CoopTask* taskReport0; 166 | CoopTask* taskReport1; 167 | CoopTask* taskReport2; 168 | #if defined(ESP8266) || defined(ESP32) 169 | CoopTask* taskReport3; 170 | CoopTask* taskReport4; 171 | CoopTask* taskWeb; 172 | #endif 173 | CoopSemaphore reportSema(0); 174 | 175 | void printStackReport(CoopTaskBase* task) 176 | { 177 | if (!task) return; 178 | Serial.print(task->name().c_str()); 179 | Serial.print(F(" free stack = ")); 180 | Serial.println(task->getFreeStack()); 181 | } 182 | 183 | uint32_t iterations = 0; 184 | uint32_t start; 185 | 186 | // to demonstrate that yield and delay work in subroutines 187 | void printReport() 188 | { 189 | //CoopTask<>::delayMicroseconds(4000000); 190 | Serial.print(F("cycle period/us = ")); 191 | if (iterations) 192 | { 193 | Serial.println(1.0F * (micros() - start) / iterations); 194 | } 195 | else 196 | { 197 | Serial.println(F("N/A")); 198 | } 199 | #if defined(ESP8266) || defined(ESP32) 200 | printStackReport(taskButton); 201 | #endif 202 | printStackReport(taskBlink); 203 | printStackReport(taskText); 204 | printStackReport(CoopTask<>::self()); 205 | #if defined(ESP8266) || defined(ESP32) 206 | printStackReport(taskWeb); 207 | #endif 208 | 209 | iterations = 0; 210 | }; 211 | 212 | class RAIITest 213 | { 214 | public: 215 | ~RAIITest() 216 | { 217 | CoopMutexLock serialLock(serialMutex); 218 | Serial.print(CoopTaskBase::self()->name()); 219 | Serial.println(F(" stack unwound, RAIITest object destructed")); 220 | } 221 | }; 222 | 223 | void setup() 224 | { 225 | #ifdef ESP8266 226 | Serial.begin(74880); 227 | #else 228 | Serial.begin(115200); 229 | #endif 230 | while (!Serial) {} 231 | delay(500); 232 | 233 | Serial.println(F("Scheduler test")); 234 | 235 | #if defined(ESP8266) || defined(ESP32) 236 | WiFi.mode(WIFI_STA); 237 | WiFi.begin(FPSTR(AP_SSID), FPSTR(AP_PASS)); 238 | 239 | // Wait for connection 240 | while (WiFi.status() != WL_CONNECTED) { 241 | delay(500); 242 | Serial.print('.'); 243 | } 244 | Serial.println(); 245 | Serial.print(F("IP address: ")); 246 | Serial.println(WiFi.localIP()); 247 | 248 | if (MDNS.begin(FPSTR(MDNSNAME))) { 249 | Serial.println(F("MDNS responder started")); 250 | } 251 | 252 | server.on(F("/"), handleRoot); 253 | 254 | server.on(F("/inline"), []() { 255 | server.send(200, F("text/plain"), F("this works as well")); 256 | }); 257 | 258 | server.onNotFound(handleNotFound); 259 | 260 | server.begin(); 261 | Serial.println(F("HTTP server started")); 262 | #endif 263 | 264 | 265 | #if defined(ESP8266) && defined(USE_BUILTIN_TASK_SCHEDULER) 266 | CoopTaskBase::useBuiltinScheduler(); 267 | #endif 268 | 269 | pinMode(LED_BUILTIN, OUTPUT); 270 | 271 | #if defined(ESP8266) || defined(ESP32) 272 | button1 = new Button(BUTTON1, reportSema); 273 | 274 | taskButton = new CoopTask(F("Button"), loopButton, 275 | #if defined(ESP8266) 276 | 0x700); 277 | #elif defined(ESP32) 278 | 0x940); 279 | #endif 280 | if (!*taskButton) Serial.printf_P(PSTR("CoopTask %s out of stack\n"), taskButton->name().c_str()); 281 | #endif 282 | 283 | taskBlink = new CoopTask>(F("Blink"), loopBlink, 284 | #if defined(ESP8266) 285 | 0x400); 286 | #elif defined(ESP32) 287 | 0x540); 288 | #else 289 | 0x40); 290 | #endif 291 | if (!*taskBlink) Serial.println(F("CoopTask Blink out of stack")); 292 | 293 | taskText = new CoopTask(F("Text"), []() -> unsigned 294 | { 295 | RAIITest raii; 296 | { 297 | CoopMutexLock serialLock(serialMutex); 298 | Serial.println(F("Task1 - A")); 299 | } 300 | yield(); 301 | { 302 | CoopMutexLock serialLock(serialMutex); 303 | Serial.println(F("Task1 - B")); 304 | } 305 | uint32_t start = millis(); 306 | CoopTask<>::delay(6000); 307 | { 308 | CoopMutexLock serialLock(serialMutex); 309 | Serial.print(F("!!!Task1 - C - ")); 310 | Serial.println(millis() - start); 311 | printStackReport(taskText); 312 | } 313 | #if !defined(ARDUINO) 314 | throw static_cast(41); 315 | #endif 316 | CoopMutexLock serialLock(serialMutex); 317 | Serial.print(F("exiting from task ")); 318 | Serial.println(CoopTaskBase::self()->name()); 319 | //CoopTask::exit(42); 320 | return 43; 321 | } 322 | #if defined(ESP8266) 323 | , 0x380); 324 | #elif defined(ESP32) 325 | , 0x4c0); 326 | #else 327 | , 0x70); 328 | #endif 329 | if (!*taskText) Serial.println(F("CoopTask Text out of stack")); 330 | 331 | auto reportFunc = []() noexcept 332 | { 333 | uint32_t count = 0; 334 | for (;;) { 335 | if (!reportSema.wait(120000)) 336 | { 337 | { 338 | CoopMutexLock serialLock(serialMutex); 339 | Serial.print(CoopTaskBase::self()->name().c_str()); 340 | Serial.println(F(": wait failed")); 341 | } 342 | yield(); 343 | continue; 344 | } 345 | { 346 | CoopMutexLock serialLock(serialMutex); 347 | Serial.print(CoopTask<>::self()->name()); 348 | Serial.print(F(" (")); 349 | Serial.print(++count); 350 | Serial.println(F("x)")); 351 | printReport(); 352 | } 353 | yield(); 354 | reportSema.setval(0); 355 | } 356 | }; 357 | taskReport0 = new CoopTask(F("Report0"), reportFunc 358 | #if defined(ESP8266) || defined(ESP32) 359 | , 0x600); 360 | #else 361 | , 0x70); 362 | #endif 363 | if (!*taskReport0) Serial.println(F("CoopTask Report out of stack")); 364 | taskReport1 = new CoopTask(F("Report1"), reportFunc 365 | #if defined(ESP8266) || defined(ESP32) 366 | , 0x600); 367 | #else 368 | , 0x70); 369 | #endif 370 | if (!*taskReport1) Serial.println(F("CoopTask Report out of stack")); 371 | taskReport2 = new CoopTask(F("Report2"), reportFunc 372 | #if defined(ESP8266) || defined(ESP32) 373 | , 0x600); 374 | #else 375 | , 0x70); 376 | #endif 377 | if (!*taskReport2) Serial.println(F("CoopTask Report out of stack")); 378 | 379 | #if defined(ESP8266) || defined(ESP32) 380 | taskReport3 = new CoopTask(F("Report3"), reportFunc 381 | , 0x600); 382 | if (!*taskReport3) Serial.println(F("CoopTask Report out of stack")); 383 | taskReport4 = new CoopTask(F("Report4"), reportFunc 384 | , 0x600); 385 | if (!*taskReport4) Serial.println(F("CoopTask Report out of stack")); 386 | 387 | taskWeb = new CoopTask(F("Web"), []() noexcept 388 | { 389 | for (;;) { 390 | server.handleClient(); 391 | #ifdef ESP8266 392 | MDNS.update(); 393 | #endif 394 | yield(); 395 | } 396 | }, 397 | #if defined(ESP8266) 398 | 0x800); 399 | #else 400 | 0xa00); 401 | #endif 402 | if (!*taskWeb) Serial.printf_P(PSTR("CoopTask %s out of stack\n"), taskWeb->name().c_str()); 403 | 404 | if (!taskButton->scheduleTask()) { Serial.printf_P(PSTR("Could not schedule task %s\n"), taskButton->name().c_str()); } 405 | if (!taskReport3->scheduleTask()) { Serial.printf_P(PSTR("Could not schedule task %s\n"), taskReport3->name().c_str()); } 406 | if (!taskReport4->scheduleTask()) { Serial.printf_P(PSTR("Could not schedule task %s\n"), taskReport4->name().c_str()); } 407 | if (!taskWeb->scheduleTask()) { Serial.printf_P(PSTR("Could not schedule task %s\n"), taskWeb->name().c_str()); } 408 | #endif 409 | 410 | if (!taskBlink->scheduleTask()) { Serial.print(F("Could not schedule task ")); Serial.println(taskBlink->name().c_str()); } 411 | if (!taskText->scheduleTask()) { Serial.print(F("Could not schedule task ")); Serial.println(taskText->name().c_str()); } 412 | if (!taskReport0->scheduleTask()) { Serial.print(F("Could not schedule task ")); Serial.println(taskReport0->name().c_str()); } 413 | if (!taskReport1->scheduleTask()) { Serial.print(F("Could not schedule task ")); Serial.println(taskReport1->name().c_str()); } 414 | if (!taskReport2->scheduleTask()) { Serial.print(F("Could not schedule task ")); Serial.println(taskReport2->name().c_str()); } 415 | 416 | #ifdef ESP32 417 | Serial.print(F("Loop free stack = ")); Serial.println(uxTaskGetStackHighWaterMark(NULL)); 418 | #endif 419 | } 420 | 421 | void taskReaper(const CoopTaskBase* const task) 422 | { 423 | if (task == taskText) 424 | { 425 | delete task; 426 | taskText = nullptr; 427 | } 428 | } 429 | 430 | void loop() 431 | { 432 | #if defined(ESP8266) && defined(USE_BUILTIN_TASK_SCHEDULER) 433 | if (taskText && !*taskText) 434 | { 435 | taskReaper(taskText); 436 | } 437 | #else 438 | runCoopTasks(taskReaper); 439 | #endif 440 | 441 | // taskReport sleeps on first run(), and after each report. 442 | // It resets iterations to 0 on each report. 443 | if (!iterations) start = micros(); 444 | ++iterations; 445 | #ifdef ESP32_FREERTOS 446 | if (iterations >= 50000) 447 | #else 448 | if (iterations >= 200000) 449 | #endif 450 | { 451 | reportSema.post(); 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /examples/blinkbuttonandweb/blinkbuttonandweb.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.33423.256 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "blinkbuttonandweb", "blinkbuttonandweb.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x86 = Debug|x86 11 | Release|x86 = Release|x86 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32 15 | {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32 16 | {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32 17 | {C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {A59244B3-73EA-4559-9718-E640DBB9E647} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /examples/blinkbuttonandweb/blinkbuttonandweb.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | Win32 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | 14 | {C5F80730-F44F-4478-BDAE-6634EFC2CA88} 15 | 16 | 17 | blinkbuttonandweb 18 | 19 | 20 | 21 | Application 22 | true 23 | MultiByte 24 | 25 | 26 | 27 | 28 | Application 29 | false 30 | true 31 | MultiByte 32 | 33 | 34 | 35 | 36 | Application 37 | true 38 | MultiByte 39 | 40 | 41 | 42 | 43 | Application 44 | false 45 | true 46 | MultiByte 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Level3 72 | MaxSpeed 73 | true 74 | true 75 | true 76 | $(ProjectDir)..\blinkbuttonandweb;$(ProjectDir)..\..\src;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\libraries\ESP8266WiFi\src;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\libraries\ESP8266WebServer\src;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\libraries\ESP8266mDNS\src;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\cores\esp8266;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\cores\esp8266\avr;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\cores\esp8266\libb64;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\cores\esp8266\spiffs;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\cores\esp8266\umm_malloc;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\variants\d1_mini;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\sdk\include;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\sdk\lwip2\include;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\sdk\libc\xtensa-lx106-elf\include;$(ProjectDir)..\..\..\..\..\..\DOCUME~1\Arduino\LIBRAR~1\CoopTask\examples\BLINKB~1;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\xtensa-lx106-elf\xtensa-lx106-elf\include\c++\4.8.2;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\xtensa-lx106-elf\xtensa-lx106-elf\include\c++\4.8.2\xtensa-lx106-elf;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\xtensa-lx106-elf\xtensa-lx106-elf\include;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\xtensa-lx106-elf\lib\gcc\xtensa-lx106-elf\4.8.2\include;$(ProjectDir)..\..\..\..\hardware\esp8266com\esp8266\tools\sdk\include;%(AdditionalIncludeDirectories) 77 | $(ProjectDir)__vm\.blinkbuttonandweb.vsarduino.h;%(ForcedIncludeFiles) 78 | __ESP8266_esp8266__;__ESP8266_ESP8266__;__ets__;ICACHE_FLASH;_GNU_SOURCE;ESP8266;MMU_IRAM_SIZE=0x8000;MMU_ICACHE_SIZE=0x8000;NONOSDK22x_190703=1;F_CPU=80000000L;LWIP_OPEN_SRC;TCP_MSS=536;LWIP_FEATURES=1;LWIP_IPV6=0;ARDUINO=108010;ARDUINO_ESP8266_WEMOS_D1MINIPRO;ARDUINO_ARCH_ESP8266;FLASHMODE_DIO;__cplusplus=201103L;_VMICRO_INTELLISENSE;%(PreprocessorDefinitions) 79 | 80 | 81 | true 82 | true 83 | true 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | true 91 | $(ProjectDir)..\blinkbuttonandweb;$(ProjectDir)..\..\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\libraries\WiFi\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\libraries\WebServer\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\libraries\ESPmDNS\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\cores\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\cores\esp32\apps;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\cores\esp32\libb64;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\variants\d1_mini32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\newlib\platform_include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\freertos\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\freertos\include\esp_additions\freertos;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\freertos\port\xtensa\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\freertos\include\esp_additions;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_hw_support\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_hw_support\include\soc;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_hw_support\include\soc\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_hw_support\port\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_hw_support\port\esp32\private_include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\heap\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\log\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\lwip\include\apps;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\lwip\include\apps\sntp;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\lwip\lwip\src\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\lwip\port\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\lwip\port\esp32\include\arch;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\soc\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\soc\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\soc\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\hal\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\hal\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\hal\platform_port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_rom\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_rom\include\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_rom\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_common\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_system\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_system\port\soc;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_system\port\public_compat;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\xtensa\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\xtensa\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\driver\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\driver\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_pm\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_ringbuf\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\efuse\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\efuse\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\vfs\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_wifi\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_event\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_netif\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_eth\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\tcpip_adapter\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_phy\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_phy\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_ipc\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\app_trace\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_timer\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\mbedtls\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\mbedtls\mbedtls\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\mbedtls\esp_crt_bundle\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\app_update\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\spi_flash\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bootloader_support\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\nvs_flash\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\pthread\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_gdbstub\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_gdbstub\xtensa;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_gdbstub\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\espcoredump\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\espcoredump\include\port\xtensa;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\wpa_supplicant\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\wpa_supplicant\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\wpa_supplicant\esp_supplicant\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\ieee802154\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\console;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\asio\asio\asio\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\asio\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\common\osi\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\include\esp32\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\common\api\include\api;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\common\btc\profile\esp\blufi\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\common\btc\profile\esp\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\host\bluedroid\api\include\api;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_common\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_common\tinycrypt\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_core;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_core\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_core\storage;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\btc\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_models\common\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_models\client\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\mesh_models\server\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\api\core\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\api\models\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\bt\esp_ble_mesh\api;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\cbor\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\unity\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\unity\unity\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\cmock\CMock\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\coap\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\coap\libcoap\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\nghttp\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\nghttp\nghttp2\lib\includes;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-tls;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-tls\esp-tls-crypto;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_adc_cal\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_hid\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\tcp_transport\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_http_client\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_http_server\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_https_ota\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_https_server\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_lcd\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_lcd\interface;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\protobuf-c\protobuf-c;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\protocomm\include\common;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\protocomm\include\security;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\protocomm\include\transports;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\mdns\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_local_ctrl\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\sdmmc\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_serial_slave_link\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_websocket_client\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\expat\expat\expat\lib;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\expat\port\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\wear_levelling\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\fatfs\diskio;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\fatfs\vfs;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\fatfs\src;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\freemodbus\common\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\idf_test\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\idf_test\include\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\jsmn\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\json\cJSON;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\libsodium\libsodium\src\libsodium\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\libsodium\port_include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\mqtt\esp-mqtt\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\openssl\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\perfmon\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\spiffs\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\ulp\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\wifi_provisioning\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\rmaker_common\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\json_parser\upstream\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\json_parser\upstream;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\json_generator\upstream;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_schedule\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_rainmaker\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\gpio_button\button\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\qrcode\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\ws2812_led;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_diagnostics\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\rtc_store\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_insights\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\dotprod\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\support\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\hann\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\blackman\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\blackman_harris\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\blackman_nuttall\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\nuttall\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\windows\flat_top\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\iir\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\fir\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\add\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\sub\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\mul\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\addc\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\mulc\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\math\sqrt\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\matrix\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\fft\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\dct\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\conv\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\common\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\kalman\ekf\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dsp\modules\kalman\ekf_imu13states\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp_littlefs\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\tool;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\typedef;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\image;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\math;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\nn;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\layer;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\detect;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-dl\include\model_zoo;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-sr\src\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-sr\esp-tts\esp_tts_chinese\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp-sr\include\esp32;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp32-camera\driver\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\esp32-camera\conversions\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\include\fb_gfx\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\esp32\dio_qspi\include;$(ProjectDir)..\..\..\..\hardware\espressif\esp32\tools\sdk\include;%(AdditionalIncludeDirectories) 92 | $(ProjectDir)__vm\.blinkbuttonandweb.vsarduino.h;%(ForcedIncludeFiles) 93 | true 94 | __ESP32_esp32__;__ESP32_ESP32__;HAVE_CONFIG_H;UNITY_INCLUDE_CONFIG_H;WITH_POSIX;_GNU_SOURCE;ESP_PLATFORM;_POSIX_READER_WRITER_LOCKS;F_CPU=240000000L;ARDUINO=108010;ARDUINO_D1_MINI32;ARDUINO_ARCH_ESP32;ARDUINO_PARTITION_default;ESP32;CORE_DEBUG_LEVEL=0;ARDUINO_USB_CDC_ON_BOOT=0;__cplusplus=201103L;_VMICRO_INTELLISENSE;%(PreprocessorDefinitions) 95 | 96 | 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | CppCode 106 | 107 | 108 | 109 | 110 | 111 | 112 | VisualMicroDebugger 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /examples/blinkbuttonandweb/blinkbuttonandweb.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/mutex/mutex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Name: mutex.ino 3 | Created: 2019-08-04 22:40:11 4 | Author: dok@dok-net.net 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define USE_BUILTIN_TASK_SCHEDULER 12 | 13 | CoopMutex serialMutex; 14 | CoopMutex mutex; 15 | CoopTaskBase* hasMutex(nullptr); 16 | 17 | void haveMutex() 18 | { 19 | if (hasMutex) 20 | { 21 | CoopMutexLock serialLock(serialMutex); 22 | Serial.print(CoopTaskBase::self()->name()); 23 | Serial.print(F(" called haveMutex, despite ")); 24 | Serial.print(hasMutex->name()); 25 | Serial.println(F(" is known to have mutex.")); 26 | return; 27 | } 28 | hasMutex = CoopTaskBase::self(); 29 | } 30 | 31 | void yieldMutex() 32 | { 33 | if (hasMutex != CoopTaskBase::self()) 34 | { 35 | CoopMutexLock serialLock(serialMutex); 36 | Serial.print(CoopTaskBase::self()->name()); 37 | Serial.println(F(" called yieldMutex, but no task currently has the mutex.")); 38 | return; 39 | } 40 | hasMutex = nullptr; 41 | } 42 | 43 | CoopTask* firstTask; 44 | CoopTask* secondTask; 45 | CoopTask* thirdTask; 46 | 47 | void setup() { 48 | #ifdef ESP8266 49 | Serial.begin(74880); 50 | #else 51 | Serial.begin(115200); 52 | #endif 53 | while (!Serial) {} 54 | delay(500); 55 | Serial.println(F("Mutex test")); 56 | 57 | #if defined(ESP8266) && defined(USE_BUILTIN_TASK_SCHEDULER) 58 | CoopTaskBase::useBuiltinScheduler(); 59 | #endif 60 | 61 | firstTask = createCoopTask(F("first"), []() 62 | { 63 | { 64 | CoopMutexLock serialLock(serialMutex); 65 | Serial.print(CoopTaskBase::self()->name()); 66 | Serial.println(F(" starts")); 67 | } 68 | for (int i = 0; i < 30; ++i) 69 | { 70 | { 71 | CoopMutexLock serialLock(serialMutex); 72 | Serial.print(CoopTaskBase::self()->name()); 73 | Serial.println(F(" locks mutex")); 74 | } 75 | { 76 | CoopMutexLock lock(mutex); 77 | if (!lock) { 78 | CoopMutexLock serialLock(serialMutex); 79 | Serial.print(F("failed to lock mutex in ")); 80 | Serial.println(CoopTaskBase::self()->name()); 81 | } 82 | haveMutex(); 83 | { 84 | CoopMutexLock serialLock(serialMutex); 85 | Serial.print(CoopTaskBase::self()->name()); 86 | Serial.println(F(" has mutex")); 87 | } 88 | yield(); 89 | yieldMutex(); 90 | } 91 | { 92 | CoopMutexLock serialLock(serialMutex); 93 | Serial.print(CoopTaskBase::self()->name()); 94 | Serial.print(F(" runs (")); 95 | Serial.print(i); 96 | Serial.println(')'); 97 | } 98 | yield(); 99 | } 100 | CoopMutexLock serialLock(serialMutex); 101 | Serial.print(F("exiting from task ")); 102 | Serial.println(CoopTaskBase::self()->name()); 103 | return 0; 104 | } 105 | #if defined(ESP8266) || defined(ESP32) 106 | ); 107 | #else 108 | , 0x120); 109 | #endif 110 | if (!firstTask) Serial.println(F("firstTask not created")); 111 | secondTask = createCoopTask(F("second"), []() 112 | { 113 | { 114 | CoopMutexLock serialLock(serialMutex); 115 | Serial.print(CoopTaskBase::self()->name()); 116 | Serial.println(F(" starts")); 117 | } 118 | for (int i = 0; i < 30; ++i) 119 | { 120 | { 121 | CoopMutexLock serialLock(serialMutex); 122 | Serial.print(CoopTaskBase::self()->name()); 123 | Serial.println(F(" locks mutex")); 124 | } 125 | { 126 | CoopMutexLock lock(mutex); 127 | if (!lock) { 128 | CoopMutexLock serialLock(serialMutex); 129 | Serial.print(F("failed to lock mutex in ")); 130 | Serial.println(CoopTaskBase::self()->name()); 131 | } 132 | haveMutex(); 133 | { 134 | CoopMutexLock serialLock(serialMutex); 135 | Serial.print(CoopTaskBase::self()->name()); 136 | Serial.println(F(" has mutex")); 137 | } 138 | yield(); 139 | yieldMutex(); 140 | } 141 | { 142 | CoopMutexLock serialLock(serialMutex); 143 | Serial.print(CoopTaskBase::self()->name()); 144 | Serial.print(F(" runs (")); 145 | Serial.print(i); 146 | Serial.println(')'); 147 | } 148 | yield(); 149 | } 150 | CoopMutexLock serialLock(serialMutex); 151 | Serial.print(F("exiting from task ")); 152 | Serial.println(CoopTaskBase::self()->name()); 153 | return 0; 154 | } 155 | #if defined(ESP8266) || defined(ESP32) 156 | ); 157 | #else 158 | , 0x120); 159 | #endif 160 | if (!secondTask) Serial.println(F("secondTask not created")); 161 | thirdTask = createCoopTask(F("third"), []() 162 | { 163 | { 164 | CoopMutexLock serialLock(serialMutex); 165 | Serial.print(CoopTaskBase::self()->name()); 166 | Serial.println(F(" starts")); 167 | } 168 | for (int i = 0; i < 10; ++i) 169 | { 170 | { 171 | CoopMutexLock serialLock(serialMutex); 172 | Serial.print(CoopTaskBase::self()->name()); 173 | Serial.println(F(" locks mutex")); 174 | } 175 | { 176 | CoopMutexLock lock(mutex); 177 | if (!lock) { 178 | CoopMutexLock serialLock(serialMutex); 179 | Serial.print(F("failed to lock mutex in ")); 180 | Serial.println(CoopTaskBase::self()->name()); 181 | } 182 | haveMutex(); 183 | { 184 | CoopMutexLock serialLock(serialMutex); 185 | Serial.print(CoopTaskBase::self()->name()); 186 | Serial.println(F(" has mutex")); 187 | } 188 | yield(); 189 | yieldMutex(); 190 | } 191 | { 192 | CoopMutexLock serialLock(serialMutex); 193 | Serial.print(CoopTaskBase::self()->name()); 194 | Serial.print(F(" runs (")); 195 | Serial.print(i); 196 | Serial.println(')'); 197 | } 198 | yield(); 199 | } 200 | for (int i = 0; i < 10; ++i) 201 | { 202 | { 203 | CoopMutexLock serialLock(serialMutex); 204 | Serial.print(CoopTaskBase::self()->name()); 205 | Serial.print(F(" still runs (")); 206 | Serial.print(i); 207 | Serial.println(')'); 208 | } 209 | yield(); 210 | } 211 | CoopMutexLock serialLock(serialMutex); 212 | Serial.print(F("exiting from task ")); 213 | Serial.println(CoopTaskBase::self()->name()); 214 | return 0; 215 | } 216 | #if defined(ESP8266) || defined(ESP32) 217 | ); 218 | #else 219 | , 0x120); 220 | #endif 221 | if (!thirdTask) Serial.println(F("thirdTask not created")); 222 | 223 | #ifdef ESP32 224 | Serial.print(F("Loop free stack = ")); Serial.println(uxTaskGetStackHighWaterMark(NULL)); 225 | #endif 226 | } 227 | 228 | void taskReaper(const CoopTaskBase* const task) 229 | { 230 | delete task; 231 | } 232 | 233 | // the loop function runs over and over again until power down or reset 234 | void loop() 235 | { 236 | #if defined(ESP8266) && defined(USE_BUILTIN_TASK_SCHEDULER) 237 | if (firstTask && !*firstTask) 238 | { 239 | taskReaper(firstTask); firstTask = nullptr; 240 | } 241 | if (secondTask && !*secondTask) 242 | { 243 | taskReaper(secondTask); secondTask = nullptr; 244 | } 245 | if (thirdTask && !*thirdTask) 246 | { 247 | taskReaper(thirdTask); thirdTask = nullptr; 248 | } 249 | #else 250 | runCoopTasks(taskReaper); 251 | #endif 252 | } 253 | -------------------------------------------------------------------------------- /examples/portable/portable.cpp: -------------------------------------------------------------------------------- 1 | // portable.cpp 2 | // This is a basic portable example, without a scheduler. 3 | // All tasks are run round-robin inside a for loop. 4 | // It shows CoopTask creation, synchronization, and termination. 5 | 6 | #include 7 | #include "CoopTask.h" 8 | #include "CoopSemaphore.h" 9 | #include "CoopMutex.h" 10 | 11 | template void printStackReport(BasicCoopTask& task) 12 | { 13 | if (!task) return; 14 | std::cerr << task.name().c_str() << " free stack = " << task.getFreeStack() << std::endl; 15 | } 16 | 17 | CoopMutex blinkMutex; 18 | 19 | int main() 20 | { 21 | CoopSemaphore terminatorSema(0); 22 | CoopSemaphore helloSema(0); 23 | 24 | auto& hello = *createCoopTask(std::string("hello"), [&terminatorSema, &helloSema]() noexcept 25 | { 26 | std::cerr << "Hello" << std::endl; 27 | yield(); 28 | for (int x = 0; x < 10; ++x) 29 | { 30 | { 31 | CoopMutexLock lock(blinkMutex); 32 | std::cerr << "Loop" << std::endl; 33 | } 34 | helloSema.wait(2000); 35 | } 36 | terminatorSema.post(); 37 | }, 0x2000); 38 | if (!hello) std::cerr << hello.name() << " CoopTask not created" << std::endl; 39 | 40 | 41 | bool keepBlinking = true; 42 | 43 | auto& terminator = *createCoopTask(std::string("terminator"), [&keepBlinking, &terminatorSema]() noexcept 44 | { 45 | if (!terminatorSema.wait()) std::cerr << "terminatorSema.wait() failed" << std::endl; 46 | keepBlinking = false; 47 | }, 0x2000); 48 | if (!terminator) std::cerr << terminator.name() << " CoopTask not created" << std::endl; 49 | 50 | auto& blink = *createCoopTask>(std::string("blink"), [&keepBlinking]() 51 | { 52 | while (keepBlinking) 53 | { 54 | { 55 | CoopMutexLock lock(blinkMutex); 56 | std::cerr << "LED on" << std::endl; 57 | delay(1000); 58 | std::cerr << "LED off" << std::endl; 59 | } 60 | delay(1000); 61 | } 62 | throw std::string("sixtynine"); 63 | return "fortytwo"; 64 | }, 0x2000); 65 | if (!blink) std::cerr << blink.name() << " CoopTask not created" << std::endl; 66 | 67 | auto& report = *createCoopTask(std::string("report"), [&hello, &blink]() noexcept 68 | { 69 | for (;;) { 70 | delay(5000); 71 | { 72 | CoopMutexLock lock(blinkMutex); 73 | printStackReport(hello); 74 | printStackReport(blink); 75 | } 76 | } 77 | }, 0x2000); 78 | if (!report) std::cerr << report.name() << " CoopTask not created" << std::endl; 79 | 80 | auto taskReaper = [&blink](const CoopTaskBase* const task) 81 | { 82 | // once: hello posts terminatorSema -> terminator sets keepBlinking = false -> blink exits -> break leaves for-loop -> program exits 83 | if (task == &blink) 84 | { 85 | std::cerr << task->name() << " returns = " << blink.exitCode() << std::endl; 86 | delete task; 87 | exit(0); 88 | } 89 | }; 90 | 91 | for (;;) 92 | { 93 | runCoopTasks(taskReaper); 94 | } 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CoopTask", 3 | "version": "3.10.0", 4 | "description": "Portable C++ library for cooperative multitasking like Arduino Scheduler on ESP8266/ESP32, AVR, Linux, Windows", 5 | "keywords": [ 6 | "multitasking", "timing" 7 | ], 8 | "repository": 9 | { 10 | "type": "git", 11 | "url": "https://github.com/dok-net/CoopTask" 12 | }, 13 | "authors": [ 14 | { 15 | "name": "Dirk Kaar" 16 | } 17 | ], 18 | "license": "LGPL-2.1+", 19 | "frameworks": "arduino", 20 | "platforms": "*", 21 | "dependencies": 22 | { 23 | "dok-net/ghostl": "^1.0.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=CoopTask 2 | version=3.10.0 3 | author=Dirk O. Kaar 4 | maintainer=Dirk O. Kaar 5 | sentence=Portable C++ library for cooperative multitasking like Arduino Scheduler on ESP8266/ESP32, AVR, Linux, Windows 6 | paragraph=Run multiple concurrent setup()/loop() tasks in Arduino sketches. Use the normal global delay() function, use yield() to give up the CPU to other tasks and the main loop(). 7 | category=Timing 8 | url=https://github.com/dok-net/CoopTask 9 | architectures=* 10 | depends=ghostl(>=1.0.0) 11 | -------------------------------------------------------------------------------- /src/BasicCoopTask.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | BasicCoopTask.cpp - Implementation of cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include "BasicCoopTask.h" 21 | 22 | #if defined(ARDUINO) && !defined(ESP32_FREERTOS) 23 | #include 24 | #endif 25 | 26 | #if !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 27 | 28 | char* CoopTaskStackAllocator::allocateStack(size_t stackSize) 29 | { 30 | char* stackTop = nullptr; 31 | if (stackSize <= CoopTaskBase::MAXSTACKSPACE - (CoopTaskBase::FULLFEATURES ? 2 : 1) * sizeof(CoopTaskBase::STACKCOOKIE)) 32 | { 33 | #if defined(ESP8266) 34 | stackTop = new (std::nothrow) char[stackSize + (CoopTaskBase::FULLFEATURES ? 2 : 1) * sizeof(CoopTaskBase::STACKCOOKIE)]; 35 | #else 36 | stackTop = new char[stackSize + (CoopTaskBase::FULLFEATURES ? 2 : 1) * sizeof(CoopTaskBase::STACKCOOKIE)]; 37 | #endif 38 | } 39 | return stackTop; 40 | } 41 | 42 | #endif // !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 43 | 44 | #if (defined(ARDUINO) && !defined(ESP32_FREERTOS)) || defined(__GNUC__) 45 | 46 | char* CoopTaskStackAllocatorFromLoopBase::allocateStack(size_t loopReserve, size_t stackSize) 47 | { 48 | char* bp = static_cast(alloca( 49 | (sizeof(unsigned) >= 4) ? ((loopReserve + sizeof(unsigned) - 1) / sizeof(unsigned)) * sizeof(unsigned) : loopReserve 50 | )); 51 | std::atomic_thread_fence(std::memory_order_release); 52 | char* stackTop = nullptr; 53 | if (stackSize <= CoopTaskBase::MAXSTACKSPACE - (CoopTaskBase::FULLFEATURES ? 2 : 1) * sizeof(CoopTaskBase::STACKCOOKIE)) 54 | { 55 | stackTop = reinterpret_cast( 56 | reinterpret_cast(bp) - stackSize + (CoopTaskBase::FULLFEATURES ? 2 : 1) * sizeof(CoopTaskBase::STACKCOOKIE)); 57 | } 58 | return stackTop; 59 | } 60 | 61 | #endif // (defined(ARDUINO) && !defined(ESP32_FREERTOS)) || defined(__GNUC__) 62 | -------------------------------------------------------------------------------- /src/BasicCoopTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | BasicCoopTask.h - Implementation of cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __BasicCoopTask_h 21 | #define __BasicCoopTask_h 22 | 23 | #include "CoopTaskBase.h" 24 | 25 | class CoopTaskStackAllocator 26 | { 27 | public: 28 | static constexpr size_t DEFAULTTASKSTACKSIZE = CoopTaskBase::DEFAULTTASKSTACKSIZE; 29 | #if !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 30 | static char* allocateStack(size_t stackSize); 31 | static void disposeStack(char* stackTop) { delete[] stackTop; } 32 | #endif 33 | }; 34 | 35 | template 36 | class CoopTaskStackAllocatorAsMember 37 | { 38 | public: 39 | static constexpr size_t DEFAULTTASKSTACKSIZE = 40 | (sizeof(unsigned) >= 4) ? ((StackSize + sizeof(unsigned) - 1) / sizeof(unsigned)) * sizeof(unsigned) : StackSize; 41 | 42 | #if !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 43 | protected: 44 | char _stackTop[DEFAULTTASKSTACKSIZE + (CoopTaskBase::FULLFEATURES ? 2 : 1) * sizeof(CoopTaskBase::STACKCOOKIE)]; 45 | public: 46 | char* allocateStack(size_t stackSize) 47 | { 48 | return (DEFAULTTASKSTACKSIZE >= stackSize) ? 49 | _stackTop : nullptr; 50 | } 51 | static void disposeStack(char* stackTop) { } 52 | #endif 53 | }; 54 | 55 | class CoopTaskStackAllocatorFromLoopBase 56 | { 57 | public: 58 | static constexpr size_t DEFAULTTASKSTACKSIZE = CoopTaskBase::DEFAULTTASKSTACKSIZE; 59 | #if (defined(ARDUINO) && !defined(ESP32_FREERTOS)) || defined(__GNUC__) 60 | protected: 61 | static char* allocateStack(size_t loopReserve, size_t stackSize); 62 | #endif 63 | public: 64 | static void disposeStack(char* stackTop) { } 65 | }; 66 | 67 | template 68 | class CoopTaskStackAllocatorFromLoop : public CoopTaskStackAllocatorFromLoopBase 69 | { 70 | public: 71 | static constexpr size_t DEFAULTTASKSTACKSIZE = CoopTaskBase::DEFAULTTASKSTACKSIZE; 72 | #if (defined(ARDUINO) && !defined(ESP32_FREERTOS)) || defined(__GNUC__) 73 | static char* allocateStack(size_t stackSize) 74 | { 75 | return CoopTaskStackAllocatorFromLoopBase::allocateStack(LoopReserve, stackSize); 76 | } 77 | #endif 78 | }; 79 | 80 | template class BasicCoopTask : public CoopTaskBase 81 | { 82 | public: 83 | #ifdef ARDUINO 84 | BasicCoopTask(const String& name, taskfunction_t _func, size_t stackSize = StackAllocator::DEFAULTTASKSTACKSIZE) : 85 | #else 86 | BasicCoopTask(const std::string& name, taskfunction_t _func, size_t stackSize = StackAllocator::DEFAULTTASKSTACKSIZE) : 87 | #endif 88 | CoopTaskBase(name, _func, stackSize) 89 | { 90 | #if !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 91 | taskStackTop = stackAllocator.allocateStack(taskStackSize); 92 | #endif 93 | } 94 | BasicCoopTask(const BasicCoopTask&) = delete; 95 | BasicCoopTask& operator=(const BasicCoopTask&) = delete; 96 | ~BasicCoopTask() 97 | { 98 | #if !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 99 | stackAllocator.disposeStack(taskStackTop); 100 | #endif 101 | } 102 | /// Every task is entered into this list by scheduleTask(). It is removed when it exits 103 | /// or gets deleted. 104 | static const std::array< std::atomic, MAXNUMBERCOOPTASKS + 1>& getRunnableTasks() 105 | { 106 | // this is safe to do because CoopTaskBase ctor is protected. 107 | return reinterpret_cast, MAXNUMBERCOOPTASKS + 1>&>(CoopTaskBase::getRunnableTasks()); 108 | } 109 | protected: 110 | StackAllocator stackAllocator; 111 | }; 112 | 113 | #endif // __BasicCoopTask_h 114 | -------------------------------------------------------------------------------- /src/CoopMutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoopMutex.h - Implementation of a mutex and an RAII lock for cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __CoopMutex_h 21 | #define __CoopMutex_h 22 | 23 | #include "CoopSemaphore.h" 24 | 25 | /// A mutex that is safe to use from CoopTasks. 26 | class CoopMutex : private CoopSemaphore 27 | { 28 | protected: 29 | std::atomic owner; 30 | 31 | public: 32 | CoopMutex(size_t maxPending = 10) : CoopSemaphore(1, maxPending), owner(nullptr) {} 33 | CoopMutex(const CoopMutex&) = delete; 34 | CoopMutex& operator=(const CoopMutex&) = delete; 35 | 36 | /// @returns: true, or false, if the current task does not own the mutex. 37 | bool unlock() 38 | { 39 | if (CoopTaskBase::running() && CoopTaskBase::self() == owner.load() && post()) 40 | { 41 | owner.store(nullptr); 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | /// @returns: true if the mutex becomes locked. false if it is already locked by the same task, or the maximum number of pending tasks is exceeded. 48 | bool lock() 49 | { 50 | if (CoopTaskBase::running() && CoopTaskBase::self() != owner.load() && wait()) 51 | { 52 | owner.store(CoopTaskBase::self()); 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | /// @returns: true if the mutex becomes freshly locked without waiting, otherwise false. 59 | bool try_lock() 60 | { 61 | if (CoopTaskBase::running() && CoopTaskBase::self() != owner.load() && try_wait()) 62 | { 63 | owner.store(CoopTaskBase::self()); 64 | return true; 65 | } 66 | return false; 67 | } 68 | }; 69 | 70 | /// A RAII CoopMutex lock class. 71 | class CoopMutexLock { 72 | protected: 73 | CoopMutex& mutex; 74 | bool locked; 75 | public: 76 | /// The constructor returns if the mutex was locked, or locking failed. 77 | explicit CoopMutexLock(CoopMutex& _mutex) : mutex(_mutex) { 78 | locked = mutex.lock(); 79 | } 80 | CoopMutexLock() = delete; 81 | CoopMutexLock(const CoopMutexLock&) = delete; 82 | CoopMutexLock& operator=(const CoopMutexLock&) = delete; 83 | /// @returns: true if the mutex became locked, potentially after blocking, otherwise false. 84 | operator bool() const { 85 | return locked; 86 | } 87 | /// The destructor unlocks the mutex. 88 | ~CoopMutexLock() { 89 | if (locked) mutex.unlock(); 90 | } 91 | }; 92 | 93 | #endif // __CoopMutex_h 94 | -------------------------------------------------------------------------------- /src/CoopSemaphore.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | CoopSemaphore.cpp - Implementation of a semaphore for cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include "CoopSemaphore.h" 21 | 22 | #if defined(ESP8266) 23 | #include 24 | using esp8266::InterruptLock; 25 | #elif defined(ESP32) || !defined(ARDUINO) 26 | using std::min; 27 | #else 28 | class InterruptLock { 29 | public: 30 | InterruptLock() { 31 | noInterrupts(); 32 | } 33 | ~InterruptLock() { 34 | interrupts(); 35 | } 36 | }; 37 | #endif 38 | 39 | #ifndef ARDUINO 40 | #include 41 | namespace 42 | { 43 | uint32_t millis() 44 | { 45 | return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); 46 | } 47 | } 48 | #endif 49 | 50 | bool CoopSemaphore::_wait(const bool withDeadline, const uint32_t ms) 51 | { 52 | const uint32_t start = withDeadline ? millis() : 0; 53 | uint32_t expired = 0; 54 | bool selfFirst = false; 55 | for (;;) 56 | { 57 | auto self = CoopTaskBase::self(); 58 | unsigned val; 59 | #if !defined(ESP32) && defined(ARDUINO) 60 | { 61 | InterruptLock lock; 62 | val = value.load(); 63 | if (val) 64 | { 65 | value.store(val - 1); 66 | } 67 | } 68 | #else 69 | val = 1; 70 | while (val && !value.compare_exchange_weak(val, val - 1)) {} 71 | #endif 72 | const unsigned valOnEntry = val; 73 | if (withDeadline) expired = millis() - start; 74 | if (!(selfFirst && valOnEntry)) 75 | { 76 | if (pendingTasks.push(self)) 77 | { 78 | if (!withDeadline) self->sleep(true); 79 | } 80 | else 81 | { 82 | selfFirst = true; 83 | } 84 | } 85 | bool fwd = !selfFirst && val; 86 | bool stop = false; 87 | CoopTaskBase* pendingTask = nullptr; 88 | bool selfSuccess = false; 89 | for (;;) 90 | { 91 | if (pendingTasks.available()) 92 | { 93 | #if !defined(ESP32) && defined(ARDUINO) 94 | { 95 | InterruptLock lock; 96 | pendingTask = pendingTask0.load(); 97 | if (fwd || !pendingTask) pendingTask0.store(pendingTasks.pop()); 98 | } 99 | #else 100 | pendingTask = nullptr; 101 | bool exchd = false; 102 | while ((fwd || !pendingTask) && !(exchd = pendingTask0.compare_exchange_weak(pendingTask, pendingTasks.peek()))) {} 103 | if (exchd) pendingTasks.pop(); 104 | #endif 105 | } 106 | else 107 | { 108 | #if !defined(ESP32) && defined(ARDUINO) 109 | { 110 | InterruptLock lock; 111 | pendingTask = pendingTask0.load(); 112 | if (fwd && pendingTask) pendingTask0.store(nullptr); 113 | } 114 | #else 115 | pendingTask = nullptr; 116 | if (fwd) pendingTask = pendingTask0.exchange(nullptr); 117 | #endif 118 | stop = true; 119 | } 120 | if (!val) 121 | { 122 | break; 123 | } 124 | if (!(pendingTask || stop)) 125 | { 126 | continue; 127 | } 128 | if (selfFirst) 129 | { 130 | selfFirst = false; 131 | if (!withDeadline) self->sleep(false); 132 | selfSuccess = true; 133 | } 134 | else if (pendingTask == self) 135 | { 136 | if (!selfSuccess) 137 | { 138 | if (!withDeadline) self->sleep(false); 139 | return true; 140 | } 141 | if (!stop) continue; 142 | } 143 | else if (pendingTask) 144 | { 145 | pendingTask->scheduleTask(true); 146 | } 147 | if (stop) 148 | { 149 | break; 150 | } 151 | val -= 1; 152 | fwd = val; 153 | } 154 | if (selfSuccess) 155 | { 156 | return true; 157 | } 158 | if (valOnEntry) 159 | { 160 | #if !defined(ESP32) && defined(ARDUINO) 161 | InterruptLock lock; 162 | val = value.load(); 163 | value.store(val + 1); 164 | #else 165 | while (!value.compare_exchange_weak(val, val + 1)) {} 166 | #endif 167 | } 168 | if (withDeadline) 169 | { 170 | if (expired >= ms) 171 | { 172 | pendingTasks.for_each_rev_requeue(notIsSelfTask); 173 | #if !defined(ESP32) && defined(ARDUINO) 174 | { 175 | InterruptLock lock; 176 | pendingTask = pendingTask0.load(); 177 | if (pendingTask == self) pendingTask0.store(pendingTasks.available() ? pendingTasks.pop() : nullptr); 178 | } 179 | #else 180 | bool exchd = false; 181 | pendingTask = self; 182 | while ((pendingTask == self) && !(exchd = pendingTask0.compare_exchange_weak(pendingTask, pendingTasks.available() ? pendingTasks.peek() : nullptr))) {} 183 | if (exchd && pendingTasks.available()) pendingTasks.pop(); 184 | #endif 185 | return false; 186 | } 187 | CoopTaskBase::delay(ms - expired); 188 | } 189 | else 190 | { 191 | CoopTaskBase::yield(); 192 | } 193 | selfFirst = true; 194 | } 195 | } 196 | 197 | bool IRAM_ATTR CoopSemaphore::post() 198 | { 199 | CoopTaskBase* pendingTask; 200 | #if !defined(ESP32) && defined(ARDUINO) 201 | { 202 | InterruptLock lock; 203 | unsigned val = value.load(); 204 | value.store(val + 1); 205 | pendingTask = pendingTask0.load(); 206 | if (pendingTask) pendingTask0.store(nullptr); 207 | } 208 | #else 209 | unsigned val = 0; 210 | while (!value.compare_exchange_weak(val, val + 1)) {} 211 | pendingTask = pendingTask0.exchange(nullptr); 212 | #endif 213 | if (!pendingTask || !pendingTask->suspended()) return true; 214 | return pendingTask->scheduleTask(true); 215 | } 216 | 217 | bool CoopSemaphore::setval(unsigned newVal) 218 | { 219 | CoopTaskBase* pendingTask = nullptr; 220 | unsigned val; 221 | #if !defined(ESP32) && defined(ARDUINO) 222 | { 223 | InterruptLock lock; 224 | val = value.load(); 225 | value.store(newVal); 226 | if (newVal > val) 227 | { 228 | pendingTask = pendingTask0.load(); 229 | pendingTask0.store(nullptr); 230 | } 231 | } 232 | #else 233 | val = value.exchange(newVal); 234 | if (newVal > val) pendingTask = pendingTask0.exchange(nullptr); 235 | #endif 236 | if (!pendingTask || !pendingTask->suspended()) return true; 237 | return pendingTask->scheduleTask(true); 238 | } 239 | 240 | bool CoopSemaphore::try_wait() 241 | { 242 | unsigned val; 243 | #if !defined(ESP32) && defined(ARDUINO) 244 | { 245 | InterruptLock lock; 246 | val = value.load(); 247 | if (val) 248 | { 249 | value.store(val - 1); 250 | } 251 | } 252 | #else 253 | val = 1; 254 | while (val && !value.compare_exchange_weak(val, val - 1)) {} 255 | #endif 256 | return val; 257 | } 258 | -------------------------------------------------------------------------------- /src/CoopSemaphore.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoopSemaphore.h - Implementation of a semaphore for cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __CoopSemaphore_h 21 | #define __CoopSemaphore_h 22 | 23 | #include "CoopTaskBase.h" 24 | #include 25 | 26 | /// A semaphore that is safe to use from CoopTasks. 27 | /// Only post() is safe to use from interrupt service routines, 28 | /// or concurrent OS threads that must synchronized with the singled thread running CoopTasks. 29 | class CoopSemaphore 30 | { 31 | protected: 32 | std::atomic value; 33 | std::atomic pendingTask0; 34 | circular_queue pendingTasks; 35 | 36 | // capture-less functions for iterators. 37 | static void awakeAndSchedule(CoopTaskBase*&& task) 38 | { 39 | task->scheduleTask(true); 40 | } 41 | static bool notIsSelfTask(CoopTaskBase*& task) 42 | { 43 | return CoopTaskBase::self() != task; 44 | } 45 | 46 | /// @param withDeadline true: the ms parameter specifies the relative timeout for a successful 47 | /// aquisition of the semaphore. 48 | /// false: there is no deadline, the ms parameter is disregarded. 49 | /// @param ms the relative timeout measured in milliseconds. 50 | /// @returns: true if it sucessfully acquired the semaphore, either immediately or after sleeping. 51 | /// false if the deadline expired, or the maximum number of pending tasks is exceeded. 52 | bool _wait(const bool withDeadline = false, const uint32_t ms = 0); 53 | 54 | public: 55 | /// @param val the initial value of the semaphore. 56 | /// @param maxPending the maximum supported number of concurrently waiting tasks. 57 | CoopSemaphore(unsigned val, size_t maxPending = 10) : value(val), pendingTask0(nullptr), pendingTasks(maxPending) {} 58 | CoopSemaphore(const CoopSemaphore&) = delete; 59 | CoopSemaphore& operator=(const CoopSemaphore&) = delete; 60 | ~CoopSemaphore() 61 | { 62 | // wake up all queued tasks 63 | pendingTasks.for_each(awakeAndSchedule); 64 | } 65 | 66 | /// post() is the only operation that is allowed from an interrupt service routine, 67 | /// or a concurrent OS thread that is synchronized with the singled thread running CoopTasks. 68 | bool IRAM_ATTR post(); 69 | 70 | /// @param newVal: the semaphore is immediately set to the specified value. if newVal is greater 71 | /// than the current semaphore value, the behavior is identical to as many post operations. 72 | bool setval(unsigned newVal); 73 | 74 | /// @returns: true if it sucessfully acquired the semaphore, either immediately or after sleeping. 75 | /// false if the maximum number of pending tasks is exceeded. 76 | bool wait() 77 | { 78 | return _wait(); 79 | } 80 | 81 | /// @param ms the relative timeout, measured in milliseconds, for a successful aquisition of the semaphore. 82 | /// @returns: true if it sucessfully acquired the semaphore, either immediately or after sleeping. 83 | /// false if the deadline expired, or the maximum number of pending tasks is exceeded. 84 | bool wait(uint32_t ms) 85 | { 86 | return _wait(true, ms); 87 | } 88 | 89 | /// @returns: true if the semaphore was acquired immediately, otherwise false. 90 | bool try_wait(); 91 | }; 92 | 93 | #endif // __CoopSemaphore_h 94 | -------------------------------------------------------------------------------- /src/CoopTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoopTask.h - Implementation of cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __CoopTask_h 21 | #define __CoopTask_h 22 | 23 | #include "BasicCoopTask.h" 24 | 25 | template class CoopTask : public BasicCoopTask 26 | { 27 | public: 28 | using taskfunction_t = Delegate< Result() >; 29 | 30 | #if defined(ARDUINO) 31 | CoopTask(const String& name, CoopTask::taskfunction_t _func, size_t stackSize = BasicCoopTask::DEFAULTTASKSTACKSIZE) : 32 | #else 33 | CoopTask(const std::string& name, CoopTask::taskfunction_t _func, size_t stackSize = BasicCoopTask::DEFAULTTASKSTACKSIZE) : 34 | #endif 35 | // Wrap _func into _exit() to capture return value as exit code 36 | BasicCoopTask(name, captureFuncReturn, stackSize), func(_func) 37 | { 38 | } 39 | 40 | protected: 41 | Result _exitCode {}; 42 | 43 | static void captureFuncReturn() noexcept 44 | { 45 | #if !defined(ARDUINO) 46 | try { 47 | #endif 48 | self()->_exitCode = self()->func(); 49 | #if !defined(ARDUINO) 50 | } 51 | catch (const Result code) 52 | { 53 | self()->_exitCode = code; 54 | } 55 | catch (...) 56 | { 57 | } 58 | #endif 59 | } 60 | void _exit(Result&& code = Result{}) noexcept 61 | { 62 | _exitCode = std::move(code); 63 | BasicCoopTask::_exit(); 64 | } 65 | void _exit(const Result& code) noexcept 66 | { 67 | _exitCode = code; 68 | BasicCoopTask::_exit(); 69 | } 70 | 71 | private: 72 | taskfunction_t func; 73 | 74 | public: 75 | /// @returns: The exit code is either the return value of of the task function, or set by using the exit() function. 76 | Result exitCode() const noexcept { return _exitCode; } 77 | 78 | /// @returns: a pointer to the CoopTask instance that is running. nullptr if not called from a CoopTask function (running() == false). 79 | static CoopTask* self() noexcept { return static_cast(BasicCoopTask::self()); } 80 | 81 | /// Use only in running CoopTask function. As stack unwinding is corrupted 82 | /// by exit(), which among other issues breaks the RAII idiom, 83 | /// using regular return or exceptions is to be preferred in most cases. 84 | /// @param code default exit code is default value of CoopTask<>'s template argument, use exit() to set a different value. 85 | static void exit(Result&& code = Result{}) noexcept { self()->_exit(std::move(code)); } 86 | 87 | /// Use only in running CoopTask function. As stack unwinding is corrupted 88 | /// by exit(), which among other issues breaks the RAII idiom, 89 | /// using regular return or exceptions is to be preferred in most cases. 90 | /// @param code default exit code is default value of CoopTask<>'s template argument, use exit() to set a different value. 91 | static void exit(const Result& code) noexcept { self()->_exit(code); } 92 | }; 93 | 94 | template class CoopTask : public BasicCoopTask 95 | { 96 | public: 97 | using CoopTaskBase::taskfunction_t; 98 | 99 | #if defined(ARDUINO) 100 | CoopTask(const String& name, CoopTaskBase::taskfunction_t func, size_t stackSize = BasicCoopTask::DEFAULTTASKSTACKSIZE) : 101 | #else 102 | CoopTask(const std::string& name, CoopTaskBase::taskfunction_t func, size_t stackSize = BasicCoopTask::DEFAULTTASKSTACKSIZE) : 103 | #endif 104 | BasicCoopTask(name, func, stackSize) 105 | { 106 | } 107 | 108 | /// @returns: a pointer to the CoopTask instance that is running. nullptr if not called from a CoopTask function (running() == false). 109 | static CoopTask* self() noexcept { return static_cast(BasicCoopTask::self()); } 110 | }; 111 | 112 | /// A convenience function that creates a new CoopTask instance for the supplied task function, with the 113 | /// given name and stack size, and schedules it. 114 | /// @returns: the pointer to the new CoopTask instance, or nullptr if the creation or preparing for scheduling failed. 115 | template 116 | CoopTask* createCoopTask( 117 | #if defined(ARDUINO) 118 | const String& name, typename CoopTask::taskfunction_t func, size_t stackSize = CoopTaskBase::DEFAULTTASKSTACKSIZE) 119 | #else 120 | const std::string& name, typename CoopTask::taskfunction_t func, size_t stackSize = CoopTaskBase::DEFAULTTASKSTACKSIZE) 121 | #endif 122 | { 123 | auto task = new CoopTask(name, func, stackSize); 124 | if (task && task->scheduleTask()) return task; 125 | delete task; 126 | return nullptr; 127 | } 128 | 129 | #endif // __CoopTask_h 130 | -------------------------------------------------------------------------------- /src/CoopTaskBase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | CoopTaskBase.cpp - Implementation of cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include "CoopTaskBase.h" 21 | #ifdef ARDUINO 22 | #include 23 | #else 24 | #include 25 | #endif 26 | 27 | #if defined(ESP8266) 28 | #include 29 | #include 30 | using esp8266::InterruptLock; 31 | #elif !defined(ESP32) && defined(ARDUINO) 32 | class InterruptLock { 33 | public: 34 | InterruptLock() { 35 | noInterrupts(); 36 | } 37 | ~InterruptLock() { 38 | interrupts(); 39 | } 40 | }; 41 | #endif 42 | 43 | extern "C" { 44 | // Integration into global yield() and delay() 45 | #if defined(ESP8266) || defined(ESP32) 46 | void __yield(); 47 | 48 | void yield() 49 | { 50 | auto self = CoopTaskBase::self(); 51 | if (self) CoopTaskBase::yield(self); 52 | else __yield(); 53 | } 54 | 55 | #elif defined(ARDUINO) 56 | void yield() 57 | { 58 | auto self = CoopTaskBase::self(); 59 | if (self) CoopTaskBase::yield(self); 60 | } 61 | #endif 62 | 63 | #if defined(ESP8266) 64 | void __esp_suspend(); 65 | 66 | // disable the CONT suspend, resume by esp_schedule pattern 67 | void esp_suspend() 68 | { 69 | auto self = CoopTaskBase::self(); 70 | if (self) CoopTaskBase::yield(self); 71 | else __esp_suspend(); 72 | } 73 | 74 | void __esp_delay(unsigned long ms); 75 | 76 | // disable the CONT suspend, resume by esp_schedule pattern 77 | void esp_delay(unsigned long ms) 78 | { 79 | auto self = CoopTaskBase::self(); 80 | if (self) CoopTaskBase::delay(self, ms); 81 | else __esp_delay(ms); 82 | } 83 | 84 | #elif defined(ESP32) && !defined(ESP32_FREERTOS) 85 | void __delay(uint32_t ms); 86 | 87 | void delay(uint32_t ms) 88 | { 89 | auto self = CoopTaskBase::self(); 90 | if (self) CoopTaskBase::delay(self, ms); 91 | else __delay(ms); 92 | } 93 | #endif // ESP32_FREERTOS 94 | } 95 | 96 | std::array< std::atomic, CoopTaskBase::MAXNUMBERCOOPTASKS + 1> CoopTaskBase::runnableTasks {}; 97 | std::atomic CoopTaskBase::runnableTasksCount(0); 98 | 99 | CoopTaskBase* CoopTaskBase::current = nullptr; 100 | 101 | #ifndef ARDUINO 102 | namespace 103 | { 104 | uint32_t millis() 105 | { 106 | return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); 107 | } 108 | uint32_t micros() 109 | { 110 | return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); 111 | } 112 | void delayMicroseconds(uint32_t us) 113 | { 114 | const uint32_t start = micros(); 115 | while (micros() - start < us) {} 116 | } 117 | } 118 | #elif defined(ESP8266) || defined(ESP32) 119 | namespace 120 | { 121 | static const uint32_t CYCLES_PER_MS = ESP.getCpuFreqMHz() * 1000; 122 | } 123 | #endif 124 | 125 | #if defined(ESP8266) 126 | bool CoopTaskBase::usingBuiltinScheduler = false; 127 | 128 | bool CoopTaskBase::rescheduleTask(uint32_t repeat_us) 129 | { 130 | auto stat = run(); 131 | if (sleeping()) return false; 132 | switch (stat) 133 | { 134 | case -1: // exited. 135 | return false; 136 | break; 137 | case 0: // runnable. 138 | // rather keep scheduling at wrong delayed interval than drop altogether 139 | if (repeat_us) 140 | { 141 | return !schedule_recurrent_function_us([this]() { return rescheduleTask(0); }, 0); 142 | } 143 | break; 144 | default: // delayed for stat milliseconds or microseconds, check delayIsMs(). 145 | uint32_t next_repeat_us = delayIsMs() ? stat * 1000 : stat; 146 | if (next_repeat_us > 26000000) next_repeat_us = 26000000; 147 | if (next_repeat_us == repeat_us) break; 148 | // rather keep scheduling at wrong interval than drop altogether 149 | return !schedule_recurrent_function_us([this, next_repeat_us]() { return rescheduleTask(next_repeat_us); }, next_repeat_us, [this]() { return !delayed(); }); 150 | break; 151 | } 152 | return true; 153 | } 154 | #endif 155 | 156 | bool IRAM_ATTR CoopTaskBase::enrollRunnable() 157 | { 158 | bool enrolled = false; 159 | bool inserted = false; 160 | for (size_t i = 0; i < runnableTasks.size(); ++i) 161 | { 162 | #if !defined(ESP32) && defined(ARDUINO) 163 | InterruptLock lock; 164 | auto task = runnableTasks[i].load(); 165 | if (!enrolled && nullptr == task) 166 | { 167 | runnableTasks[i].store(this); 168 | enrolled = true; 169 | inserted = true; 170 | } 171 | else if (this == task) 172 | { 173 | if (enrolled) 174 | { 175 | runnableTasks[i].store(nullptr); 176 | inserted = false; 177 | } 178 | else 179 | { 180 | enrolled = true; 181 | } 182 | break; 183 | } 184 | } 185 | if (inserted) 186 | { 187 | InterruptLock lock; 188 | runnableTasksCount.store(runnableTasksCount.load() + 1); 189 | } 190 | #else 191 | CoopTaskBase* cmpTo = nullptr; 192 | if (!enrolled && runnableTasks[i].compare_exchange_strong(cmpTo, this)) 193 | { 194 | enrolled = true; 195 | inserted = true; 196 | } 197 | else if (enrolled) 198 | { 199 | cmpTo = this; 200 | if (runnableTasks[i].compare_exchange_strong(cmpTo, nullptr)) 201 | { 202 | inserted = false; 203 | break; 204 | } 205 | } 206 | else if (this == runnableTasks[i].load()) 207 | { 208 | enrolled = true; 209 | break; 210 | } 211 | } 212 | if (inserted) ++runnableTasksCount; 213 | #endif 214 | return enrolled; 215 | } 216 | 217 | void CoopTaskBase::delistRunnable() 218 | { 219 | #if !defined(ESP32) && defined(ARDUINO) 220 | InterruptLock lock; 221 | for (size_t i = 0; i < runnableTasks.size(); ++i) 222 | { 223 | if (runnableTasks[i].load() == this) 224 | { 225 | runnableTasks[i].store(nullptr); 226 | runnableTasksCount.store(runnableTasksCount.load() - 1); 227 | break; 228 | } 229 | } 230 | #else 231 | for (size_t i = 0; i < runnableTasks.size(); ++i) 232 | { 233 | CoopTaskBase* self = this; 234 | if (runnableTasks[i].compare_exchange_strong(self, nullptr)) 235 | { 236 | --runnableTasksCount; 237 | break; 238 | } 239 | } 240 | #endif 241 | } 242 | 243 | bool IRAM_ATTR CoopTaskBase::scheduleTask(bool wakeup) 244 | { 245 | if (!*this || !enrollRunnable()) return false; 246 | #if defined(ESP8266) 247 | bool reschedule = usingBuiltinScheduler && sleeping(); 248 | #endif 249 | if (wakeup) 250 | { 251 | sleep(false); 252 | } 253 | #if defined(ESP8266) 254 | return !reschedule || schedule_function([this]() { rescheduleTask(1); }); 255 | #else 256 | return true; 257 | #endif 258 | } 259 | 260 | #if defined(_MSC_VER) 261 | 262 | CoopTaskBase::~CoopTaskBase() 263 | { 264 | if (taskFiber) DeleteFiber(taskFiber); 265 | delistRunnable(); 266 | } 267 | 268 | LPVOID CoopTaskBase::primaryFiber = nullptr; 269 | 270 | void __stdcall CoopTaskBase::taskFiberFunc(void* self) 271 | { 272 | static_cast(self)->func(); 273 | static_cast(self)->_exit(); 274 | } 275 | 276 | 277 | int32_t CoopTaskBase::initialize() 278 | { 279 | if (!cont || init) return -1; 280 | init = true; 281 | if (*this) 282 | { 283 | if (!primaryFiber) primaryFiber = ConvertThreadToFiber(nullptr); 284 | if (primaryFiber) 285 | { 286 | taskFiber = CreateFiber(taskStackSize, taskFiberFunc, this); 287 | if (taskFiber) return 0; 288 | } 289 | } 290 | cont = false; 291 | delistRunnable(); 292 | return -1; 293 | } 294 | 295 | int32_t CoopTaskBase::run() 296 | { 297 | if (!cont) return -1; 298 | if (sleeps.load()) return 0; 299 | if (delays.load()) 300 | { 301 | if (delay_ms) 302 | { 303 | auto expired = millis() - delay_start; 304 | if (expired < delay_duration) 305 | { 306 | auto delay_rem = delay_duration - expired; 307 | return static_cast(delay_rem) < 0 ? DELAY_MAXINT : delay_rem; 308 | } 309 | } 310 | else 311 | { 312 | auto expired = micros() - delay_start; 313 | if (expired < delay_duration) 314 | { 315 | auto delay_rem = delay_duration - expired; 316 | if (delay_rem >= DELAYMICROS_THRESHOLD) 317 | { 318 | return static_cast(delay_rem) < 0 ? DELAY_MAXINT : delay_rem; 319 | } 320 | ::delayMicroseconds(delay_rem); 321 | } 322 | } 323 | delays.store(false); 324 | delay_duration = 0; 325 | } 326 | current = this; 327 | if (!init && initialize() < 0) return -1; 328 | SwitchToFiber(taskFiber); 329 | current = nullptr; 330 | 331 | // val = 0: init; -1: exit() task; 1: yield task; 2: sleep task; 3: delay task for delay_duration 332 | cont = cont && (val > 0); 333 | sleeps.store(sleeps.load() || (val == 2)); 334 | delays.store(delays.load() || (val > 2)); 335 | 336 | if (!cont) { 337 | DeleteFiber(taskFiber); 338 | taskFiber = NULL; 339 | delistRunnable(); 340 | return -1; 341 | } 342 | switch (val) 343 | { 344 | case 1: 345 | case 2: 346 | return 0; 347 | break; 348 | case 3: 349 | default: 350 | return static_cast(delay_duration) < 0 ? DELAY_MAXINT : delay_duration; 351 | break; 352 | } 353 | } 354 | 355 | size_t CoopTaskBase::getFreeStack() const 356 | { 357 | return taskFiber ? taskStackSize : 0; 358 | } 359 | 360 | void CoopTaskBase::doYield(unsigned val) noexcept 361 | { 362 | self()->val = val; 363 | SwitchToFiber(primaryFiber); 364 | } 365 | 366 | void CoopTaskBase::_delay(uint32_t ms) noexcept 367 | { 368 | delay_ms = true; 369 | delay_start = millis(); 370 | delay_duration = ms; 371 | // CoopTask::run() defers task for delay_duration milliseconds. 372 | doYield(3); 373 | } 374 | 375 | void CoopTaskBase::_delayMicroseconds(uint32_t us) noexcept 376 | { 377 | if (us < DELAYMICROS_THRESHOLD) { 378 | ::delayMicroseconds(us); 379 | return; 380 | } 381 | delay_ms = false; 382 | delay_start = micros(); 383 | delay_duration = us; 384 | // CoopTask::run() defers task for delay_duration microseconds. 385 | doYield(3); 386 | } 387 | 388 | void CoopTaskBase::_exit() noexcept 389 | { 390 | self()->val = -1; 391 | SwitchToFiber(primaryFiber); 392 | } 393 | 394 | void CoopTaskBase::_yield() noexcept 395 | { 396 | doYield(1); 397 | } 398 | 399 | void CoopTaskBase::_sleep() noexcept 400 | { 401 | doYield(2); 402 | } 403 | 404 | void IRAM_ATTR CoopTaskBase::sleep(const bool state) noexcept 405 | { 406 | sleeps.store(state); 407 | if (!state) 408 | { 409 | delays.store(false); 410 | delay_duration = 0; 411 | } 412 | } 413 | 414 | #elif defined(ESP32_FREERTOS) 415 | 416 | CoopTaskBase::~CoopTaskBase() 417 | { 418 | if (taskHandle) vTaskDelete(taskHandle); 419 | taskHandle = nullptr; 420 | delistRunnable(); 421 | } 422 | 423 | void CoopTaskBase::taskFunc(void* _self) 424 | { 425 | static_cast(_self)->func(); 426 | static_cast(_self)->_exit(); 427 | } 428 | 429 | int32_t CoopTaskBase::initialize() 430 | { 431 | if (!cont || init) return -1; 432 | init = true; 433 | if (*this) 434 | { 435 | xTaskCreateUniversal(taskFunc, name().c_str(), taskStackSize, this, tskIDLE_PRIORITY, &taskHandle, CONFIG_ARDUINO_RUNNING_CORE); 436 | if (taskHandle) 437 | { 438 | vTaskSuspend(taskHandle); 439 | return 0; 440 | } 441 | } 442 | cont = false; 443 | delistRunnable(); 444 | return -1; 445 | } 446 | 447 | int32_t CoopTaskBase::run() 448 | { 449 | if (!cont) return -1; 450 | if (sleeps.load()) return 0; 451 | if (delays.load()) 452 | { 453 | if (delay_ms) 454 | { 455 | if (0 == delay_duration && eSuspended != eTaskGetState(taskHandle)) 456 | { 457 | // fall through, blocked during FreeRTOS delay or asynchronously ready after is specifically handled below 458 | } 459 | else 460 | { 461 | auto expired = (ESP.getCycleCount() - delay_start) / CYCLES_PER_MS; 462 | while (expired && delay_duration) 463 | { 464 | delay_start += CYCLES_PER_MS; 465 | --delay_duration; 466 | --expired; 467 | } 468 | if (expired < delay_duration) 469 | { 470 | auto delay_rem = delay_duration - expired; 471 | return static_cast(delay_rem) < 0 ? DELAY_MAXINT : delay_rem; 472 | } 473 | delays.store(false); 474 | delay_duration = 0; 475 | } 476 | } 477 | else 478 | { 479 | auto expired = micros() - delay_start; 480 | if (expired < delay_duration) 481 | { 482 | auto delay_rem = delay_duration - expired; 483 | if (delay_rem >= DELAYMICROS_THRESHOLD) 484 | { 485 | return static_cast(delay_rem) < 0 ? DELAY_MAXINT : delay_rem; 486 | } 487 | ::delayMicroseconds(delay_rem); 488 | } 489 | delays.store(false); 490 | delay_duration = 0; 491 | } 492 | } 493 | 494 | current = this; 495 | 496 | if (!init) 497 | { 498 | if (initialize() < 0) 499 | { 500 | current = nullptr; 501 | return -1; 502 | } 503 | } 504 | bool resume = true; 505 | for (;;) 506 | { 507 | auto taskState = eTaskGetState(taskHandle); 508 | if (eSuspended == taskState) 509 | { 510 | if (!resume) 511 | { 512 | vTaskPrioritySet(taskHandle, tskIDLE_PRIORITY); 513 | break; 514 | } 515 | resume = false; 516 | vTaskPrioritySet(taskHandle, 1); 517 | vTaskResume(taskHandle); 518 | continue; 519 | } 520 | else if (eReady == taskState) 521 | { 522 | if (resume) 523 | { 524 | resume = false; 525 | sleep(false); 526 | delay_duration = 0; 527 | vTaskPrioritySet(taskHandle, 1); 528 | continue; 529 | } 530 | vPortYield(); 531 | continue; 532 | } 533 | else if (eBlocked == taskState) 534 | { 535 | if (resume) 536 | { 537 | if (!delays.load()) 538 | { 539 | vTaskSuspend(taskHandle); 540 | continue; 541 | } 542 | break; 543 | } 544 | vTaskPrioritySet(taskHandle, tskIDLE_PRIORITY); 545 | if (!delays.exchange(true)) 546 | { 547 | delay_ms = true; 548 | delay_start = ESP.getCycleCount(); 549 | delay_duration = 0; 550 | } 551 | break; 552 | } 553 | else if (eDeleted == taskState) 554 | { 555 | cont = false; 556 | break; 557 | } 558 | } 559 | 560 | current = nullptr; 561 | 562 | if (!cont) { 563 | vTaskDelete(taskHandle); 564 | taskHandle = nullptr; 565 | delistRunnable(); 566 | return -1; 567 | } 568 | return static_cast(delay_duration) < 0 ? DELAY_MAXINT : delay_duration; 569 | } 570 | 571 | size_t CoopTaskBase::getFreeStack() const 572 | { 573 | return taskHandle ? uxTaskGetStackHighWaterMark(taskHandle) : 0; 574 | } 575 | 576 | void CoopTaskBase::_delay(uint32_t ms) noexcept 577 | { 578 | delays.store(true); 579 | delay_ms = true; 580 | delay_start = ESP.getCycleCount(); 581 | delay_duration = ms; 582 | vTaskSuspend(taskHandle); 583 | } 584 | 585 | void CoopTaskBase::_delayMicroseconds(uint32_t us) noexcept 586 | { 587 | if (us < DELAYMICROS_THRESHOLD) { 588 | ::delayMicroseconds(us); 589 | return; 590 | } 591 | delays.store(true); 592 | delay_ms = false; 593 | delay_start = micros(); 594 | delay_duration = us; 595 | vTaskSuspend(taskHandle); 596 | } 597 | 598 | void CoopTaskBase::_sleep() noexcept 599 | { 600 | sleeps.store(true); 601 | vTaskSuspend(taskHandle); 602 | } 603 | 604 | void CoopTaskBase::_yield() noexcept 605 | { 606 | delay_duration = 0; 607 | delays.store(false); 608 | vTaskSuspend(taskHandle); 609 | } 610 | 611 | void CoopTaskBase::_exit() noexcept 612 | { 613 | cont = false; 614 | vTaskSuspend(taskHandle); 615 | } 616 | 617 | void IRAM_ATTR CoopTaskBase::sleep(const bool state) noexcept 618 | { 619 | sleeps.store(state); 620 | if (!state) 621 | { 622 | delay_duration = 0; 623 | delays.store(false); 624 | } 625 | } 626 | 627 | CoopTaskBase* CoopTaskBase::self() noexcept 628 | { 629 | const auto currentTaskHandle = xTaskGetCurrentTaskHandle(); 630 | auto cur = current; 631 | if (cur && currentTaskHandle == cur->taskHandle) return cur; 632 | for (size_t i = 0; i < runnableTasks.size(); ++i) 633 | { 634 | cur = runnableTasks[i].load(); 635 | if (cur && currentTaskHandle == cur->taskHandle) return cur; 636 | } 637 | return nullptr; 638 | } 639 | 640 | #else 641 | 642 | jmp_buf CoopTaskBase::env; 643 | 644 | CoopTaskBase::~CoopTaskBase() 645 | { 646 | delistRunnable(); 647 | } 648 | 649 | int32_t CoopTaskBase::initialize() 650 | { 651 | if (!cont || init) return -1; 652 | init = true; 653 | // fill stack with magic values to check overflow, corruption, and high water mark 654 | for (size_t pos = 0; pos <= (taskStackSize + (FULLFEATURES ? sizeof(STACKCOOKIE) : 0)) / sizeof(STACKCOOKIE); ++pos) 655 | { 656 | reinterpret_cast(taskStackTop)[pos] = STACKCOOKIE; 657 | } 658 | #if defined(__GNUC__) && (defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)) 659 | asm volatile ( 660 | "movq %0, %%rsp" 661 | : 662 | : "r" (((reinterpret_cast(taskStackTop) + taskStackSize + (FULLFEATURES ? sizeof(STACKCOOKIE) : 0)) >> 4) << 4) 663 | ); 664 | #elif defined(ARDUINO) || defined(__GNUC__) 665 | char* bp = static_cast(alloca( 666 | reinterpret_cast(&bp) - reinterpret_cast(taskStackTop) - (taskStackSize + (FULLFEATURES ? sizeof(STACKCOOKIE) : 0)) 667 | )); 668 | std::atomic_thread_fence(std::memory_order_release); 669 | #else 670 | #error Setting stack pointer is not implemented on this target 671 | #endif 672 | func(); 673 | self()->_exit(); 674 | cont = false; 675 | delistRunnable(); 676 | return -1; 677 | } 678 | 679 | int32_t CoopTaskBase::run() 680 | { 681 | if (!cont) return -1; 682 | if (sleeps.load()) return 0; 683 | if (delays.load()) 684 | { 685 | if (delay_ms) 686 | { 687 | #if defined(ESP8266) || defined(ESP32) 688 | uint32_t expired; 689 | #ifdef ESP8266 690 | if (usingBuiltinScheduler) 691 | { 692 | expired = millis() - delay_start; 693 | } 694 | else 695 | #endif 696 | { 697 | expired = (ESP.getCycleCount() - delay_start) / CYCLES_PER_MS; 698 | while (expired && delay_duration) 699 | { 700 | delay_start += CYCLES_PER_MS; 701 | --delay_duration; 702 | --expired; 703 | } 704 | } 705 | #else 706 | auto expired = millis() - delay_start; 707 | #endif 708 | if (expired < delay_duration) 709 | { 710 | auto delay_rem = delay_duration - expired; 711 | return static_cast(delay_rem) < 0 ? DELAY_MAXINT : delay_rem; 712 | } 713 | } 714 | else 715 | { 716 | auto expired = micros() - delay_start; 717 | if (expired < delay_duration) 718 | { 719 | auto delay_rem = delay_duration - expired; 720 | if (delay_rem >= DELAYMICROS_THRESHOLD) 721 | { 722 | return static_cast(delay_rem) < 0 ? DELAY_MAXINT : delay_rem; 723 | } 724 | ::delayMicroseconds(delay_rem); 725 | } 726 | } 727 | delays.store(false); 728 | delay_duration = 0; 729 | } 730 | auto val = setjmp(env); 731 | // val = 0: init; -1: exit() task; 1: yield task; 2: sleep task; 3: delay task for delay_duration 732 | if (!val) { 733 | current = this; 734 | if (!init) return initialize(); 735 | if (FULLFEATURES && *reinterpret_cast(taskStackTop + taskStackSize + sizeof(STACKCOOKIE)) != STACKCOOKIE) 736 | { 737 | #if !defined(ARDUINO_attiny) 738 | ::printf(PSTR("FATAL ERROR: CoopTask %s stack corrupted\n"), name().c_str()); 739 | #endif 740 | ::abort(); 741 | } 742 | 743 | longjmp(env_yield, 1); 744 | } 745 | else 746 | { 747 | current = nullptr; 748 | if (*reinterpret_cast(taskStackTop) != STACKCOOKIE) 749 | { 750 | #if !defined(ARDUINO_attiny) 751 | ::printf(PSTR("FATAL ERROR: CoopTask %s stack overflow\n"), name().c_str()); 752 | #endif 753 | ::abort(); 754 | } 755 | cont = cont && (val > 0); 756 | sleeps.store(sleeps.load() || (val == 2)); 757 | delays.store(delays.load() || (val > 2)); 758 | } 759 | if (!cont) { 760 | delistRunnable(); 761 | return -1; 762 | } 763 | switch (val) 764 | { 765 | case 1: 766 | case 2: 767 | return 0; 768 | break; 769 | case 3: 770 | default: 771 | return static_cast(delay_duration) < 0 ? DELAY_MAXINT : delay_duration; 772 | break; 773 | } 774 | } 775 | 776 | void CoopTaskBase::dumpStack() const 777 | { 778 | if (!taskStackTop) return; 779 | size_t pos; 780 | for (pos = 1; pos < (taskStackSize + (FULLFEATURES ? sizeof(STACKCOOKIE) : 0)) / sizeof(STACKCOOKIE); ++pos) 781 | { 782 | if (STACKCOOKIE != reinterpret_cast(taskStackTop)[pos]) 783 | break; 784 | } 785 | #if !defined(ARDUINO_attiny) 786 | ::printf(PSTR(">>>stack>>>\n")); 787 | #endif 788 | while (pos < (taskStackSize + (FULLFEATURES ? sizeof(STACKCOOKIE) : 0)) / sizeof(STACKCOOKIE)) 789 | { 790 | #if !defined(ARDUINO_attiny) 791 | auto* sp = &reinterpret_cast(taskStackTop)[pos]; 792 | 793 | // rough indicator: stack frames usually have SP saved as the second word 794 | bool looksLikeStackFrame = (sp[2] == reinterpret_cast(&sp[4])); 795 | ::printf(PSTR("%08x: %08x %08x %08x %08x %c\n"), 796 | reinterpret_cast(sp), sp[0], sp[1], sp[2], sp[3], looksLikeStackFrame ? '<' : ' '); 797 | #endif 798 | 799 | pos += 4; 800 | } 801 | #if !defined(ARDUINO_attiny) 802 | ::printf(PSTR("<<(taskStackTop)[pos]) 813 | break; 814 | } 815 | return (pos - 1) * sizeof(unsigned); 816 | } 817 | 818 | void CoopTaskBase::doYield(unsigned val) noexcept 819 | { 820 | if (!setjmp(env_yield)) 821 | { 822 | longjmp(env, val); 823 | } 824 | } 825 | 826 | void CoopTaskBase::_delay(uint32_t ms) noexcept 827 | { 828 | delay_ms = true; 829 | #ifdef ESP8266 830 | delay_start = usingBuiltinScheduler ? millis() : ESP.getCycleCount(); 831 | #elif ESP32 832 | delay_start = ESP.getCycleCount(); 833 | #else 834 | delay_start = millis(); 835 | #endif 836 | delay_duration = ms; 837 | // CoopTask::run() defers task for delay_duration milliseconds. 838 | doYield(3); 839 | } 840 | 841 | void CoopTaskBase::_delayMicroseconds(uint32_t us) noexcept 842 | { 843 | if (us < DELAYMICROS_THRESHOLD) { 844 | ::delayMicroseconds(us); 845 | return; 846 | } 847 | delay_ms = false; 848 | delay_start = micros(); 849 | delay_duration = us; 850 | // CoopTask::run() defers task for delay_duration microseconds. 851 | doYield(3); 852 | } 853 | 854 | void CoopTaskBase::_exit() noexcept 855 | { 856 | longjmp(env, -1); 857 | } 858 | 859 | void CoopTaskBase::_yield() noexcept 860 | { 861 | doYield(1); 862 | } 863 | 864 | void CoopTaskBase::_sleep() noexcept 865 | { 866 | doYield(2); 867 | } 868 | 869 | void IRAM_ATTR CoopTaskBase::sleep(const bool state) noexcept 870 | { 871 | sleeps.store(state); 872 | if (!state) 873 | { 874 | delays.store(false); 875 | delay_duration = 0; 876 | } 877 | } 878 | 879 | #endif // _MSC_VER 880 | 881 | void runCoopTasks(const Delegate& reaper, 882 | const Delegate& onDelay, const Delegate& onSleep) 883 | { 884 | #ifdef ESP32_FREERTOS 885 | static TaskHandle_t yieldGuardHandle = nullptr; 886 | if (!yieldGuardHandle) 887 | { 888 | xTaskCreateUniversal([](void*) 889 | { 890 | for (;;) 891 | { 892 | vPortYield(); 893 | } 894 | }, "YieldGuard", 0x200, nullptr, 1, &yieldGuardHandle, CONFIG_ARDUINO_RUNNING_CORE); 895 | } 896 | #endif 897 | 898 | auto taskCount = CoopTaskBase::getRunnableTasksCount(); 899 | bool allSleeping = true; 900 | uint32_t minDelay_ms = ~(decltype(minDelay_ms))0U; 901 | for (size_t i = 0; taskCount && i < CoopTaskBase::getRunnableTasks().size(); ++i) 902 | { 903 | #if defined(ESP8266) || defined(ESP32) 904 | optimistic_yield(10000); 905 | #endif 906 | auto task = CoopTaskBase::getRunnableTasks()[i].load(); 907 | if (task) 908 | { 909 | --taskCount; 910 | auto runResult = task->run(); 911 | if (runResult < 0 && reaper) 912 | reaper(task); 913 | else if (minDelay_ms) 914 | { 915 | if (task->delayed()) 916 | { 917 | allSleeping = false; 918 | uint32_t delay_ms = task->delayIsMs() ? static_cast(runResult) : static_cast(runResult) / 1000UL; 919 | if (delay_ms < minDelay_ms) 920 | minDelay_ms = delay_ms; 921 | } 922 | else if (!task->sleeping()) 923 | { 924 | allSleeping = false; 925 | minDelay_ms = 0; 926 | } 927 | } 928 | } 929 | } 930 | 931 | bool cleanup = true; 932 | if (allSleeping && onSleep) 933 | { 934 | cleanup = onSleep(); 935 | } 936 | else if (minDelay_ms && onDelay) 937 | { 938 | cleanup = onDelay(minDelay_ms); 939 | } 940 | if (cleanup) 941 | { 942 | #ifdef ESP32_FREERTOS 943 | vTaskSuspend(yieldGuardHandle); 944 | vTaskDelay(1); 945 | vTaskResume(yieldGuardHandle); 946 | #endif 947 | } 948 | } 949 | -------------------------------------------------------------------------------- /src/CoopTaskBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | CoopTaskBase.h - Implementation of cooperative scheduling tasks 3 | Copyright (c) 2019 Dirk O. Kaar. All rights reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef __CoopTaskBase_h 21 | #define __CoopTaskBase_h 22 | 23 | #ifdef ESP32 24 | #define ESP32_FREERTOS 25 | #endif 26 | 27 | #include 28 | #if defined(ESP8266) || defined(ESP32) 29 | #include 30 | #include 31 | #include 32 | #include 33 | #elif defined(ARDUINO) 34 | #include 35 | #include 36 | #elif defined(_MSC_VER) 37 | #include 38 | #include 39 | #include 40 | #else 41 | #include 42 | #include 43 | #include 44 | #endif 45 | 46 | #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) 47 | #include 48 | #else 49 | #include 50 | #endif 51 | 52 | #if !defined(ESP32) && !defined(ESP8266) 53 | #define IRAM_ATTR 54 | #endif 55 | 56 | #if !defined(ARDUINO) 57 | #define PSTR(x) x 58 | #endif 59 | 60 | #ifdef _MSC_VER 61 | #define __attribute__(_) 62 | #endif 63 | 64 | class CoopTaskBase 65 | { 66 | public: 67 | static constexpr bool FULLFEATURES = sizeof(unsigned) >= 4; 68 | 69 | protected: 70 | using taskfunction_t = Delegate< void() >; 71 | 72 | #ifdef ARDUINO 73 | CoopTaskBase(const String& name, taskfunction_t _func, size_t stackSize = DEFAULTTASKSTACKSIZE) : 74 | #else 75 | CoopTaskBase(const std::string& name, taskfunction_t _func, size_t stackSize = DEFAULTTASKSTACKSIZE) : 76 | #endif 77 | taskName(name), sleeps(true), delays(false), func(_func) 78 | { 79 | taskStackSize = (sizeof(unsigned) >= 4) ? ((stackSize + sizeof(unsigned) - 1) / sizeof(unsigned)) * sizeof(unsigned) : stackSize; 80 | } 81 | CoopTaskBase(const CoopTaskBase&) = delete; 82 | CoopTaskBase& operator=(const CoopTaskBase&) = delete; 83 | 84 | static constexpr int32_t DELAYMICROS_THRESHOLD = 50; 85 | static constexpr uint32_t DELAY_MAXINT = (~(uint32_t)0) >> 1; 86 | 87 | #ifdef ARDUINO 88 | const String taskName; 89 | #else 90 | const std::string taskName; 91 | #endif 92 | 93 | size_t taskStackSize; 94 | #if defined(_MSC_VER) 95 | static LPVOID primaryFiber; 96 | LPVOID taskFiber = nullptr; 97 | int val = 0; 98 | static void __stdcall taskFiberFunc(void* self); 99 | #elif defined(ESP32_FREERTOS) 100 | TaskHandle_t taskHandle = nullptr; 101 | static void taskFunc(void* _self); 102 | #else 103 | char* taskStackTop = nullptr; 104 | static jmp_buf env; 105 | jmp_buf env_yield; 106 | #endif 107 | static constexpr size_t MAXNUMBERCOOPTASKS = FULLFEATURES ? 32 : 8; 108 | // for lock-free insertion, must be one element larger than max task count 109 | static std::array< std::atomic, MAXNUMBERCOOPTASKS + 1> runnableTasks; 110 | static std::atomic runnableTasksCount; 111 | static CoopTaskBase* current; 112 | bool init = false; 113 | bool cont = true; 114 | std::atomic sleeps; 115 | // ESP32 FreeRTOS (#define ESP32_FREERTOS) handles delays, on this platfrom delays is always false 116 | std::atomic delays; 117 | 118 | int32_t initialize(); 119 | void doYield(unsigned val) noexcept; 120 | 121 | #if defined(ESP8266) 122 | static bool usingBuiltinScheduler; 123 | bool rescheduleTask(uint32_t repeat_us); 124 | #endif 125 | bool IRAM_ATTR enrollRunnable(); 126 | void delistRunnable(); 127 | 128 | void _exit() noexcept; 129 | void _yield() noexcept; 130 | void _sleep() noexcept; 131 | void _delay(uint32_t ms) noexcept; 132 | void _delayMicroseconds(uint32_t us) noexcept; 133 | 134 | private: 135 | // true: delay_start/delay_duration are in milliseconds; false: delay_start/delay_duration are in microseconds. 136 | bool delay_ms = false; 137 | uint32_t delay_start = 0; 138 | uint32_t delay_duration = 0; 139 | 140 | taskfunction_t func; 141 | 142 | public: 143 | virtual ~CoopTaskBase(); 144 | #if defined(ESP32) 145 | static constexpr size_t MAXSTACKSPACE = 0x2000; 146 | #elif defined(ESP8266) 147 | static constexpr size_t MAXSTACKSPACE = 0x1000; 148 | #elif defined(ARDUINO) 149 | static constexpr size_t MAXSTACKSPACE = FULLFEATURES ? 0x180 : 0xc0; 150 | #else 151 | static constexpr size_t MAXSTACKSPACE = 0x10000; 152 | #endif 153 | static constexpr unsigned STACKCOOKIE = FULLFEATURES ? 0xdeadbeefUL : 0xdeadU; 154 | static constexpr size_t DEFAULTTASKSTACKSIZE = MAXSTACKSPACE - (FULLFEATURES ? 2 : 1) * sizeof(STACKCOOKIE); 155 | 156 | #ifdef ARDUINO 157 | const String& name() const noexcept { return taskName; } 158 | #else 159 | const std::string& name() const noexcept { return taskName; } 160 | #endif 161 | 162 | /// @returns: true if the CoopTask object is ready to run, including stack allocation. 163 | /// false if either initialization has failed, or the task has exited(). 164 | #if !defined(_MSC_VER) && !defined(ESP32_FREERTOS) 165 | operator bool() const noexcept { return cont && taskStackTop; } 166 | /// Prints the task stack, decodable by the ESP exception decoder 167 | void dumpStack() const; 168 | #else 169 | operator bool() const noexcept { return cont; } 170 | #endif 171 | 172 | /// Ready the task for scheduling, by default waking up the task from both sleep and delay. 173 | /// @returns: true on success. 174 | bool IRAM_ATTR scheduleTask(bool wakeup = true); 175 | inline bool IRAM_ATTR wakeup() ALWAYS_INLINE_ATTR { return scheduleTask(true); } 176 | 177 | #ifdef ESP8266 178 | /// For full access to all features, cyclic task scheduling, state evaluation 179 | /// and running are performed explicitly from user code. For convenience, the function 180 | /// runCoopTasks() implements the pattern as best practice. See the CoopTask examples for this. 181 | /// If only a pre-determined number of loop tasks need to run indefinitely 182 | /// without exit codes or explicit deep sleep system states, on platforms where a 183 | /// scheduler exists that is suffiently capable to iteratively run each of these CoopTasks, 184 | /// calling this function switches all task creation and scheduling to using that, obviating 185 | /// the need to call a scheduler explicitly from user code. 186 | /// The scheduler selection should be done before the first CoopTask is created, and not 187 | /// changed thereafter during runtime. 188 | /// By default, builtin schedulers are not used, for well-defined behavior and portability. 189 | /// @param state true: The parameter default value. All subsequent scheduling of tasks is 190 | /// handed to the builtin scheduler. 191 | static void useBuiltinScheduler(bool state = true) 192 | { 193 | usingBuiltinScheduler = state; 194 | } 195 | #endif 196 | /// Every task is entered into this list by scheduleTask(). It is removed when it exits 197 | /// or gets deleted. 198 | static const decltype(runnableTasks)& getRunnableTasks() 199 | { 200 | return runnableTasks; 201 | } 202 | /// @returns: the count of runnable, non-nullptr, tasks in the return of getRunnableTasks(). 203 | static size_t getRunnableTasksCount() 204 | { 205 | return runnableTasksCount.load(); 206 | } 207 | 208 | /// @returns: -1: exited. 0: runnable or sleeping. >0: delayed for milliseconds or microseconds, check delayIsMs(). 209 | int32_t run(); 210 | 211 | /// @returns: size of unused stack space. 0 if stack is not allocated yet or was deleted after task exited. 212 | size_t getFreeStack() const; 213 | 214 | bool delayIsMs() const noexcept { return delay_ms; } 215 | 216 | /// Modifies the sleep flag. if called from a running task, it is not immediately suspended. 217 | /// @param state true: a suspended task becomes sleeping, if call from the running task, 218 | /// the next call to yield() or delay() puts it into sleeping state. 219 | /// false: clears the sleeping and delay state of the task. 220 | void IRAM_ATTR sleep(const bool state) noexcept; 221 | 222 | #ifdef ESP32_FREERTOS 223 | /// @returns: a pointer to the CoopTask instance that is running. nullptr if not called from a CoopTask function (running() == false). 224 | static CoopTaskBase* self() noexcept; 225 | #else 226 | /// @returns: a pointer to the CoopTask instance that is running. nullptr if not called from a CoopTask function (running() == false). 227 | static CoopTaskBase* self() noexcept { return current; } 228 | #endif 229 | /// @returns: true if called from the task function of a CoopTask, false otherwise. 230 | static bool running() noexcept { return self(); } 231 | 232 | /// @returns: true if the task's is set to sleep. 233 | /// For a non-running task, this implies it is also currently not scheduled. 234 | inline bool IRAM_ATTR sleeping() const noexcept ALWAYS_INLINE_ATTR { return sleeps.load(); } 235 | inline bool IRAM_ATTR delayed() const noexcept ALWAYS_INLINE_ATTR { return delays.load(); } 236 | inline bool IRAM_ATTR suspended() const noexcept ALWAYS_INLINE_ATTR { return sleeps.load() || delays.load(); } 237 | 238 | /// use only in running CoopTask function. As stack unwinding is corrupted 239 | /// by exit(), which among other issues breaks the RAII idiom, 240 | /// using regular return or exceptions is to be preferred in most cases. 241 | static void exit() noexcept { self()->_exit(); } 242 | /// use only in running CoopTask function. 243 | static void yield() noexcept { self()->_yield(); } 244 | static void yield(CoopTaskBase* self) noexcept { self->_yield(); } 245 | /// use only in running CoopTask function. 246 | static void sleep() noexcept { self()->_sleep(); } 247 | /// use only in running CoopTask function. 248 | static void delay(uint32_t ms) noexcept { self()->_delay(ms); } 249 | static void delay(CoopTaskBase* self, uint32_t ms) noexcept { self->_delay(ms); } 250 | /// use only in running CoopTask function. 251 | static void delayMicroseconds(uint32_t us) noexcept { self()->_delayMicroseconds(us); } 252 | }; 253 | 254 | #ifndef ARDUINO 255 | inline void yield() { CoopTaskBase::yield(); } 256 | inline void delay(uint32_t ms) { CoopTaskBase::delay(ms); } 257 | #endif 258 | 259 | /// An optional convenience funtion that does all the work to cyclically perform CoopTask execution. 260 | /// @param reaper An optional function that is called once when a task exits. 261 | /// @param onDelay An optional function to handle a global delay greater or equal 1 millisecond, resulting 262 | /// from the minimum time interval for which at this time all CoopTasks are delayed. 263 | /// This can be used for power saving, if wake up by asynchronous events is properly considered. 264 | /// If onSleep is not set, onDelay() is called instead with the maximum value for the ms delay parameter. 265 | /// onDelay() must return a bool value, if true, runCoopTasks performs the default housekeeping actions, 266 | /// otherwise it skips those. 267 | /// @param onSleep An optional function indicating that all CoopTasks are sleeping, that is, are infinitely delayed. 268 | /// This can be used for power saving modes. 269 | /// onSleep(), like onDelay(), must return a bool value, if true, runCoopTasks performs the 270 | /// default housekeeping actions, otherwise it skips those. 271 | void runCoopTasks(const Delegate& reaper = nullptr, 272 | const Delegate& onDelay = nullptr, const Delegate& onSleep = nullptr); 273 | 274 | #endif // __CoopTaskBase_h 275 | --------------------------------------------------------------------------------