1*03ce13f7SAndroid Build Coastguard Worker# Marl 2*03ce13f7SAndroid Build Coastguard Worker 3*03ce13f7SAndroid Build Coastguard WorkerMarl is a hybrid thread / fiber task scheduler written in C++ 11. 4*03ce13f7SAndroid Build Coastguard Worker 5*03ce13f7SAndroid Build Coastguard Worker## About 6*03ce13f7SAndroid Build Coastguard Worker 7*03ce13f7SAndroid Build Coastguard WorkerMarl is a C++ 11 library that provides a fluent interface for running tasks across a number of threads. 8*03ce13f7SAndroid Build Coastguard Worker 9*03ce13f7SAndroid Build Coastguard WorkerMarl uses a combination of fibers and threads to allow efficient execution of tasks that can block, while keeping a fixed number of hardware threads. 10*03ce13f7SAndroid Build Coastguard Worker 11*03ce13f7SAndroid Build Coastguard WorkerMarl supports Windows, macOS, Linux, FreeBSD, Fuchsia, Emscripten, Android and iOS (arm, aarch64, loongarch64, mips64, ppc64, rv64, x86 and x64). 12*03ce13f7SAndroid Build Coastguard Worker 13*03ce13f7SAndroid Build Coastguard WorkerMarl has no dependencies on other libraries (with an exception on googletest for building the optional unit tests). 14*03ce13f7SAndroid Build Coastguard Worker 15*03ce13f7SAndroid Build Coastguard WorkerExample: 16*03ce13f7SAndroid Build Coastguard Worker 17*03ce13f7SAndroid Build Coastguard Worker```cpp 18*03ce13f7SAndroid Build Coastguard Worker#include "marl/defer.h" 19*03ce13f7SAndroid Build Coastguard Worker#include "marl/event.h" 20*03ce13f7SAndroid Build Coastguard Worker#include "marl/scheduler.h" 21*03ce13f7SAndroid Build Coastguard Worker#include "marl/waitgroup.h" 22*03ce13f7SAndroid Build Coastguard Worker 23*03ce13f7SAndroid Build Coastguard Worker#include <cstdio> 24*03ce13f7SAndroid Build Coastguard Worker 25*03ce13f7SAndroid Build Coastguard Workerint main() { 26*03ce13f7SAndroid Build Coastguard Worker // Create a marl scheduler using all the logical processors available to the process. 27*03ce13f7SAndroid Build Coastguard Worker // Bind this scheduler to the main thread so we can call marl::schedule() 28*03ce13f7SAndroid Build Coastguard Worker marl::Scheduler scheduler(marl::Scheduler::Config::allCores()); 29*03ce13f7SAndroid Build Coastguard Worker scheduler.bind(); 30*03ce13f7SAndroid Build Coastguard Worker defer(scheduler.unbind()); // Automatically unbind before returning. 31*03ce13f7SAndroid Build Coastguard Worker 32*03ce13f7SAndroid Build Coastguard Worker constexpr int numTasks = 10; 33*03ce13f7SAndroid Build Coastguard Worker 34*03ce13f7SAndroid Build Coastguard Worker // Create an event that is manually reset. 35*03ce13f7SAndroid Build Coastguard Worker marl::Event sayHello(marl::Event::Mode::Manual); 36*03ce13f7SAndroid Build Coastguard Worker 37*03ce13f7SAndroid Build Coastguard Worker // Create a WaitGroup with an initial count of numTasks. 38*03ce13f7SAndroid Build Coastguard Worker marl::WaitGroup saidHello(numTasks); 39*03ce13f7SAndroid Build Coastguard Worker 40*03ce13f7SAndroid Build Coastguard Worker // Schedule some tasks to run asynchronously. 41*03ce13f7SAndroid Build Coastguard Worker for (int i = 0; i < numTasks; i++) { 42*03ce13f7SAndroid Build Coastguard Worker // Each task will run on one of the 4 worker threads. 43*03ce13f7SAndroid Build Coastguard Worker marl::schedule([=] { // All marl primitives are capture-by-value. 44*03ce13f7SAndroid Build Coastguard Worker // Decrement the WaitGroup counter when the task has finished. 45*03ce13f7SAndroid Build Coastguard Worker defer(saidHello.done()); 46*03ce13f7SAndroid Build Coastguard Worker 47*03ce13f7SAndroid Build Coastguard Worker printf("Task %d waiting to say hello...\n", i); 48*03ce13f7SAndroid Build Coastguard Worker 49*03ce13f7SAndroid Build Coastguard Worker // Blocking in a task? 50*03ce13f7SAndroid Build Coastguard Worker // The scheduler will find something else for this thread to do. 51*03ce13f7SAndroid Build Coastguard Worker sayHello.wait(); 52*03ce13f7SAndroid Build Coastguard Worker 53*03ce13f7SAndroid Build Coastguard Worker printf("Hello from task %d!\n", i); 54*03ce13f7SAndroid Build Coastguard Worker }); 55*03ce13f7SAndroid Build Coastguard Worker } 56*03ce13f7SAndroid Build Coastguard Worker 57*03ce13f7SAndroid Build Coastguard Worker sayHello.signal(); // Unblock all the tasks. 58*03ce13f7SAndroid Build Coastguard Worker 59*03ce13f7SAndroid Build Coastguard Worker saidHello.wait(); // Wait for all tasks to complete. 60*03ce13f7SAndroid Build Coastguard Worker 61*03ce13f7SAndroid Build Coastguard Worker printf("All tasks said hello.\n"); 62*03ce13f7SAndroid Build Coastguard Worker 63*03ce13f7SAndroid Build Coastguard Worker // All tasks are guaranteed to complete before the scheduler is destructed. 64*03ce13f7SAndroid Build Coastguard Worker} 65*03ce13f7SAndroid Build Coastguard Worker``` 66*03ce13f7SAndroid Build Coastguard Worker 67*03ce13f7SAndroid Build Coastguard Worker## Benchmarks 68*03ce13f7SAndroid Build Coastguard Worker 69*03ce13f7SAndroid Build Coastguard WorkerGraphs of several microbenchmarks can be found [here](https://google.github.io/marl/benchmarks). 70*03ce13f7SAndroid Build Coastguard Worker 71*03ce13f7SAndroid Build Coastguard Worker## Building 72*03ce13f7SAndroid Build Coastguard Worker 73*03ce13f7SAndroid Build Coastguard WorkerMarl contains many unit tests and examples that can be built using CMake. 74*03ce13f7SAndroid Build Coastguard Worker 75*03ce13f7SAndroid Build Coastguard WorkerUnit tests require fetching the `googletest` external project, which can be done by typing the following in your terminal: 76*03ce13f7SAndroid Build Coastguard Worker 77*03ce13f7SAndroid Build Coastguard Worker```bash 78*03ce13f7SAndroid Build Coastguard Workercd <path-to-marl> 79*03ce13f7SAndroid Build Coastguard Workergit submodule update --init 80*03ce13f7SAndroid Build Coastguard Worker``` 81*03ce13f7SAndroid Build Coastguard Worker 82*03ce13f7SAndroid Build Coastguard Worker### Linux and macOS 83*03ce13f7SAndroid Build Coastguard Worker 84*03ce13f7SAndroid Build Coastguard WorkerTo build the unit tests and examples, type the following in your terminal: 85*03ce13f7SAndroid Build Coastguard Worker 86*03ce13f7SAndroid Build Coastguard Worker```bash 87*03ce13f7SAndroid Build Coastguard Workercd <path-to-marl> 88*03ce13f7SAndroid Build Coastguard Workermkdir build 89*03ce13f7SAndroid Build Coastguard Workercd build 90*03ce13f7SAndroid Build Coastguard Workercmake .. -DMARL_BUILD_EXAMPLES=1 -DMARL_BUILD_TESTS=1 91*03ce13f7SAndroid Build Coastguard Workermake 92*03ce13f7SAndroid Build Coastguard Worker``` 93*03ce13f7SAndroid Build Coastguard Worker 94*03ce13f7SAndroid Build Coastguard WorkerThe resulting binaries will be found in `<path-to-marl>/build` 95*03ce13f7SAndroid Build Coastguard Worker 96*03ce13f7SAndroid Build Coastguard Worker### Emscripten 97*03ce13f7SAndroid Build Coastguard Worker 98*03ce13f7SAndroid Build Coastguard Worker1. install and activate the emscripten sdk following [standard instructions for your platform](https://emscripten.org/docs/getting_started/downloads.html). 99*03ce13f7SAndroid Build Coastguard Worker2. build an example from the examples folder using emscripten, say `hello_task`. 100*03ce13f7SAndroid Build Coastguard Worker```bash 101*03ce13f7SAndroid Build Coastguard Workercd <path-to-marl> 102*03ce13f7SAndroid Build Coastguard Workermkdir build 103*03ce13f7SAndroid Build Coastguard Workercd build 104*03ce13f7SAndroid Build Coastguard Workeremcmake cmake .. -DMARL_BUILD_EXAMPLES=1 105*03ce13f7SAndroid Build Coastguard Workermake hello_task -j 8 106*03ce13f7SAndroid Build Coastguard Worker``` 107*03ce13f7SAndroid Build Coastguard WorkerNOTE: you want to change the value of the linker flag `sPTHREAD_POOL_SIZE` that must be at least as large as the number of threads used by your application. 108*03ce13f7SAndroid Build Coastguard Worker3. Test the emscripten output. 109*03ce13f7SAndroid Build Coastguard WorkerYou can use the provided python script to create a local web server: 110*03ce13f7SAndroid Build Coastguard Worker```bash 111*03ce13f7SAndroid Build Coastguard Worker../run_webserver 112*03ce13f7SAndroid Build Coastguard Worker``` 113*03ce13f7SAndroid Build Coastguard WorkerIn your browser, navigate to the example URL: [http://127.0.0.1:8080/hello_task.html](http://127.0.0.1:8080/hello_task.html). 114*03ce13f7SAndroid Build Coastguard WorkerVoilà - you should see the log output appear on the web page. 115*03ce13f7SAndroid Build Coastguard Worker 116*03ce13f7SAndroid Build Coastguard Worker### Installing Marl (vcpkg) 117*03ce13f7SAndroid Build Coastguard Worker 118*03ce13f7SAndroid Build Coastguard WorkerAlternatively, you can build and install Marl using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: 119*03ce13f7SAndroid Build Coastguard Worker 120*03ce13f7SAndroid Build Coastguard Worker```bash or powershell 121*03ce13f7SAndroid Build Coastguard Workergit clone https://github.com/Microsoft/vcpkg.git 122*03ce13f7SAndroid Build Coastguard Workercd vcpkg 123*03ce13f7SAndroid Build Coastguard Worker./bootstrap-vcpkg.sh 124*03ce13f7SAndroid Build Coastguard Worker./vcpkg integrate install 125*03ce13f7SAndroid Build Coastguard Worker./vcpkg install marl 126*03ce13f7SAndroid Build Coastguard Worker``` 127*03ce13f7SAndroid Build Coastguard Worker 128*03ce13f7SAndroid Build Coastguard WorkerThe Marl port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. 129*03ce13f7SAndroid Build Coastguard Worker 130*03ce13f7SAndroid Build Coastguard Worker### Windows 131*03ce13f7SAndroid Build Coastguard Worker 132*03ce13f7SAndroid Build Coastguard WorkerMarl can be built using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019). 133*03ce13f7SAndroid Build Coastguard Worker 134*03ce13f7SAndroid Build Coastguard Worker### Using Marl in your CMake project 135*03ce13f7SAndroid Build Coastguard Worker 136*03ce13f7SAndroid Build Coastguard WorkerYou can build and link Marl using `add_subdirectory()` in your project's `CMakeLists.txt` file: 137*03ce13f7SAndroid Build Coastguard Worker 138*03ce13f7SAndroid Build Coastguard Worker```cmake 139*03ce13f7SAndroid Build Coastguard Workerset(MARL_DIR <path-to-marl>) # example <path-to-marl>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/marl" 140*03ce13f7SAndroid Build Coastguard Workeradd_subdirectory(${MARL_DIR}) 141*03ce13f7SAndroid Build Coastguard Worker``` 142*03ce13f7SAndroid Build Coastguard Worker 143*03ce13f7SAndroid Build Coastguard WorkerThis will define the `marl` library target, which you can pass to `target_link_libraries()`: 144*03ce13f7SAndroid Build Coastguard Worker 145*03ce13f7SAndroid Build Coastguard Worker```cmake 146*03ce13f7SAndroid Build Coastguard Workertarget_link_libraries(<target> marl) # replace <target> with the name of your project's target 147*03ce13f7SAndroid Build Coastguard Worker``` 148*03ce13f7SAndroid Build Coastguard Worker 149*03ce13f7SAndroid Build Coastguard WorkerYou may also wish to specify your own paths to the third party libraries used by `marl`. 150*03ce13f7SAndroid Build Coastguard WorkerYou can do this by setting any of the following variables before the call to `add_subdirectory()`: 151*03ce13f7SAndroid Build Coastguard Worker 152*03ce13f7SAndroid Build Coastguard Worker```cmake 153*03ce13f7SAndroid Build Coastguard Workerset(MARL_THIRD_PARTY_DIR <third-party-root-directory>) # defaults to ${MARL_DIR}/third_party 154*03ce13f7SAndroid Build Coastguard Workerset(MARL_GOOGLETEST_DIR <path-to-googletest>) # defaults to ${MARL_THIRD_PARTY_DIR}/googletest 155*03ce13f7SAndroid Build Coastguard Workeradd_subdirectory(${MARL_DIR}) 156*03ce13f7SAndroid Build Coastguard Worker``` 157*03ce13f7SAndroid Build Coastguard Worker 158*03ce13f7SAndroid Build Coastguard Worker### Usage Recommendations 159*03ce13f7SAndroid Build Coastguard Worker 160*03ce13f7SAndroid Build Coastguard Worker#### Capture marl synchronization primitives by value 161*03ce13f7SAndroid Build Coastguard Worker 162*03ce13f7SAndroid Build Coastguard WorkerAll marl synchronization primitives aside from `marl::ConditionVariable` should be lambda-captured by **value**: 163*03ce13f7SAndroid Build Coastguard Worker 164*03ce13f7SAndroid Build Coastguard Worker```c++ 165*03ce13f7SAndroid Build Coastguard Workermarl::Event event; 166*03ce13f7SAndroid Build Coastguard Workermarl::schedule([=]{ // [=] Good, [&] Bad. 167*03ce13f7SAndroid Build Coastguard Worker event.signal(); 168*03ce13f7SAndroid Build Coastguard Worker}) 169*03ce13f7SAndroid Build Coastguard Worker``` 170*03ce13f7SAndroid Build Coastguard Worker 171*03ce13f7SAndroid Build Coastguard WorkerInternally, these primitives hold a shared pointer to the primitive state. By capturing by value we avoid common issues where the primitive may be destructed before the last reference is used. 172*03ce13f7SAndroid Build Coastguard Worker 173*03ce13f7SAndroid Build Coastguard Worker#### Create one instance of `marl::Scheduler`, use it for the lifetime of the process 174*03ce13f7SAndroid Build Coastguard Worker 175*03ce13f7SAndroid Build Coastguard WorkerThe `marl::Scheduler` constructor can be expensive as it may spawn a number of hardware threads. \ 176*03ce13f7SAndroid Build Coastguard WorkerDestructing the `marl::Scheduler` requires waiting on all tasks to complete. 177*03ce13f7SAndroid Build Coastguard Worker 178*03ce13f7SAndroid Build Coastguard WorkerMultiple `marl::Scheduler`s may fight each other for hardware thread utilization. 179*03ce13f7SAndroid Build Coastguard Worker 180*03ce13f7SAndroid Build Coastguard WorkerFor these reasons, it is recommended to create a single `marl::Scheduler` for the lifetime of your process. 181*03ce13f7SAndroid Build Coastguard Worker 182*03ce13f7SAndroid Build Coastguard WorkerFor example: 183*03ce13f7SAndroid Build Coastguard Worker 184*03ce13f7SAndroid Build Coastguard Worker```c++ 185*03ce13f7SAndroid Build Coastguard Workerint main() { 186*03ce13f7SAndroid Build Coastguard Worker marl::Scheduler scheduler(marl::Scheduler::Config::allCores()); 187*03ce13f7SAndroid Build Coastguard Worker scheduler.bind(); 188*03ce13f7SAndroid Build Coastguard Worker defer(scheduler.unbind()); 189*03ce13f7SAndroid Build Coastguard Worker 190*03ce13f7SAndroid Build Coastguard Worker return do_program_stuff(); 191*03ce13f7SAndroid Build Coastguard Worker} 192*03ce13f7SAndroid Build Coastguard Worker``` 193*03ce13f7SAndroid Build Coastguard Worker 194*03ce13f7SAndroid Build Coastguard Worker#### Bind the scheduler to externally created threads 195*03ce13f7SAndroid Build Coastguard Worker 196*03ce13f7SAndroid Build Coastguard WorkerIn order to call `marl::schedule()` the scheduler must be bound to the calling thread. Failure to bind the scheduler to the thread before calling `marl::schedule()` will result in undefined behavior. 197*03ce13f7SAndroid Build Coastguard Worker 198*03ce13f7SAndroid Build Coastguard Worker`marl::Scheduler` may be simultaneously bound to any number of threads, and the scheduler can be retrieved from a bound thread with `marl::Scheduler::get()`. 199*03ce13f7SAndroid Build Coastguard Worker 200*03ce13f7SAndroid Build Coastguard WorkerA typical way to pass the scheduler from one thread to another would be: 201*03ce13f7SAndroid Build Coastguard Worker 202*03ce13f7SAndroid Build Coastguard Worker```c++ 203*03ce13f7SAndroid Build Coastguard Workerstd::thread spawn_new_thread() { 204*03ce13f7SAndroid Build Coastguard Worker // Grab the scheduler from the currently running thread. 205*03ce13f7SAndroid Build Coastguard Worker marl::Scheduler* scheduler = marl::Scheduler::get(); 206*03ce13f7SAndroid Build Coastguard Worker 207*03ce13f7SAndroid Build Coastguard Worker // Spawn the new thread. 208*03ce13f7SAndroid Build Coastguard Worker return std::thread([=] { 209*03ce13f7SAndroid Build Coastguard Worker // Bind the scheduler to the new thread. 210*03ce13f7SAndroid Build Coastguard Worker scheduler->bind(); 211*03ce13f7SAndroid Build Coastguard Worker defer(scheduler->unbind()); 212*03ce13f7SAndroid Build Coastguard Worker 213*03ce13f7SAndroid Build Coastguard Worker // You can now safely call `marl::schedule()` 214*03ce13f7SAndroid Build Coastguard Worker run_thread_logic(); 215*03ce13f7SAndroid Build Coastguard Worker }); 216*03ce13f7SAndroid Build Coastguard Worker} 217*03ce13f7SAndroid Build Coastguard Worker 218*03ce13f7SAndroid Build Coastguard Worker``` 219*03ce13f7SAndroid Build Coastguard Worker 220*03ce13f7SAndroid Build Coastguard WorkerAlways remember to unbind the scheduler before terminating the thread. Forgetting to unbind will result in the `marl::Scheduler` destructor blocking indefinitely. 221*03ce13f7SAndroid Build Coastguard Worker 222*03ce13f7SAndroid Build Coastguard Worker#### Don't use externally blocking calls in marl tasks 223*03ce13f7SAndroid Build Coastguard Worker 224*03ce13f7SAndroid Build Coastguard WorkerThe `marl::Scheduler` internally holds a number of worker threads which will execute the scheduled tasks. If a marl task becomes blocked on a marl synchronization primitive, marl can yield from the blocked task and continue execution of other scheduled tasks. 225*03ce13f7SAndroid Build Coastguard Worker 226*03ce13f7SAndroid Build Coastguard WorkerCalling a non-marl blocking function on a marl worker thread will prevent that worker thread from being able to switch to execute other tasks until the blocking function has returned. Examples of these non-marl blocking functions include: [`std::mutex::lock()`](https://en.cppreference.com/w/cpp/thread/mutex/lock), [`std::condition_variable::wait()`](https://en.cppreference.com/w/cpp/thread/condition_variable/wait), [`accept()`](http://man7.org/linux/man-pages/man2/accept.2.html). 227*03ce13f7SAndroid Build Coastguard Worker 228*03ce13f7SAndroid Build Coastguard WorkerShort blocking calls are acceptable, such as a mutex lock to access a data structure. However be careful that you do not use a marl blocking call with a `std::mutex` lock held - the marl task may yield with the lock held, and block other tasks from re-locking the mutex. This sort of situation may end up with a deadlock. 229*03ce13f7SAndroid Build Coastguard Worker 230*03ce13f7SAndroid Build Coastguard WorkerIf you need to make a blocking call from a marl worker thread, you may wish to use [`marl::blocking_call()`](https://github.com/google/marl/blob/main/include/marl/blockingcall.h), which will spawn a new thread for performing the call, allowing the marl worker to continue processing other scheduled tasks. 231*03ce13f7SAndroid Build Coastguard Worker 232*03ce13f7SAndroid Build Coastguard Worker--- 233*03ce13f7SAndroid Build Coastguard Worker 234*03ce13f7SAndroid Build Coastguard WorkerNote: This is not an officially supported Google product 235