diff --git a/CMakeLists.txt b/CMakeLists.txt index 74adfd91..9874a6c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ project(FiberTaskingLib) # Options option(FTL_BUILD_TESTS "Build FiberTaskingLib tests" ON) +option(FTL_BUILD_CONTRIB_TESTS "Should FiberTaskingLib contrib tests be built?" ON) option(FTL_BUILD_BENCHMARKS "Build FiberTaskingLib benchmarks" ON) option(FTL_VALGRIND "Link and test with Valgrind" OFF) option(FTL_FIBER_STACK_GUARD_PAGES "Add guard pages around the fiber stacks" OFF) diff --git a/include/ftl/contrib/trampoline.h b/include/ftl/contrib/trampoline.h new file mode 100644 index 00000000..df10f22b --- /dev/null +++ b/include/ftl/contrib/trampoline.h @@ -0,0 +1,128 @@ +/** + * FiberTaskingLib - A tasking library that uses fibers for efficient task switching + * + * This library was created as a proof of concept of the ideas presented by + * Christian Gyrling in his 2015 GDC Talk 'Parallelizing the Naughty Dog Engine Using Fibers' + * + * http://gdcvault.com/play/1022186/Parallelizing-the-Naughty-Dog-Engine + * + * FiberTaskingLib is the legal property of Adrian Astley + * Copyright Adrian Astley 2015 - 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "ftl/task.h" + +#include +#include +#include + +namespace ftl { + class TaskScheduler; + + template struct BoundTrampoline; + + template + struct Trampoline { + Trampoline(F&& f) : handler(std::move(f)) {} + F handler; + + template + BoundTrampoline* bind(Args&&... args) { + return new BoundTrampoline(*this, std::forward_as_tuple(args...)); + } + }; + + template + Trampoline make_trampoline(F&& f) { + Trampoline t(std::forward(f)); + return t; + } + + /* + C++11 machinery to power std::apply + */ + namespace detail { + // based on http://stackoverflow.com/a/17426611/410767 by Xeo + template + struct index_sequence { + using type = index_sequence; + using value_type = size_t; + static constexpr std::size_t size() noexcept { return sizeof...(Ints); } + }; + + // -------------------------------------------------------------- + + template + struct _merge_and_renumber; + + template + struct _merge_and_renumber, index_sequence> + : index_sequence {}; + + // -------------------------------------------------------------- + + template + struct make_index_sequence + : _merge_and_renumber::type, + typename make_index_sequence::type> {}; + + template<> struct make_index_sequence<0> : index_sequence<> {}; + template<> struct make_index_sequence<1> : index_sequence<0> {}; + + + template + auto invoke(F f, Args&&... args) -> decltype(std::ref(f)(std::forward(args)...)) { + return std::ref(f)(std::forward(args)...); + } + + template + auto apply_impl(F&& f, Tuple&& t, detail::index_sequence) -> decltype(std::ref(f)(std::get(std::forward(t))...)) { + return invoke(std::forward(f), std::get(std::forward(t))...); + } + + } // namespace detail + + template + struct BoundTrampoline { + Trampoline tramp; + std::tuple args; + + friend struct Trampoline; + private: + BoundTrampoline(Trampoline func, std::tuple&& args) : tramp(func), args(std::move(args)) {} + public: + BoundTrampoline(const BoundTrampoline&) = delete; + BoundTrampoline& operator=(const BoundTrampoline&) = delete; + + BoundTrampoline(BoundTrampoline&&) = default; + BoundTrampoline& operator=(BoundTrampoline&&) = default; + + // call task + static void gencall(ftl::TaskScheduler* ts, void * arg) { + auto t = static_cast*>(arg); + detail::apply_impl(t->tramp.handler, t->args,detail::make_index_sequence::type>::value>{}); + delete t; + } + + operator Task() { + return Task{ &gencall, static_cast(this) }; + } + + ~BoundTrampoline() = default; + }; + +} // End of namespace ftl diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 0a90ea79..3486d5e8 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -85,15 +85,24 @@ SetSourceGroup(NAME Util ../include/ftl/ftl_valgrind.h ) +SetSourceGroup(NAME Contrib + PREFIX FTL + SOURCE_FILES ../include/ftl/contrib/trampoline.h +) + # Link all the sources into one set(FIBER_TASKING_LIB_SRC ${FTL_CORE} ${FTL_UTIL} ) -add_library(ftl STATIC ${FIBER_TASKING_LIB_SRC}) +set(FIBER_TASKING_LIB_CONTRIB_SRC + ${FTL_CONTRIB} +) + +add_library(ftl STATIC ${FIBER_TASKING_LIB_SRC} ${FIBER_TASKING_LIB_CONTRIB_SRC}) target_link_libraries(ftl boost_context ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(ftl PUBLIC ../include) # Remove the prefix -set_target_properties(ftl PROPERTIES PREFIX "") +set_target_properties(ftl PROPERTIES PREFIX "") \ No newline at end of file diff --git a/source/atomic_counter.cpp b/source/atomic_counter.cpp index 747449eb..003aeb7b 100644 --- a/source/atomic_counter.cpp +++ b/source/atomic_counter.cpp @@ -152,5 +152,4 @@ void AtomicCounter::CheckWaitingFibers(uint value) { } } - } // End of namespace ftl diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c1a409a2..9d47d297 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,8 +51,24 @@ set(FIBER_TASKING_LIB_TESTS_SRC ${FTL_TEST_TRIANGLE_NUMBER} ) +if(FTL_BUILD_CONTRIB_TESTS) + SetSourceGroup(NAME "Trampoline" + PREFIX FTL_TEST_CONTRIB + SOURCE_FILES contrib/trampoline.cpp + ) + + # Link all the sources into one + set(FIBER_TASKING_LIB_TESTS_CONTRIB_SRC + ${FTL_TEST_CONTRIB_TRAMPOLINE} + ) + + set(FIBER_TASKING_LIB_TESTS_SRC + ${FIBER_TASKING_LIB_TESTS_SRC} + ${FIBER_TASKING_LIB_TESTS_CONTRIB_SRC} + ) +endif() add_executable(ftl-test ${FIBER_TASKING_LIB_TESTS_SRC}) target_link_libraries(ftl-test gtest gtest_main ftl) -GTEST_ADD_TESTS(ftl-test "" ${FIBER_TASKING_LIB_TESTS_SRC}) +GTEST_ADD_TESTS(ftl-test "" ${FIBER_TASKING_LIB_TESTS_SRC}) \ No newline at end of file diff --git a/tests/contrib/trampoline.cpp b/tests/contrib/trampoline.cpp new file mode 100644 index 00000000..950e29c3 --- /dev/null +++ b/tests/contrib/trampoline.cpp @@ -0,0 +1,61 @@ +/** + * FiberTaskingLib - A tasking library that uses fibers for efficient task switching + * + * This library was created as a proof of concept of the ideas presented by + * Christian Gyrling in his 2015 GDC Talk 'Parallelizing the Naughty Dog Engine Using Fibers' + * + * http://gdcvault.com/play/1022186/Parallelizing-the-Naughty-Dog-Engine + * + * FiberTaskingLib is the legal property of Adrian Astley + * Copyright Adrian Astley 2015 - 2018 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ftl/atomic_counter.h" +#include "ftl/task_scheduler.h" + +#include + +#include "ftl/contrib/trampoline.h" + +struct Foo {}; + +/* + Showcases Trampoline functionality + + Features: + - automatic type safe interface and lambdas-as-tasks + - error if mismatched type/count of arguments (if a bit cryptic) + - no need to repeat types + + Glossary: + - Trampoline: wrapper around a lambda / function + - BoundTrampoline: Trampoline + arguments +*/ + +void TrampolineMainTask(ftl::TaskScheduler *taskScheduler, void *arg) { + Foo f; + int a = 3, b = 4, d = 6; + + ftl::AtomicCounter counter(taskScheduler); + taskScheduler->AddTask(*ftl::make_trampoline([](int& a, const int& b, const Foo& f, const int d) { a++; }).bind(a, b, f, d), &counter); + taskScheduler->WaitForCounter(&counter, 0); + // values are correctly captured and const is respected + std::cout << a << '\n'; +} + +TEST(ContribTests, Trampoline) { + ftl::TaskScheduler taskScheduler; + taskScheduler.Run(400, TrampolineMainTask); +}