├── examples ├── msbuild.bat ├── mem_check.h ├── example1.cpp ├── example3.cpp ├── example2.cpp ├── Makefile ├── example4.cpp ├── example5.cpp ├── example6.cpp └── example7.cpp ├── README.md ├── .travis.yml ├── .appveyor.yml └── LICENSE /examples/msbuild.bat: -------------------------------------------------------------------------------- 1 | cl example1.cpp 2 | cl example2.cpp 3 | cl example3.cpp 4 | cl example4.cpp 5 | cl example5.cpp 6 | cl example6.cpp 7 | cl example7.cpp 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *WARNING*: Project Moved!! 2 | 3 | I moved this project here [px/px_sched.h](https://github.com/pplux/px/blob/master/px_sched.h) to be part of a larger collection of single header libraries [px](https://github.com/pplux/px) , please update your references/links... 4 | 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | matrix: 3 | include: 4 | - compiler: gcc 5 | os: linux 6 | - compiler: clang 7 | os: osx 8 | 9 | script: 10 | - cd examples; make 11 | 12 | branches: 13 | only: 14 | - master 15 | 16 | notifications: 17 | email: false 18 | -------------------------------------------------------------------------------- /examples/mem_check.h: -------------------------------------------------------------------------------- 1 | size_t GLOBAL_amount_alloc = 0; 2 | size_t GLOBAL_amount_dealloc = 0; 3 | 4 | void *mem_check_alloc(size_t s) { 5 | size_t *ptr = static_cast(malloc(sizeof(size_t)+s)); 6 | GLOBAL_amount_alloc += s; 7 | *ptr = s; 8 | return ptr+1; 9 | } 10 | 11 | void mem_check_free(void *raw_ptr) { 12 | size_t *ptr = static_cast(raw_ptr); 13 | GLOBAL_amount_dealloc += ptr[-1]; 14 | free(ptr-1); 15 | } 16 | 17 | void mem_report() { 18 | printf("Total memory allocated: %zu\n", GLOBAL_amount_alloc); 19 | printf("Total memory freed: %zu\n", GLOBAL_amount_dealloc); 20 | if (GLOBAL_amount_alloc != GLOBAL_amount_dealloc) abort(); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /examples/example1.cpp: -------------------------------------------------------------------------------- 1 | // Example-1: 2 | // launch N tasks in parallel, wait for all of them to finish 3 | 4 | #define PX_SCHED_IMPLEMENTATION 1 5 | #include "../px_sched.h" 6 | 7 | int main(int, char **) { 8 | px_sched::Scheduler schd; 9 | schd.init(); 10 | 11 | // (a) Sync objects can be used later to wait for them 12 | px_sched::Sync s; 13 | for(size_t i = 0; i < 128; ++i) { 14 | auto job = [i] { 15 | printf("Task %zu completed from %s\n", 16 | i, px_sched::Scheduler::current_thread_name()); 17 | }; 18 | // run a task, and notify to the given sync object 19 | schd.run(job, &s); 20 | } 21 | 22 | printf("Waiting for tasks to finish...\n"); 23 | schd.waitFor(s); // wait for all tasks to finish 24 | printf("Waiting for tasks to finish...DONE \n"); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /examples/example3.cpp: -------------------------------------------------------------------------------- 1 | // Example-3: 2 | // launch tasks in sequence 3 | 4 | #define PX_SCHED_IMPLEMENTATION 1 5 | #include "../px_sched.h" 6 | 7 | int main(int, char **) { 8 | px_sched::Scheduler schd; 9 | schd.init(); 10 | 11 | px_sched::Sync s; 12 | for(size_t i = 0; i < 128; ++i) { 13 | auto job = [i] { 14 | printf("Task %zu completed from %s\n", 15 | i, px_sched::Scheduler::current_thread_name()); 16 | }; 17 | px_sched::Sync new_s; 18 | // run job after s 19 | schd.runAfter(s, job, &new_s); 20 | // sync objects can be copied, store the new sync object for the next task 21 | s = new_s; 22 | } 23 | 24 | printf("Waiting for tasks to finish...\n"); 25 | schd.waitFor(s); // wait for all tasks to finish 26 | printf("Waiting for tasks to finish...DONE \n"); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 4 | CMD1: 'call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat" ' 5 | CMD2: 'echo ....' 6 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 7 | CMD1: 'call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"' 8 | CMD2: 'echo ....' 9 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 10 | CMD1: 'call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86' 11 | CMD2: 'echo ....' 12 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 13 | CMD1: 'call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64' 14 | CMD2: 'call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64' 15 | 16 | build_script: 17 | - cmd: call %CMD1% 18 | - cmd: call %CMD2% 19 | - cmd: cd examples 20 | - cmd: call msbuild.bat 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jose L. Hidalgo Valiño (PpluX) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/example2.cpp: -------------------------------------------------------------------------------- 1 | // Example-2: 2 | // Custom Job Definition 3 | 4 | #include 5 | // Job definition must be done before including px_sched.h 6 | #define PX_SCHED_CUSTOM_JOB_DEFINITION 7 | namespace px_sched { 8 | struct Job { 9 | void (*func)(size_t); 10 | size_t n; 11 | void operator()() { func(n); } 12 | }; 13 | } // px namespace 14 | 15 | #define PX_SCHED_IMPLEMENTATION 1 16 | #include "../px_sched.h" 17 | 18 | void task(size_t n) { 19 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 20 | printf("Task %zu completed from %s\n", n, px_sched::Scheduler::current_thread_name()); 21 | } 22 | 23 | int main(int, char **) { 24 | px_sched::Scheduler schd; 25 | schd.init(); 26 | 27 | // (a) Sync objects can be used later to wait for them 28 | px_sched::Sync s; 29 | for(size_t i = 0; i < 128; ++i) { 30 | px_sched::Job job { task, i }; 31 | // run a task, and notify to the given sync object 32 | schd.run(job, &s); 33 | } 34 | 35 | printf("Waiting for tasks to finish...\n"); 36 | schd.waitFor(s); // wait for all tasks to finish 37 | printf("Waiting for tasks to finish...DONE \n"); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CXXFLAGS=-std=c++11 -pedantic -g -O2\ 3 | -Wall \ 4 | -Wcast-align \ 5 | -Wcast-qual \ 6 | -Wctor-dtor-privacy \ 7 | -Wdisabled-optimization \ 8 | -Wdouble-promotion \ 9 | -Werror \ 10 | -Wextra \ 11 | -Wformat=2 \ 12 | -Winit-self \ 13 | -Wmissing-include-dirs \ 14 | -Wno-unused\ 15 | -Wold-style-cast \ 16 | -Woverloaded-virtual \ 17 | -Wredundant-decls \ 18 | -Wshadow \ 19 | -Wsign-conversion \ 20 | -Wsign-promo \ 21 | -Wstrict-overflow=5 \ 22 | -Wswitch-default \ 23 | -Wundef \ 24 | 25 | UNAME_S := $(shell uname -s) 26 | 27 | 28 | ifeq ($(UNAME_S),Linux) 29 | LDFLAGS += -lpthread 30 | ifeq ($(config),tsan) 31 | LDFLAGS += -ltsan 32 | CXXFLAGS += -fsanitize=thread -pie -fPIE 33 | endif 34 | endif 35 | 36 | examples = example1 example2 example3 example4 example5 example6 example7 37 | all: $(examples) 38 | 39 | $(examples): %: %.cpp ../px_sched.h 40 | $(CXX) $(CXXFLAGS) -o $@ $(LDFLAGS) $< 41 | 42 | .PHONY: clean tests 43 | clean: 44 | rm -f $(examples) 45 | 46 | tests: $(examples) 47 | ./example1 48 | ./example2 49 | ./example3 50 | ./example4 51 | ./example5 52 | ./example6 53 | ./example7 54 | @echo "ALL examples executed" 55 | -------------------------------------------------------------------------------- /examples/example4.cpp: -------------------------------------------------------------------------------- 1 | // Example-4: 2 | // Tasks can launch sub-tasks and wait for them 3 | 4 | #define PX_SCHED_IMPLEMENTATION 1 5 | #include "../px_sched.h" 6 | #include "mem_check.h" 7 | 8 | int main(int, char **) { 9 | atexit(mem_report); 10 | px_sched::Scheduler schd; 11 | px_sched::SchedulerParams s_params; 12 | s_params.mem_callbacks.alloc_fn = mem_check_alloc; 13 | s_params.mem_callbacks.free_fn = mem_check_free; 14 | schd.init(s_params); 15 | 16 | px_sched::Sync s; 17 | for(size_t i = 0; i < 10; ++i) { 18 | auto job = [i] { 19 | printf("Phase 1: Task %zu completed from %s\n", 20 | i, px_sched::Scheduler::current_thread_name()); 21 | }; 22 | schd.run(job, &s); 23 | } 24 | 25 | px_sched::Sync last; 26 | schd.runAfter(s, [&schd]{ 27 | printf("Phase 2\n"); 28 | px_sched::Sync s2; 29 | for(size_t i = 0; i < 10; ++i) { 30 | auto job = [i] { 31 | printf("Phase 2: Task %zu completed from %s\n", 32 | i, px_sched::Scheduler::current_thread_name()); 33 | }; 34 | schd.run(job, &s2); 35 | } 36 | schd.waitFor(s2); 37 | printf("Phase 2, done\n"); 38 | }, &last); 39 | 40 | printf("Waiting for tasks to finish...\n"); 41 | schd.waitFor(last); // wait for all tasks to finish 42 | printf("Waiting for tasks to finish...DONE \n"); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /examples/example5.cpp: -------------------------------------------------------------------------------- 1 | // Example-5: 2 | // Launch 3 phases, without Waiting and locking 3 | 4 | #define PX_SCHED_IMPLEMENTATION 1 5 | #include "../px_sched.h" 6 | #include "mem_check.h" 7 | 8 | int main(int, char **) { 9 | atexit(mem_report); 10 | px_sched::Scheduler schd; 11 | px_sched::SchedulerParams s_params; 12 | s_params.mem_callbacks.alloc_fn = mem_check_alloc; 13 | s_params.mem_callbacks.free_fn = mem_check_free; 14 | schd.init(s_params); 15 | 16 | px_sched::Sync s1,s2,s3; 17 | for(size_t i = 0; i < 10; ++i) { 18 | auto job = [i] { 19 | printf("Phase 1: Task %zu completed from %s\n", 20 | i, px_sched::Scheduler::current_thread_name()); 21 | }; 22 | schd.run(job, &s1); 23 | } 24 | 25 | for(size_t i = 0; i < 10; ++i) { 26 | auto job = [i] { 27 | printf("Phase 2: Task %zu completed from %s\n", 28 | i, px_sched::Scheduler::current_thread_name()); 29 | }; 30 | schd.runAfter(s1, job, &s2); 31 | } 32 | 33 | for(size_t i = 0; i < 10; ++i) { 34 | auto job = [i] { 35 | printf("Phase 3: Task %zu completed from %s\n", 36 | i, px_sched::Scheduler::current_thread_name()); 37 | }; 38 | schd.runAfter(s2, job, &s3); 39 | } 40 | 41 | 42 | px_sched::Sync last = s3; 43 | printf("Waiting for tasks to finish...\n"); 44 | schd.waitFor(last); // wait for all tasks to finish 45 | printf("Waiting for tasks to finish...DONE \n"); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /examples/example6.cpp: -------------------------------------------------------------------------------- 1 | // Example-6: 2 | // Manually working with sync objects to control the launch of tasks 3 | 4 | #define PX_SCHED_IMPLEMENTATION 1 5 | #include "../px_sched.h" 6 | #include "mem_check.h" 7 | 8 | int main(int, char **) { 9 | atexit(mem_report); 10 | px_sched::Scheduler schd; 11 | px_sched::SchedulerParams s_params; 12 | s_params.mem_callbacks.alloc_fn = mem_check_alloc; 13 | s_params.mem_callbacks.free_fn = mem_check_free; 14 | schd.init(s_params); 15 | 16 | px_sched::Sync s1,s2; 17 | for(size_t i = 0; i < 10; ++i) { 18 | auto job = [i] { 19 | printf("Phase 1: Task %zu completed from %s\n", 20 | i, px_sched::Scheduler::current_thread_name()); 21 | }; 22 | schd.run(job, &s1); 23 | } 24 | 25 | // manually increment the sync object, to control any task that will be 26 | // attached to it. Tasks are executed when sync objects reach zero. 27 | schd.incrementSync(&s1); 28 | 29 | for(size_t i = 0; i < 10; ++i) { 30 | auto job = [i] { 31 | printf("Phase 2: Task %zu completed from %s\n", 32 | i, px_sched::Scheduler::current_thread_name()); 33 | }; 34 | schd.runAfter(s1, job, &s2); 35 | } 36 | 37 | printf("Holding sync object 1 to prevent launch of phase2\n"); 38 | std::this_thread::sleep_for(std::chrono::seconds(2)); 39 | printf("releasing Sync-1...\n"); 40 | schd.decrementSync(&s1); 41 | 42 | px_sched::Sync last = s2; 43 | printf("Waiting for tasks to finish...\n"); 44 | schd.waitFor(last); // wait for all tasks to finish 45 | printf("Waiting for tasks to finish...DONE \n"); 46 | 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /examples/example7.cpp: -------------------------------------------------------------------------------- 1 | // Example-7: 2 | // Multiple Readers, Single Writer pattern 3 | 4 | 5 | #include // demo: rand 6 | #include // for the scoped lock 7 | 8 | //#define PX_SCHED_CONFIG_SINGLE_THREAD 1 9 | #define PX_SCHED_IMPLEMENTATION 1 10 | #include "../px_sched.h" 11 | #include "mem_check.h" 12 | 13 | 14 | template 15 | class MRSW { 16 | public: 17 | ~MRSW() { finish(); } 18 | 19 | void init(px_sched::Scheduler *s) { 20 | finish(); 21 | sched_ = s; 22 | void *ptr = sched_->params().mem_callbacks.alloc_fn(sizeof(T)); 23 | obj_ = new (ptr) T(); 24 | read_mode_ = true; 25 | } 26 | 27 | void finish() { 28 | if (obj_) { 29 | sched_->waitFor(next_); 30 | obj_->~T(); 31 | sched_->params().mem_callbacks.free_fn(obj_); 32 | obj_ = nullptr; 33 | sched_ = nullptr; 34 | } 35 | } 36 | 37 | template 38 | void executeRead(T_func func, px_sched::Sync *finish_signal = nullptr) { 39 | std::lock_guard g(lock_); 40 | if (!read_mode_) { 41 | read_mode_ = true; 42 | prev_ = next_; 43 | next_ = px_sched::Sync(); 44 | } 45 | if (finish_signal) sched_->incrementSync(finish_signal); 46 | sched_->runAfter(prev_, [this, func, finish_signal]() { 47 | func(static_cast(obj_)); 48 | if (finish_signal) sched_->decrementSync(finish_signal); 49 | }, &next_); 50 | } 51 | 52 | template 53 | void executeWrite(T_func func, px_sched::Sync *finish_signal = nullptr) { 54 | std::lock_guard g(lock_); 55 | read_mode_ = false; 56 | px_sched::Sync new_next; 57 | if (finish_signal) sched_->incrementSync(finish_signal); 58 | sched_->runAfter(next_, [this, func, finish_signal]() { 59 | func(obj_); 60 | if (finish_signal) sched_->decrementSync(finish_signal); 61 | }, &new_next); 62 | next_ = new_next; 63 | } 64 | private: 65 | px_sched::Scheduler *sched_ = nullptr; 66 | T *obj_ = nullptr; 67 | px_sched::Sync prev_; 68 | px_sched::Sync next_; 69 | px_sched::Spinlock lock_; 70 | bool read_mode_; 71 | }; 72 | 73 | struct Example { 74 | mutable std::atomic readers = {0}; 75 | std::atomic writers = {0}; 76 | }; 77 | 78 | int main(int, char **) { 79 | atexit(mem_report); 80 | px_sched::Scheduler sched_; 81 | px_sched::SchedulerParams s_params; 82 | s_params.max_number_tasks = 8196; 83 | s_params.mem_callbacks.alloc_fn = mem_check_alloc; 84 | s_params.mem_callbacks.free_fn = mem_check_free; 85 | sched_.init(s_params); 86 | 87 | MRSW example; 88 | example.init(&sched_); 89 | 90 | for(uint32_t i = 0; i < 1000; ++i) { 91 | if ((std::rand() & 0xFF) < 200) { 92 | example.executeRead([i](const Example *e) { 93 | e->readers.fetch_add(1); 94 | printf("[%u] Read Op %d(R)/%d(W)\n", i, e->readers.load(), e->writers.load()); 95 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 96 | e->readers.fetch_sub(1); 97 | }); 98 | } else { 99 | example.executeWrite([i](Example *e) { 100 | e->writers.fetch_add(1); 101 | printf("[%u] Write Op %d(R)/%d(W)\n", i, e->readers.load(), e->writers.load()); 102 | std::this_thread::sleep_for(std::chrono::milliseconds(3)); 103 | e->writers.fetch_sub(1); 104 | }); 105 | } 106 | } 107 | 108 | printf("WAITING FOR TASKS TO FINISH....\n"); 109 | 110 | 111 | return 0; 112 | } 113 | --------------------------------------------------------------------------------