Commit afe6ca45 by Ben Clayton

Merge changes I48e99f11,I1a914ff6

* changes: Update Marl to f1c446ccd Squashed 'third_party/marl/' changes from d29553a3730..f1c446ccdc0
parents 6d93a633 26e27c5e
# Revision history for `marl`
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).
## 1.0.0-dev
First versioned release of marl.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
include(cmake/parse_version.cmake) include(cmake/parse_version.cmake)
parse_version("${CMAKE_CURRENT_SOURCE_DIR}/VERSION" MARL) parse_version("${CMAKE_CURRENT_SOURCE_DIR}/CHANGES.md" MARL)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
...@@ -51,6 +51,7 @@ option_if_not_defined(MARL_MSAN "Build marl with memory sanitizer" OFF) ...@@ -51,6 +51,7 @@ option_if_not_defined(MARL_MSAN "Build marl with memory sanitizer" OFF)
option_if_not_defined(MARL_TSAN "Build marl with thread sanitizer" OFF) option_if_not_defined(MARL_TSAN "Build marl with thread sanitizer" OFF)
option_if_not_defined(MARL_INSTALL "Create marl install target" OFF) option_if_not_defined(MARL_INSTALL "Create marl install target" OFF)
option_if_not_defined(MARL_FULL_BENCHMARK "Run benchmarks for [0 .. numLogicalCPUs] with no stepping" OFF) option_if_not_defined(MARL_FULL_BENCHMARK "Run benchmarks for [0 .. numLogicalCPUs] with no stepping" OFF)
option_if_not_defined(MARL_FIBERS_USE_UCONTEXT "Use ucontext instead of assembly for fibers (ignored for platforms that do not support ucontext)" OFF)
option_if_not_defined(MARL_DEBUG_ENABLED "Enable debug checks even in release builds" OFF) option_if_not_defined(MARL_DEBUG_ENABLED "Enable debug checks even in release builds" OFF)
########################################################### ###########################################################
...@@ -115,9 +116,30 @@ check_cxx_source_compiles( ...@@ -115,9 +116,30 @@ check_cxx_source_compiles(
MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED) MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED)
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
# Check whether ucontext is supported.
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "-Werror")
check_cxx_source_compiles(
"#include <ucontext.h>
int main() {
ucontext_t ctx;
getcontext(&ctx);
makecontext(&ctx, nullptr, 2, 1, 2);
swapcontext(&ctx, &ctx);
return 0;
}"
MARL_UCONTEXT_SUPPORTED)
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
if (MARL_FIBERS_USE_UCONTEXT AND NOT MARL_UCONTEXT_SUPPORTED)
# Disable MARL_FIBERS_USE_UCONTEXT and warn if MARL_UCONTEXT_SUPPORTED is 0.
message(WARNING "MARL_FIBERS_USE_UCONTEXT is enabled, but ucontext is not supported by the target. Disabling")
set(MARL_FIBERS_USE_UCONTEXT 0)
endif()
if(MARL_IS_SUBPROJECT) if(MARL_IS_SUBPROJECT)
# Export MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED as this may be useful to parent projects # Export supported flags as this may be useful to parent projects
set(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED PARENT_SCOPE ${MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED}) set(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED PARENT_SCOPE ${MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED})
set(MARL_UCONTEXT_SUPPORTED PARENT_SCOPE ${MARL_UCONTEXT_SUPPORTED})
endif() endif()
########################################################### ###########################################################
...@@ -162,17 +184,18 @@ function(marl_set_target_options target) ...@@ -162,17 +184,18 @@ function(marl_set_target_options target)
# Enable all warnings # Enable all warnings
if(MSVC) if(MSVC)
target_compile_options(${target} PRIVATE target_compile_options(${target} PRIVATE "-W4")
"-W4"
"/wd4127" # conditional expression is constant
)
else() else()
target_compile_options(${target} PRIVATE "-Wall") target_compile_options(${target} PRIVATE "-Wall")
endif() endif()
# Disable specific, pedantic warnings # Disable specific, pedantic warnings
if(MSVC) if(MSVC)
target_compile_options(${target} PRIVATE "-D_CRT_SECURE_NO_WARNINGS") target_compile_options(${target} PRIVATE
"-D_CRT_SECURE_NO_WARNINGS"
"/wd4127" # conditional expression is constant
"/wd4324" # structure was padded due to alignment specifier
)
endif() endif()
# Treat all warnings as errors # Treat all warnings as errors
...@@ -195,6 +218,10 @@ function(marl_set_target_options target) ...@@ -195,6 +218,10 @@ function(marl_set_target_options target)
target_link_libraries(${target} PUBLIC "-fsanitize=thread") target_link_libraries(${target} PUBLIC "-fsanitize=thread")
endif() endif()
if(MARL_FIBERS_USE_UCONTEXT)
target_compile_definitions(${target} PRIVATE "MARL_FIBERS_USE_UCONTEXT=1")
endif()
if(MARL_DEBUG_ENABLED) if(MARL_DEBUG_ENABLED)
target_compile_definitions(${target} PRIVATE "MARL_DEBUG_ENABLED=1") target_compile_definitions(${target} PRIVATE "MARL_DEBUG_ENABLED=1")
endif() endif()
...@@ -207,12 +234,23 @@ endfunction(marl_set_target_options) ...@@ -207,12 +234,23 @@ endfunction(marl_set_target_options)
########################################################### ###########################################################
# marl # marl
if(MARL_BUILD_SHARED) # Can also be controlled by BUILD_SHARED_LIBS if(MARL_BUILD_SHARED OR BUILD_SHARED_LIBS)
add_library(marl SHARED ${MARL_LIST}) add_library(marl SHARED ${MARL_LIST})
if(MSVC)
target_compile_definitions(marl
PRIVATE "MARL_BUILDING_DLL=1"
PUBLIC "MARL_DLL=1"
)
endif()
else() else()
add_library(marl ${MARL_LIST}) add_library(marl ${MARL_LIST})
endif() endif()
if(NOT MSVC)
# Public API symbols are made visible with the MARL_EXPORT annotation.
target_compile_options(marl PRIVATE "-fvisibility=hidden")
endif()
set_target_properties(marl PROPERTIES set_target_properties(marl PROPERTIES
POSITION_INDEPENDENT_CODE 1 POSITION_INDEPENDENT_CODE 1
VERSION ${MARL_VERSION} VERSION ${MARL_VERSION}
...@@ -264,6 +302,7 @@ if(MARL_BUILD_TESTS) ...@@ -264,6 +302,7 @@ if(MARL_BUILD_TESTS)
${MARL_SRC_DIR}/blockingcall_test.cpp ${MARL_SRC_DIR}/blockingcall_test.cpp
${MARL_SRC_DIR}/conditionvariable_test.cpp ${MARL_SRC_DIR}/conditionvariable_test.cpp
${MARL_SRC_DIR}/containers_test.cpp ${MARL_SRC_DIR}/containers_test.cpp
${MARL_SRC_DIR}/dag_test.cpp
${MARL_SRC_DIR}/defer_test.cpp ${MARL_SRC_DIR}/defer_test.cpp
${MARL_SRC_DIR}/event_test.cpp ${MARL_SRC_DIR}/event_test.cpp
${MARL_SRC_DIR}/marl_test.cpp ${MARL_SRC_DIR}/marl_test.cpp
...@@ -277,12 +316,14 @@ if(MARL_BUILD_TESTS) ...@@ -277,12 +316,14 @@ if(MARL_BUILD_TESTS)
${MARL_SRC_DIR}/ticket_test.cpp ${MARL_SRC_DIR}/ticket_test.cpp
${MARL_SRC_DIR}/waitgroup_test.cpp ${MARL_SRC_DIR}/waitgroup_test.cpp
${MARL_GOOGLETEST_DIR}/googletest/src/gtest-all.cc ${MARL_GOOGLETEST_DIR}/googletest/src/gtest-all.cc
${MARL_GOOGLETEST_DIR}/googlemock/src/gmock-all.cc
) )
set(MARL_TEST_INCLUDE_DIR set(MARL_TEST_INCLUDE_DIR
${MARL_GOOGLETEST_DIR}/googletest/include/ ${MARL_GOOGLETEST_DIR}/googletest/include/
${MARL_GOOGLETEST_DIR}/googlemock/include/ ${MARL_GOOGLETEST_DIR}/googlemock/include/
${MARL_GOOGLETEST_DIR}/googletest/ ${MARL_GOOGLETEST_DIR}/googletest/
${MARL_GOOGLETEST_DIR}/googlemock/
) )
add_executable(marl-unittests ${MARL_TEST_LIST}) add_executable(marl-unittests ${MARL_TEST_LIST})
...@@ -311,6 +352,7 @@ if(MARL_BUILD_BENCHMARKS) ...@@ -311,6 +352,7 @@ if(MARL_BUILD_BENCHMARKS)
) )
add_executable(marl-benchmarks ${MARL_BENCHMARK_LIST}) add_executable(marl-benchmarks ${MARL_BENCHMARK_LIST})
set_target_properties(${target} PROPERTIES FOLDER "Benchmarks")
marl_set_target_options(marl-benchmarks) marl_set_target_options(marl-benchmarks)
...@@ -325,9 +367,7 @@ endif(MARL_BUILD_BENCHMARKS) ...@@ -325,9 +367,7 @@ endif(MARL_BUILD_BENCHMARKS)
if(MARL_BUILD_EXAMPLES) if(MARL_BUILD_EXAMPLES)
function(build_example target) function(build_example target)
add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp") add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp")
set_target_properties(${target} PROPERTIES set_target_properties(${target} PROPERTIES FOLDER "Examples")
FOLDER "Examples"
)
marl_set_target_options(${target}) marl_set_target_options(${target})
target_link_libraries(${target} PRIVATE marl) target_link_libraries(${target} PRIVATE marl)
endfunction(build_example) endfunction(build_example)
......
...@@ -8,7 +8,7 @@ Marl is a C++ 11 library that provides a fluent interface for running tasks acro ...@@ -8,7 +8,7 @@ Marl is a C++ 11 library that provides a fluent interface for running tasks acro
Marl uses a combination of fibers and threads to allow efficient execution of tasks that can block, while keeping a fixed number of hardware threads. Marl uses a combination of fibers and threads to allow efficient execution of tasks that can block, while keeping a fixed number of hardware threads.
Marl supports Windows, macOS, Linux, Fuchsia and Android (arm, aarch64, mips64, ppc64 (ELFv2), x86 and x64). Marl supports Windows, macOS, Linux, FreeBSD, Fuchsia, Android and iOS (arm, aarch64, mips64, ppc64 (ELFv2), x86 and x64).
Marl has no dependencies on other libraries (with an exception on googletest for building the optional unit tests). Marl has no dependencies on other libraries (with an exception on googletest for building the optional unit tests).
...@@ -64,12 +64,10 @@ int main() { ...@@ -64,12 +64,10 @@ int main() {
} }
``` ```
## Benchmarks ## Benchmarks
Graphs of several microbenchmarks can be found [here](https://google.github.io/marl/benchmarks). Graphs of several microbenchmarks can be found [here](https://google.github.io/marl/benchmarks).
## Building ## Building
Marl contains many unit tests and examples that can be built using CMake. Marl contains many unit tests and examples that can be built using CMake.
...@@ -102,6 +100,7 @@ Marl can be built using [Visual Studio 2019's CMake integration](https://docs.mi ...@@ -102,6 +100,7 @@ Marl can be built using [Visual Studio 2019's CMake integration](https://docs.mi
### Using Marl in your CMake project ### Using Marl in your CMake project
You can build and link Marl using `add_subdirectory()` in your project's `CMakeLists.txt` file: You can build and link Marl using `add_subdirectory()` in your project's `CMakeLists.txt` file:
```cmake ```cmake
set(MARL_DIR <path-to-marl>) # example <path-to-marl>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/marl" set(MARL_DIR <path-to-marl>) # example <path-to-marl>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/marl"
add_subdirectory(${MARL_DIR}) add_subdirectory(${MARL_DIR})
...@@ -137,7 +136,7 @@ marl::schedule([=]{ // [=] Good, [&] Bad. ...@@ -137,7 +136,7 @@ marl::schedule([=]{ // [=] Good, [&] Bad.
Internally, 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. Internally, 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.
#### Create one instance of `marl::Scheduler`, use it for the lifetime of the process. #### Create one instance of `marl::Scheduler`, use it for the lifetime of the process
The `marl::Scheduler` constructor can be expensive as it may spawn a number of hardware threads. \ The `marl::Scheduler` constructor can be expensive as it may spawn a number of hardware threads. \
Destructing the `marl::Scheduler` requires waiting on all tasks to complete. Destructing the `marl::Scheduler` requires waiting on all tasks to complete.
......
0.0.0-dev
\ No newline at end of file
...@@ -21,19 +21,21 @@ ...@@ -21,19 +21,21 @@
# <major>.<minor>.<patch> # <major>.<minor>.<patch>
# <major>.<minor>.<patch>-<flavor> # <major>.<minor>.<patch>-<flavor>
function(parse_version FILE PROJECT) function(parse_version FILE PROJECT)
configure_file(${FILE} "${CMAKE_CURRENT_BINARY_DIR}/VERSION") # Required to re-run cmake on version change configure_file(${FILE} "${CMAKE_CURRENT_BINARY_DIR}/CHANGES.md") # Required to re-run cmake on version change
file(READ ${FILE} VERSION) file(READ ${FILE} CHANGES)
if(${VERSION} MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[a-zA-Z0-9]+)?") if(${CHANGES} MATCHES "#+ *([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[a-zA-Z0-9]+)?")
set(FLAVOR "") set(FLAVOR "")
if(NOT "${CMAKE_MATCH_4}" STREQUAL "") if(NOT "${CMAKE_MATCH_4}" STREQUAL "")
string(SUBSTRING ${CMAKE_MATCH_4} 1 -1 FLAVOR) string(SUBSTRING ${CMAKE_MATCH_4} 1 -1 FLAVOR)
endif() endif()
set("${PROJECT}_VERSION" ${VERSION} PARENT_SCOPE)
set("${PROJECT}_VERSION_MAJOR" ${CMAKE_MATCH_1} PARENT_SCOPE) set("${PROJECT}_VERSION_MAJOR" ${CMAKE_MATCH_1} PARENT_SCOPE)
set("${PROJECT}_VERSION_MINOR" ${CMAKE_MATCH_2} PARENT_SCOPE) set("${PROJECT}_VERSION_MINOR" ${CMAKE_MATCH_2} PARENT_SCOPE)
set("${PROJECT}_VERSION_PATCH" ${CMAKE_MATCH_3} PARENT_SCOPE) set("${PROJECT}_VERSION_PATCH" ${CMAKE_MATCH_3} PARENT_SCOPE)
set("${PROJECT}_VERSION_FLAVOR" ${FLAVOR} PARENT_SCOPE) set("${PROJECT}_VERSION_FLAVOR" ${FLAVOR} PARENT_SCOPE)
set("${PROJECT}_VERSION"
"${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}${CMAKE_MATCH_4}"
PARENT_SCOPE)
else() else()
message(FATAL_ERROR "Unable to parse version string '${VERSION}'") message(FATAL_ERROR "Unable to parse version from '${FILE}'")
endif() endif()
endfunction() endfunction()
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#ifndef marl_blocking_call_h #ifndef marl_blocking_call_h
#define marl_blocking_call_h #define marl_blocking_call_h
#include "export.h"
#include "scheduler.h" #include "scheduler.h"
#include "waitgroup.h" #include "waitgroup.h"
...@@ -29,7 +30,7 @@ template <typename RETURN_TYPE> ...@@ -29,7 +30,7 @@ template <typename RETURN_TYPE>
class OnNewThread { class OnNewThread {
public: public:
template <typename F, typename... Args> template <typename F, typename... Args>
inline static RETURN_TYPE call(F&& f, Args&&... args) { MARL_NO_EXPORT inline static RETURN_TYPE call(F&& f, Args&&... args) {
RETURN_TYPE result; RETURN_TYPE result;
WaitGroup wg(1); WaitGroup wg(1);
auto scheduler = Scheduler::get(); auto scheduler = Scheduler::get();
...@@ -55,7 +56,7 @@ template <> ...@@ -55,7 +56,7 @@ template <>
class OnNewThread<void> { class OnNewThread<void> {
public: public:
template <typename F, typename... Args> template <typename F, typename... Args>
inline static void call(F&& f, Args&&... args) { MARL_NO_EXPORT inline static void call(F&& f, Args&&... args) {
WaitGroup wg(1); WaitGroup wg(1);
auto scheduler = Scheduler::get(); auto scheduler = Scheduler::get();
auto thread = std::thread( auto thread = std::thread(
...@@ -94,7 +95,8 @@ class OnNewThread<void> { ...@@ -94,7 +95,8 @@ class OnNewThread<void> {
// }); // });
// } // }
template <typename F, typename... Args> template <typename F, typename... Args>
auto inline blocking_call(F&& f, Args&&... args) -> decltype(f(args...)) { MARL_NO_EXPORT auto inline blocking_call(F&& f, Args&&... args)
-> decltype(f(args...)) {
return detail::OnNewThread<decltype(f(args...))>::call( return detail::OnNewThread<decltype(f(args...))>::call(
std::forward<F>(f), std::forward<Args>(args)...); std::forward<F>(f), std::forward<Args>(args)...);
} }
......
...@@ -35,37 +35,40 @@ namespace marl { ...@@ -35,37 +35,40 @@ namespace marl {
// thread will work on other tasks until the ConditionVariable is unblocked. // thread will work on other tasks until the ConditionVariable is unblocked.
class ConditionVariable { class ConditionVariable {
public: public:
inline ConditionVariable(Allocator* allocator = Allocator::Default); MARL_NO_EXPORT inline ConditionVariable(
Allocator* allocator = Allocator::Default);
// notify_one() notifies and potentially unblocks one waiting fiber or thread. // notify_one() notifies and potentially unblocks one waiting fiber or thread.
inline void notify_one(); MARL_NO_EXPORT inline void notify_one();
// notify_all() notifies and potentially unblocks all waiting fibers and/or // notify_all() notifies and potentially unblocks all waiting fibers and/or
// threads. // threads.
inline void notify_all(); MARL_NO_EXPORT inline void notify_all();
// wait() blocks the current fiber or thread until the predicate is satisfied // wait() blocks the current fiber or thread until the predicate is satisfied
// and the ConditionVariable is notified. // and the ConditionVariable is notified.
template <typename Predicate> template <typename Predicate>
inline void wait(marl::lock& lock, Predicate&& pred); MARL_NO_EXPORT inline void wait(marl::lock& lock, Predicate&& pred);
// wait_for() blocks the current fiber or thread until the predicate is // wait_for() blocks the current fiber or thread until the predicate is
// satisfied, and the ConditionVariable is notified, or the timeout has been // satisfied, and the ConditionVariable is notified, or the timeout has been
// reached. Returns false if pred still evaluates to false after the timeout // reached. Returns false if pred still evaluates to false after the timeout
// has been reached, otherwise true. // has been reached, otherwise true.
template <typename Rep, typename Period, typename Predicate> template <typename Rep, typename Period, typename Predicate>
bool wait_for(marl::lock& lock, MARL_NO_EXPORT inline bool wait_for(
const std::chrono::duration<Rep, Period>& duration, marl::lock& lock,
Predicate&& pred); const std::chrono::duration<Rep, Period>& duration,
Predicate&& pred);
// wait_until() blocks the current fiber or thread until the predicate is // wait_until() blocks the current fiber or thread until the predicate is
// satisfied, and the ConditionVariable is notified, or the timeout has been // satisfied, and the ConditionVariable is notified, or the timeout has been
// reached. Returns false if pred still evaluates to false after the timeout // reached. Returns false if pred still evaluates to false after the timeout
// has been reached, otherwise true. // has been reached, otherwise true.
template <typename Clock, typename Duration, typename Predicate> template <typename Clock, typename Duration, typename Predicate>
bool wait_until(marl::lock& lock, MARL_NO_EXPORT inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& timeout, marl::lock& lock,
Predicate&& pred); const std::chrono::time_point<Clock, Duration>& timeout,
Predicate&& pred);
private: private:
ConditionVariable(const ConditionVariable&) = delete; ConditionVariable(const ConditionVariable&) = delete;
...@@ -90,8 +93,8 @@ void ConditionVariable::notify_one() { ...@@ -90,8 +93,8 @@ void ConditionVariable::notify_one() {
} }
{ {
marl::lock lock(mutex); marl::lock lock(mutex);
for (auto fiber : waiting) { if (waiting.size() > 0) {
fiber->notify(); (*waiting.begin())->notify(); // Only wake one fiber.
} }
} }
if (numWaitingOnCondition > 0) { if (numWaitingOnCondition > 0) {
......
...@@ -58,7 +58,7 @@ using unordered_set = std::unordered_set<K, H, E, StlAllocator<K>>; ...@@ -58,7 +58,7 @@ using unordered_set = std::unordered_set<K, H, E, StlAllocator<K>>;
// take() takes and returns the front value from the deque. // take() takes and returns the front value from the deque.
template <typename T> template <typename T>
inline T take(deque<T>& queue) { MARL_NO_EXPORT inline T take(deque<T>& queue) {
auto out = std::move(queue.front()); auto out = std::move(queue.front());
queue.pop_front(); queue.pop_front();
return out; return out;
...@@ -66,7 +66,7 @@ inline T take(deque<T>& queue) { ...@@ -66,7 +66,7 @@ inline T take(deque<T>& queue) {
// take() takes and returns the first value from the unordered_set. // take() takes and returns the first value from the unordered_set.
template <typename T, typename H, typename E> template <typename T, typename H, typename E>
inline T take(unordered_set<T, H, E>& set) { MARL_NO_EXPORT inline T take(unordered_set<T, H, E>& set) {
auto it = set.begin(); auto it = set.begin();
auto out = std::move(*it); auto out = std::move(*it);
set.erase(it); set.erase(it);
...@@ -85,45 +85,47 @@ inline T take(unordered_set<T, H, E>& set) { ...@@ -85,45 +85,47 @@ inline T take(unordered_set<T, H, E>& set) {
template <typename T, int BASE_CAPACITY> template <typename T, int BASE_CAPACITY>
class vector { class vector {
public: public:
inline vector(Allocator* allocator = Allocator::Default); MARL_NO_EXPORT inline vector(Allocator* allocator = Allocator::Default);
template <int BASE_CAPACITY_2> template <int BASE_CAPACITY_2>
inline vector(const vector<T, BASE_CAPACITY_2>& other, MARL_NO_EXPORT inline vector(const vector<T, BASE_CAPACITY_2>& other,
Allocator* allocator = Allocator::Default); Allocator* allocator = Allocator::Default);
template <int BASE_CAPACITY_2> template <int BASE_CAPACITY_2>
inline vector(vector<T, BASE_CAPACITY_2>&& other, MARL_NO_EXPORT inline vector(vector<T, BASE_CAPACITY_2>&& other,
Allocator* allocator = Allocator::Default); Allocator* allocator = Allocator::Default);
inline ~vector(); MARL_NO_EXPORT inline ~vector();
inline vector& operator=(const vector&); MARL_NO_EXPORT inline vector& operator=(const vector&);
template <int BASE_CAPACITY_2> template <int BASE_CAPACITY_2>
inline vector<T, BASE_CAPACITY>& operator=(const vector<T, BASE_CAPACITY_2>&); MARL_NO_EXPORT inline vector<T, BASE_CAPACITY>& operator=(
const vector<T, BASE_CAPACITY_2>&);
template <int BASE_CAPACITY_2> template <int BASE_CAPACITY_2>
inline vector<T, BASE_CAPACITY>& operator=(vector<T, BASE_CAPACITY_2>&&); MARL_NO_EXPORT inline vector<T, BASE_CAPACITY>& operator=(
vector<T, BASE_CAPACITY_2>&&);
inline void push_back(const T& el);
inline void emplace_back(T&& el); MARL_NO_EXPORT inline void push_back(const T& el);
inline void pop_back(); MARL_NO_EXPORT inline void emplace_back(T&& el);
inline T& front(); MARL_NO_EXPORT inline void pop_back();
inline T& back(); MARL_NO_EXPORT inline T& front();
inline const T& front() const; MARL_NO_EXPORT inline T& back();
inline const T& back() const; MARL_NO_EXPORT inline const T& front() const;
inline T* begin(); MARL_NO_EXPORT inline const T& back() const;
inline T* end(); MARL_NO_EXPORT inline T* begin();
inline const T* begin() const; MARL_NO_EXPORT inline T* end();
inline const T* end() const; MARL_NO_EXPORT inline const T* begin() const;
inline T& operator[](size_t i); MARL_NO_EXPORT inline const T* end() const;
inline const T& operator[](size_t i) const; MARL_NO_EXPORT inline T& operator[](size_t i);
inline size_t size() const; MARL_NO_EXPORT inline const T& operator[](size_t i) const;
inline size_t cap() const; MARL_NO_EXPORT inline size_t size() const;
inline void resize(size_t n); MARL_NO_EXPORT inline size_t cap() const;
inline void reserve(size_t n); MARL_NO_EXPORT inline void resize(size_t n);
inline T* data(); MARL_NO_EXPORT inline void reserve(size_t n);
inline const T* data() const; MARL_NO_EXPORT inline T* data();
MARL_NO_EXPORT inline const T* data() const;
Allocator* const allocator; Allocator* const allocator;
...@@ -132,7 +134,7 @@ class vector { ...@@ -132,7 +134,7 @@ class vector {
vector(const vector&) = delete; vector(const vector&) = delete;
inline void free(); MARL_NO_EXPORT inline void free();
size_t count = 0; size_t count = 0;
size_t capacity = BASE_CAPACITY; size_t capacity = BASE_CAPACITY;
...@@ -366,28 +368,28 @@ class list { ...@@ -366,28 +368,28 @@ class list {
public: public:
class iterator { class iterator {
public: public:
inline iterator(Entry*); MARL_NO_EXPORT inline iterator(Entry*);
inline T* operator->(); MARL_NO_EXPORT inline T* operator->();
inline T& operator*(); MARL_NO_EXPORT inline T& operator*();
inline iterator& operator++(); MARL_NO_EXPORT inline iterator& operator++();
inline bool operator==(const iterator&) const; MARL_NO_EXPORT inline bool operator==(const iterator&) const;
inline bool operator!=(const iterator&) const; MARL_NO_EXPORT inline bool operator!=(const iterator&) const;
private: private:
friend list; friend list;
Entry* entry; Entry* entry;
}; };
inline list(Allocator* allocator = Allocator::Default); MARL_NO_EXPORT inline list(Allocator* allocator = Allocator::Default);
inline ~list(); MARL_NO_EXPORT inline ~list();
inline iterator begin(); MARL_NO_EXPORT inline iterator begin();
inline iterator end(); MARL_NO_EXPORT inline iterator end();
inline size_t size() const; MARL_NO_EXPORT inline size_t size() const;
template <typename... Args> template <typename... Args>
iterator emplace_front(Args&&... args); MARL_NO_EXPORT inline iterator emplace_front(Args&&... args);
inline void erase(iterator); MARL_NO_EXPORT inline void erase(iterator);
private: private:
// copy / move is currently unsupported. // copy / move is currently unsupported.
...@@ -401,10 +403,10 @@ class list { ...@@ -401,10 +403,10 @@ class list {
AllocationChain* next; AllocationChain* next;
}; };
void grow(size_t count); MARL_NO_EXPORT inline void grow(size_t count);
static void unlink(Entry* entry, Entry*& list); MARL_NO_EXPORT static inline void unlink(Entry* entry, Entry*& list);
static void link(Entry* entry, Entry*& list); MARL_NO_EXPORT static inline void link(Entry* entry, Entry*& list);
Allocator* const allocator; Allocator* const allocator;
size_t size_ = 0; size_t size_ = 0;
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#ifndef marl_debug_h #ifndef marl_debug_h
#define marl_debug_h #define marl_debug_h
#include "export.h"
#if !defined(MARL_DEBUG_ENABLED) #if !defined(MARL_DEBUG_ENABLED)
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
#define MARL_DEBUG_ENABLED 1 #define MARL_DEBUG_ENABLED 1
...@@ -25,8 +27,13 @@ ...@@ -25,8 +27,13 @@
namespace marl { namespace marl {
MARL_EXPORT
void fatal(const char* msg, ...); void fatal(const char* msg, ...);
MARL_EXPORT
void warn(const char* msg, ...); void warn(const char* msg, ...);
MARL_EXPORT
void assert_has_bound_scheduler(const char* feature); void assert_has_bound_scheduler(const char* feature);
#if MARL_DEBUG_ENABLED #if MARL_DEBUG_ENABLED
......
...@@ -15,14 +15,6 @@ ...@@ -15,14 +15,6 @@
#ifndef marl_deprecated_h #ifndef marl_deprecated_h
#define marl_deprecated_h #define marl_deprecated_h
// Deprecated marl::Scheduler methods:
// Scheduler(Allocator* allocator = Allocator::Default)
// getThreadInitializer(), setThreadInitializer()
// getWorkerThreadCount(), setWorkerThreadCount()
#ifndef MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS
#define MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS 1
#endif // MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS
#ifndef MARL_WARN_DEPRECATED #ifndef MARL_WARN_DEPRECATED
#define MARL_WARN_DEPRECATED 1 #define MARL_WARN_DEPRECATED 1
#endif // MARL_WARN_DEPRECATED #endif // MARL_WARN_DEPRECATED
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "conditionvariable.h" #include "conditionvariable.h"
#include "containers.h" #include "containers.h"
#include "export.h"
#include "memory.h" #include "memory.h"
#include <chrono> #include <chrono>
...@@ -39,21 +40,21 @@ class Event { ...@@ -39,21 +40,21 @@ class Event {
Manual Manual
}; };
inline Event(Mode mode = Mode::Auto, MARL_NO_EXPORT inline Event(Mode mode = Mode::Auto,
bool initialState = false, bool initialState = false,
Allocator* allocator = Allocator::Default); Allocator* allocator = Allocator::Default);
// signal() signals the event, possibly unblocking a call to wait(). // signal() signals the event, possibly unblocking a call to wait().
inline void signal() const; MARL_NO_EXPORT inline void signal() const;
// clear() clears the signaled state. // clear() clears the signaled state.
inline void clear() const; MARL_NO_EXPORT inline void clear() const;
// wait() blocks until the event is signaled. // wait() blocks until the event is signaled.
// If the event was constructed with the Auto Mode, then only one // If the event was constructed with the Auto Mode, then only one
// call to wait() will unblock before returning, upon which the signalled // call to wait() will unblock before returning, upon which the signalled
// state will be automatically cleared. // state will be automatically cleared.
inline void wait() const; MARL_NO_EXPORT inline void wait() const;
// wait_for() blocks until the event is signaled, or the timeout has been // wait_for() blocks until the event is signaled, or the timeout has been
// reached. // reached.
...@@ -62,7 +63,7 @@ class Event { ...@@ -62,7 +63,7 @@ class Event {
// then only one call to wait() will unblock before returning, upon which the // then only one call to wait() will unblock before returning, upon which the
// signalled state will be automatically cleared. // signalled state will be automatically cleared.
template <typename Rep, typename Period> template <typename Rep, typename Period>
inline bool wait_for( MARL_NO_EXPORT inline bool wait_for(
const std::chrono::duration<Rep, Period>& duration) const; const std::chrono::duration<Rep, Period>& duration) const;
// wait_until() blocks until the event is signaled, or the timeout has been // wait_until() blocks until the event is signaled, or the timeout has been
...@@ -72,45 +73,49 @@ class Event { ...@@ -72,45 +73,49 @@ class Event {
// then only one call to wait() will unblock before returning, upon which the // then only one call to wait() will unblock before returning, upon which the
// signalled state will be automatically cleared. // signalled state will be automatically cleared.
template <typename Clock, typename Duration> template <typename Clock, typename Duration>
inline bool wait_until( MARL_NO_EXPORT inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& timeout) const; const std::chrono::time_point<Clock, Duration>& timeout) const;
// test() returns true if the event is signaled, otherwise false. // test() returns true if the event is signaled, otherwise false.
// If the event is signalled and was constructed with the Auto Mode // If the event is signalled and was constructed with the Auto Mode
// then the signalled state will be automatically cleared upon returning. // then the signalled state will be automatically cleared upon returning.
inline bool test() const; MARL_NO_EXPORT inline bool test() const;
// isSignalled() returns true if the event is signaled, otherwise false. // isSignalled() returns true if the event is signaled, otherwise false.
// Unlike test() the signal is not automatically cleared when the event was // Unlike test() the signal is not automatically cleared when the event was
// constructed with the Auto Mode. // constructed with the Auto Mode.
// Note: No lock is held after bool() returns, so the event state may // Note: No lock is held after bool() returns, so the event state may
// immediately change after returning. Use with caution. // immediately change after returning. Use with caution.
inline bool isSignalled() const; MARL_NO_EXPORT inline bool isSignalled() const;
// any returns an event that is automatically signalled whenever any of the // any returns an event that is automatically signalled whenever any of the
// events in the list are signalled. // events in the list are signalled.
template <typename Iterator> template <typename Iterator>
inline static Event any(Mode mode, MARL_NO_EXPORT inline static Event any(Mode mode,
const Iterator& begin, const Iterator& begin,
const Iterator& end); const Iterator& end);
// any returns an event that is automatically signalled whenever any of the // any returns an event that is automatically signalled whenever any of the
// events in the list are signalled. // events in the list are signalled.
// This overload defaults to using the Auto mode. // This overload defaults to using the Auto mode.
template <typename Iterator> template <typename Iterator>
inline static Event any(const Iterator& begin, const Iterator& end); MARL_NO_EXPORT inline static Event any(const Iterator& begin,
const Iterator& end);
private: private:
struct Shared { struct Shared {
inline Shared(Allocator* allocator, Mode mode, bool initialState); MARL_NO_EXPORT inline Shared(Allocator* allocator,
inline void signal(); Mode mode,
inline void wait(); bool initialState);
MARL_NO_EXPORT inline void signal();
MARL_NO_EXPORT inline void wait();
template <typename Rep, typename Period> template <typename Rep, typename Period>
inline bool wait_for(const std::chrono::duration<Rep, Period>& duration); MARL_NO_EXPORT inline bool wait_for(
const std::chrono::duration<Rep, Period>& duration);
template <typename Clock, typename Duration> template <typename Clock, typename Duration>
inline bool wait_until( MARL_NO_EXPORT inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& timeout); const std::chrono::time_point<Clock, Duration>& timeout);
marl::mutex mutex; marl::mutex mutex;
......
// Copyright 2020 The Marl Authors.
//
// 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
//
// https://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.
#ifndef marl_export_h
#define marl_export_h
#ifdef MARL_DLL
#if MARL_BUILDING_DLL
#define MARL_EXPORT __declspec(dllexport)
#else
#define MARL_EXPORT __declspec(dllimport)
#endif
#else // #ifdef MARL_DLL
#if __GNUC__ >= 4
#define MARL_EXPORT __attribute__((visibility("default")))
#define MARL_NO_EXPORT __attribute__((visibility("hidden")))
#endif
#endif
#ifndef MARL_EXPORT
#define MARL_EXPORT
#endif
#ifndef MARL_NO_EXPORT
#define MARL_NO_EXPORT
#endif
#endif // marl_export_h
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#ifndef marl_finally_h #ifndef marl_finally_h
#define marl_finally_h #define marl_finally_h
#include "export.h"
#include <functional> #include <functional>
#include <memory> #include <memory>
...@@ -42,10 +44,10 @@ class Finally { ...@@ -42,10 +44,10 @@ class Finally {
template <typename F> template <typename F>
class FinallyImpl : public Finally { class FinallyImpl : public Finally {
public: public:
inline FinallyImpl(const F& func); MARL_NO_EXPORT inline FinallyImpl(const F& func);
inline FinallyImpl(F&& func); MARL_NO_EXPORT inline FinallyImpl(F&& func);
inline FinallyImpl(FinallyImpl<F>&& other); MARL_NO_EXPORT inline FinallyImpl(FinallyImpl<F>&& other);
inline ~FinallyImpl(); MARL_NO_EXPORT inline ~FinallyImpl();
private: private:
FinallyImpl(const FinallyImpl<F>& other) = delete; FinallyImpl(const FinallyImpl<F>& other) = delete;
...@@ -76,12 +78,12 @@ FinallyImpl<F>::~FinallyImpl() { ...@@ -76,12 +78,12 @@ FinallyImpl<F>::~FinallyImpl() {
template <typename F> template <typename F>
inline FinallyImpl<F> make_finally(F&& f) { inline FinallyImpl<F> make_finally(F&& f) {
return FinallyImpl<F>(std::move(f)); return FinallyImpl<F>(std::forward<F>(f));
} }
template <typename F> template <typename F>
inline std::shared_ptr<Finally> make_shared_finally(F&& f) { inline std::shared_ptr<Finally> make_shared_finally(F&& f) {
return std::make_shared<FinallyImpl<F>>(std::move(f)); return std::make_shared<FinallyImpl<F>>(std::forward<F>(f));
} }
} // namespace marl } // namespace marl
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define marl_memory_h #define marl_memory_h
#include "debug.h" #include "debug.h"
#include "export.h"
#include <stdint.h> #include <stdint.h>
...@@ -32,10 +33,11 @@ struct StlAllocator; ...@@ -32,10 +33,11 @@ struct StlAllocator;
// pageSize() returns the size in bytes of a virtual memory page for the host // pageSize() returns the size in bytes of a virtual memory page for the host
// system. // system.
MARL_EXPORT
size_t pageSize(); size_t pageSize();
template <typename T> template <typename T>
inline T alignUp(T val, T alignment) { MARL_NO_EXPORT inline T alignUp(T val, T alignment) {
return alignment * ((val + alignment - 1) / alignment); return alignment * ((val + alignment - 1) / alignment);
} }
...@@ -87,19 +89,20 @@ class Allocator { ...@@ -87,19 +89,20 @@ class Allocator {
public: public:
// The default allocator. Initialized with an implementation that allocates // The default allocator. Initialized with an implementation that allocates
// from the OS. Can be assigned a custom implementation. // from the OS. Can be assigned a custom implementation.
static Allocator* Default; MARL_EXPORT static Allocator* Default;
// Deleter is a smart-pointer compatible deleter that can be used to delete // Deleter is a smart-pointer compatible deleter that can be used to delete
// objects created by Allocator::create(). Deleter is used by the smart // objects created by Allocator::create(). Deleter is used by the smart
// pointers returned by make_shared() and make_unique(). // pointers returned by make_shared() and make_unique().
struct Deleter { struct MARL_EXPORT Deleter {
inline Deleter(); MARL_NO_EXPORT inline Deleter();
inline Deleter(Allocator* allocator); MARL_NO_EXPORT inline Deleter(Allocator* allocator, size_t count);
template <typename T> template <typename T>
inline void operator()(T* object); MARL_NO_EXPORT inline void operator()(T* object);
Allocator* allocator = nullptr; Allocator* allocator = nullptr;
size_t count = 0;
}; };
// unique_ptr<T> is an alias to std::unique_ptr<T, Deleter>. // unique_ptr<T> is an alias to std::unique_ptr<T, Deleter>.
...@@ -132,6 +135,12 @@ class Allocator { ...@@ -132,6 +135,12 @@ class Allocator {
template <typename T, typename... ARGS> template <typename T, typename... ARGS>
inline unique_ptr<T> make_unique(ARGS&&... args); inline unique_ptr<T> make_unique(ARGS&&... args);
// make_unique_n() returns an array of n new objects allocated from the
// allocator wrapped in a unique_ptr that respects the alignemnt of the
// type.
template <typename T, typename... ARGS>
inline unique_ptr<T> make_unique_n(size_t n, ARGS&&... args);
// make_shared() returns a new object allocated from the allocator // make_shared() returns a new object allocated from the allocator
// wrapped in a std::shared_ptr that respects the alignemnt of the type. // wrapped in a std::shared_ptr that respects the alignemnt of the type.
template <typename T, typename... ARGS> template <typename T, typename... ARGS>
...@@ -141,8 +150,12 @@ class Allocator { ...@@ -141,8 +150,12 @@ class Allocator {
Allocator() = default; Allocator() = default;
}; };
///////////////////////////////////////////////////////////////////////////////
// Allocator::Deleter
///////////////////////////////////////////////////////////////////////////////
Allocator::Deleter::Deleter() : allocator(nullptr) {} Allocator::Deleter::Deleter() : allocator(nullptr) {}
Allocator::Deleter::Deleter(Allocator* allocator) : allocator(allocator) {} Allocator::Deleter::Deleter(Allocator* allocator, size_t count)
: allocator(allocator), count(count) {}
template <typename T> template <typename T>
void Allocator::Deleter::operator()(T* object) { void Allocator::Deleter::operator()(T* object) {
...@@ -150,12 +163,15 @@ void Allocator::Deleter::operator()(T* object) { ...@@ -150,12 +163,15 @@ void Allocator::Deleter::operator()(T* object) {
Allocation allocation; Allocation allocation;
allocation.ptr = object; allocation.ptr = object;
allocation.request.size = sizeof(T); allocation.request.size = sizeof(T) * count;
allocation.request.alignment = alignof(T); allocation.request.alignment = alignof(T);
allocation.request.usage = Allocation::Usage::Create; allocation.request.usage = Allocation::Usage::Create;
allocator->free(allocation); allocator->free(allocation);
} }
///////////////////////////////////////////////////////////////////////////////
// Allocator
///////////////////////////////////////////////////////////////////////////////
template <typename T, typename... ARGS> template <typename T, typename... ARGS>
T* Allocator::create(ARGS&&... args) { T* Allocator::create(ARGS&&... args) {
Allocation::Request request; Allocation::Request request;
...@@ -182,14 +198,23 @@ void Allocator::destroy(T* object) { ...@@ -182,14 +198,23 @@ void Allocator::destroy(T* object) {
template <typename T, typename... ARGS> template <typename T, typename... ARGS>
Allocator::unique_ptr<T> Allocator::make_unique(ARGS&&... args) { Allocator::unique_ptr<T> Allocator::make_unique(ARGS&&... args) {
return make_unique_n<T>(1, std::forward<ARGS>(args)...);
}
template <typename T, typename... ARGS>
Allocator::unique_ptr<T> Allocator::make_unique_n(size_t n, ARGS&&... args) {
if (n == 0) {
return nullptr;
}
Allocation::Request request; Allocation::Request request;
request.size = sizeof(T); request.size = sizeof(T) * n;
request.alignment = alignof(T); request.alignment = alignof(T);
request.usage = Allocation::Usage::Create; request.usage = Allocation::Usage::Create;
auto alloc = allocate(request); auto alloc = allocate(request);
new (alloc.ptr) T(std::forward<ARGS>(args)...); new (alloc.ptr) T(std::forward<ARGS>(args)...);
return unique_ptr<T>(reinterpret_cast<T*>(alloc.ptr), Deleter{this}); return unique_ptr<T>(reinterpret_cast<T*>(alloc.ptr), Deleter{this, n});
} }
template <typename T, typename... ARGS> template <typename T, typename... ARGS>
...@@ -201,7 +226,7 @@ std::shared_ptr<T> Allocator::make_shared(ARGS&&... args) { ...@@ -201,7 +226,7 @@ std::shared_ptr<T> Allocator::make_shared(ARGS&&... args) {
auto alloc = allocate(request); auto alloc = allocate(request);
new (alloc.ptr) T(std::forward<ARGS>(args)...); new (alloc.ptr) T(std::forward<ARGS>(args)...);
return std::shared_ptr<T>(reinterpret_cast<T*>(alloc.ptr), Deleter{this}); return std::shared_ptr<T>(reinterpret_cast<T*>(alloc.ptr), Deleter{this, 1});
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#ifndef marl_mutex_h #ifndef marl_mutex_h
#define marl_mutex_h #define marl_mutex_h
#include "export.h"
#include "tsa.h" #include "tsa.h"
#include <condition_variable> #include <condition_variable>
...@@ -32,16 +33,18 @@ namespace marl { ...@@ -32,16 +33,18 @@ namespace marl {
// as these require a std::unique_lock<> which are unsupported by the TSA. // as these require a std::unique_lock<> which are unsupported by the TSA.
class CAPABILITY("mutex") mutex { class CAPABILITY("mutex") mutex {
public: public:
inline void lock() ACQUIRE() { _.lock(); } MARL_NO_EXPORT inline void lock() ACQUIRE() { _.lock(); }
inline void unlock() RELEASE() { _.unlock(); } MARL_NO_EXPORT inline void unlock() RELEASE() { _.unlock(); }
inline bool try_lock() TRY_ACQUIRE(true) { return _.try_lock(); } MARL_NO_EXPORT inline bool try_lock() TRY_ACQUIRE(true) {
return _.try_lock();
}
// wait_locked calls cv.wait() on this already locked mutex. // wait_locked calls cv.wait() on this already locked mutex.
template <typename Predicate> template <typename Predicate>
inline void wait_locked(std::condition_variable& cv, Predicate&& p) MARL_NO_EXPORT inline void wait_locked(std::condition_variable& cv,
REQUIRES(this) { Predicate&& p) REQUIRES(this) {
std::unique_lock<std::mutex> lock(_, std::adopt_lock); std::unique_lock<std::mutex> lock(_, std::adopt_lock);
cv.wait(lock, std::forward<Predicate>(p)); cv.wait(lock, std::forward<Predicate>(p));
lock.release(); // Keep lock held. lock.release(); // Keep lock held.
...@@ -49,9 +52,9 @@ class CAPABILITY("mutex") mutex { ...@@ -49,9 +52,9 @@ class CAPABILITY("mutex") mutex {
// wait_until_locked calls cv.wait() on this already locked mutex. // wait_until_locked calls cv.wait() on this already locked mutex.
template <typename Predicate, typename Time> template <typename Predicate, typename Time>
inline bool wait_until_locked(std::condition_variable& cv, MARL_NO_EXPORT inline bool wait_until_locked(std::condition_variable& cv,
Time&& time, Time&& time,
Predicate&& p) REQUIRES(this) { Predicate&& p) REQUIRES(this) {
std::unique_lock<std::mutex> lock(_, std::adopt_lock); std::unique_lock<std::mutex> lock(_, std::adopt_lock);
auto res = cv.wait_until(lock, std::forward<Time>(time), auto res = cv.wait_until(lock, std::forward<Time>(time),
std::forward<Predicate>(p)); std::forward<Predicate>(p));
......
...@@ -22,10 +22,10 @@ namespace marl { ...@@ -22,10 +22,10 @@ namespace marl {
namespace detail { namespace detail {
inline void parallelizeChain(WaitGroup&) {} MARL_NO_EXPORT inline void parallelizeChain(WaitGroup&) {}
template <typename F, typename... L> template <typename F, typename... L>
inline void parallelizeChain(WaitGroup& wg, F&& f, L&&... l) { MARL_NO_EXPORT inline void parallelizeChain(WaitGroup& wg, F&& f, L&&... l) {
schedule([=] { schedule([=] {
f(); f();
wg.done(); wg.done();
...@@ -49,7 +49,7 @@ inline void parallelizeChain(WaitGroup& wg, F&& f, L&&... l) { ...@@ -49,7 +49,7 @@ inline void parallelizeChain(WaitGroup& wg, F&& f, L&&... l) {
// pass the function that'll take the most time as the first argument. That way // pass the function that'll take the most time as the first argument. That way
// you'll be more likely to avoid the cost of a fiber switch. // you'll be more likely to avoid the cost of a fiber switch.
template <typename F0, typename... FN> template <typename F0, typename... FN>
inline void parallelize(F0&& f0, FN&&... fn) { MARL_NO_EXPORT inline void parallelize(F0&& f0, FN&&... fn) {
WaitGroup wg(sizeof...(FN)); WaitGroup wg(sizeof...(FN));
// Schedule all the functions in fn. // Schedule all the functions in fn.
detail::parallelizeChain(wg, std::forward<FN>(fn)...); detail::parallelizeChain(wg, std::forward<FN>(fn)...);
......
...@@ -53,17 +53,17 @@ class Pool { ...@@ -53,17 +53,17 @@ class Pool {
// item to the pool when the final Loan reference is dropped. // item to the pool when the final Loan reference is dropped.
class Loan { class Loan {
public: public:
inline Loan() = default; MARL_NO_EXPORT inline Loan() = default;
inline Loan(Item*, const std::shared_ptr<Storage>&); MARL_NO_EXPORT inline Loan(Item*, const std::shared_ptr<Storage>&);
inline Loan(const Loan&); MARL_NO_EXPORT inline Loan(const Loan&);
inline Loan(Loan&&); MARL_NO_EXPORT inline Loan(Loan&&);
inline ~Loan(); MARL_NO_EXPORT inline ~Loan();
inline Loan& operator=(const Loan&); MARL_NO_EXPORT inline Loan& operator=(const Loan&);
inline Loan& operator=(Loan&&); MARL_NO_EXPORT inline Loan& operator=(Loan&&);
inline T& operator*(); MARL_NO_EXPORT inline T& operator*();
inline T* operator->() const; MARL_NO_EXPORT inline T* operator->() const;
inline T* get() const; MARL_NO_EXPORT inline T* get() const;
void reset(); MARL_NO_EXPORT inline void reset();
private: private:
Item* item = nullptr; Item* item = nullptr;
...@@ -83,13 +83,13 @@ class Pool { ...@@ -83,13 +83,13 @@ class Pool {
// The backing data of a single item in the pool. // The backing data of a single item in the pool.
struct Item { struct Item {
// get() returns a pointer to the item's data. // get() returns a pointer to the item's data.
inline T* get(); MARL_NO_EXPORT inline T* get();
// construct() calls the constructor on the item's data. // construct() calls the constructor on the item's data.
inline void construct(); MARL_NO_EXPORT inline void construct();
// destruct() calls the destructor on the item's data. // destruct() calls the destructor on the item's data.
inline void destruct(); MARL_NO_EXPORT inline void destruct();
using Data = typename aligned_storage<sizeof(T), alignof(T)>::type; using Data = typename aligned_storage<sizeof(T), alignof(T)>::type;
Data data; Data data;
...@@ -210,31 +210,31 @@ class BoundedPool : public Pool<T> { ...@@ -210,31 +210,31 @@ class BoundedPool : public Pool<T> {
using Item = typename Pool<T>::Item; using Item = typename Pool<T>::Item;
using Loan = typename Pool<T>::Loan; using Loan = typename Pool<T>::Loan;
inline BoundedPool(Allocator* allocator = Allocator::Default); MARL_NO_EXPORT inline BoundedPool(Allocator* allocator = Allocator::Default);
// borrow() borrows a single item from the pool, blocking until an item is // borrow() borrows a single item from the pool, blocking until an item is
// returned if the pool is empty. // returned if the pool is empty.
inline Loan borrow() const; MARL_NO_EXPORT inline Loan borrow() const;
// borrow() borrows count items from the pool, blocking until there are at // borrow() borrows count items from the pool, blocking until there are at
// least count items in the pool. The function f() is called with each // least count items in the pool. The function f() is called with each
// borrowed item. // borrowed item.
// F must be a function with the signature: void(T&&) // F must be a function with the signature: void(T&&)
template <typename F> template <typename F>
inline void borrow(size_t count, const F& f) const; MARL_NO_EXPORT inline void borrow(size_t count, const F& f) const;
// tryBorrow() attempts to borrow a single item from the pool without // tryBorrow() attempts to borrow a single item from the pool without
// blocking. // blocking.
// The boolean of the returned pair is true on success, or false if the pool // The boolean of the returned pair is true on success, or false if the pool
// is empty. // is empty.
inline std::pair<Loan, bool> tryBorrow() const; MARL_NO_EXPORT inline std::pair<Loan, bool> tryBorrow() const;
private: private:
class Storage : public Pool<T>::Storage { class Storage : public Pool<T>::Storage {
public: public:
inline Storage(Allocator* allocator); MARL_NO_EXPORT inline Storage(Allocator* allocator);
inline ~Storage(); MARL_NO_EXPORT inline ~Storage();
inline void return_(Item*) override; MARL_NO_EXPORT inline void return_(Item*) override;
Item items[N]; Item items[N];
marl::mutex mutex; marl::mutex mutex;
...@@ -340,26 +340,27 @@ class UnboundedPool : public Pool<T> { ...@@ -340,26 +340,27 @@ class UnboundedPool : public Pool<T> {
using Item = typename Pool<T>::Item; using Item = typename Pool<T>::Item;
using Loan = typename Pool<T>::Loan; using Loan = typename Pool<T>::Loan;
inline UnboundedPool(Allocator* allocator = Allocator::Default); MARL_NO_EXPORT inline UnboundedPool(
Allocator* allocator = Allocator::Default);
// borrow() borrows a single item from the pool, automatically allocating // borrow() borrows a single item from the pool, automatically allocating
// more items if the pool is empty. // more items if the pool is empty.
// This function does not block. // This function does not block.
inline Loan borrow() const; MARL_NO_EXPORT inline Loan borrow() const;
// borrow() borrows count items from the pool, calling the function f() with // borrow() borrows count items from the pool, calling the function f() with
// each borrowed item. // each borrowed item.
// F must be a function with the signature: void(T&&) // F must be a function with the signature: void(T&&)
// This function does not block. // This function does not block.
template <typename F> template <typename F>
inline void borrow(size_t n, const F& f) const; MARL_NO_EXPORT inline void borrow(size_t n, const F& f) const;
private: private:
class Storage : public Pool<T>::Storage { class Storage : public Pool<T>::Storage {
public: public:
inline Storage(Allocator* allocator); MARL_NO_EXPORT inline Storage(Allocator* allocator);
inline ~Storage(); MARL_NO_EXPORT inline ~Storage();
inline void return_(Item*) override; MARL_NO_EXPORT inline void return_(Item*) override;
Allocator* allocator; Allocator* allocator;
marl::mutex mutex; marl::mutex mutex;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "containers.h" #include "containers.h"
#include "debug.h" #include "debug.h"
#include "deprecated.h" #include "deprecated.h"
#include "export.h"
#include "memory.h" #include "memory.h"
#include "mutex.h" #include "mutex.h"
#include "task.h" #include "task.h"
...@@ -51,83 +52,78 @@ class Scheduler { ...@@ -51,83 +52,78 @@ class Scheduler {
// Config holds scheduler configuration settings that can be passed to the // Config holds scheduler configuration settings that can be passed to the
// Scheduler constructor. // Scheduler constructor.
struct Config { struct Config {
static constexpr size_t DefaultFiberStackSize = 1024 * 1024;
// Per-worker-thread settings. // Per-worker-thread settings.
struct WorkerThread { struct WorkerThread {
// Total number of dedicated worker threads to spawn for the scheduler.
int count = 0; int count = 0;
// Initializer function to call after thread creation and before any work
// is run by the thread.
ThreadInitializer initializer; ThreadInitializer initializer;
// Thread affinity policy to use for worker threads.
std::shared_ptr<Thread::Affinity::Policy> affinityPolicy; std::shared_ptr<Thread::Affinity::Policy> affinityPolicy;
}; };
WorkerThread workerThread; WorkerThread workerThread;
// Memory allocator to use for the scheduler and internal allocations. // Memory allocator to use for the scheduler and internal allocations.
Allocator* allocator = Allocator::Default; Allocator* allocator = Allocator::Default;
// Size of each fiber stack. This may be rounded up to the nearest
// allocation granularity for the given platform.
size_t fiberStackSize = DefaultFiberStackSize;
// allCores() returns a Config with a worker thread for each of the logical // allCores() returns a Config with a worker thread for each of the logical
// cpus available to the process. // cpus available to the process.
MARL_EXPORT
static Config allCores(); static Config allCores();
// Fluent setters that return this Config so set calls can be chained. // Fluent setters that return this Config so set calls can be chained.
inline Config& setAllocator(Allocator*); MARL_NO_EXPORT inline Config& setAllocator(Allocator*);
inline Config& setWorkerThreadCount(int); MARL_NO_EXPORT inline Config& setFiberStackSize(size_t);
inline Config& setWorkerThreadInitializer(const ThreadInitializer&); MARL_NO_EXPORT inline Config& setWorkerThreadCount(int);
inline Config& setWorkerThreadAffinityPolicy( MARL_NO_EXPORT inline Config& setWorkerThreadInitializer(
const ThreadInitializer&);
MARL_NO_EXPORT inline Config& setWorkerThreadAffinityPolicy(
const std::shared_ptr<Thread::Affinity::Policy>&); const std::shared_ptr<Thread::Affinity::Policy>&);
}; };
// Constructor. // Constructor.
MARL_EXPORT
Scheduler(const Config&); Scheduler(const Config&);
// Destructor. // Destructor.
// Blocks until the scheduler is unbound from all threads before returning. // Blocks until the scheduler is unbound from all threads before returning.
MARL_EXPORT
~Scheduler(); ~Scheduler();
// get() returns the scheduler bound to the current thread. // get() returns the scheduler bound to the current thread.
MARL_EXPORT
static Scheduler* get(); static Scheduler* get();
// bind() binds this scheduler to the current thread. // bind() binds this scheduler to the current thread.
// There must be no existing scheduler bound to the thread prior to calling. // There must be no existing scheduler bound to the thread prior to calling.
MARL_EXPORT
void bind(); void bind();
// unbind() unbinds the scheduler currently bound to the current thread. // unbind() unbinds the scheduler currently bound to the current thread.
// There must be a existing scheduler bound to the thread prior to calling. // There must be a existing scheduler bound to the thread prior to calling.
// unbind() flushes any enqueued tasks on the single-threaded worker before // unbind() flushes any enqueued tasks on the single-threaded worker before
// returning. // returning.
MARL_EXPORT
static void unbind(); static void unbind();
// enqueue() queues the task for asynchronous execution. // enqueue() queues the task for asynchronous execution.
MARL_EXPORT
void enqueue(Task&& task); void enqueue(Task&& task);
// config() returns the Config that was used to build the schededuler. // config() returns the Config that was used to build the schededuler.
MARL_EXPORT
const Config& config() const; const Config& config() const;
#if MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS
MARL_DEPRECATED(139, "use Scheduler::Scheduler(const Config&)")
Scheduler(Allocator* allocator = Allocator::Default);
// setThreadInitializer() sets the worker thread initializer function which
// will be called for each new worker thread spawned.
// The initializer will only be called on newly created threads (call
// setThreadInitializer() before setWorkerThreadCount()).
MARL_DEPRECATED(139, "use Config::setWorkerThreadInitializer()")
void setThreadInitializer(const std::function<void()>& init);
// getThreadInitializer() returns the thread initializer function set by
// setThreadInitializer().
MARL_DEPRECATED(139, "use config().workerThread.initializer")
std::function<void()> getThreadInitializer();
// setWorkerThreadCount() adjusts the number of dedicated worker threads.
// A count of 0 puts the scheduler into single-threaded mode.
// Note: Currently the number of threads cannot be adjusted once tasks
// have been enqueued. This restriction may be lifted at a later time.
MARL_DEPRECATED(139, "use Config::setWorkerThreadCount()")
void setWorkerThreadCount(int count);
// getWorkerThreadCount() returns the number of worker threads.
MARL_DEPRECATED(139, "use config().workerThread.count")
int getWorkerThreadCount();
#endif // MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS
// Fibers expose methods to perform cooperative multitasking and are // Fibers expose methods to perform cooperative multitasking and are
// automatically created by the Scheduler. // automatically created by the Scheduler.
// //
...@@ -141,6 +137,7 @@ class Scheduler { ...@@ -141,6 +137,7 @@ class Scheduler {
public: public:
// current() returns the currently executing fiber, or nullptr if called // current() returns the currently executing fiber, or nullptr if called
// without a bound scheduler. // without a bound scheduler.
MARL_EXPORT
static Fiber* current(); static Fiber* current();
// wait() suspends execution of this Fiber until the Fiber is woken up with // wait() suspends execution of this Fiber until the Fiber is woken up with
...@@ -155,6 +152,7 @@ class Scheduler { ...@@ -155,6 +152,7 @@ class Scheduler {
// will be locked before wait() returns. // will be locked before wait() returns.
// pred will be always be called with the lock held. // pred will be always be called with the lock held.
// wait() must only be called on the currently executing fiber. // wait() must only be called on the currently executing fiber.
MARL_EXPORT
void wait(marl::lock& lock, const Predicate& pred); void wait(marl::lock& lock, const Predicate& pred);
// wait() suspends execution of this Fiber until the Fiber is woken up with // wait() suspends execution of this Fiber until the Fiber is woken up with
...@@ -172,9 +170,10 @@ class Scheduler { ...@@ -172,9 +170,10 @@ class Scheduler {
// pred will be always be called with the lock held. // pred will be always be called with the lock held.
// wait() must only be called on the currently executing fiber. // wait() must only be called on the currently executing fiber.
template <typename Clock, typename Duration> template <typename Clock, typename Duration>
inline bool wait(marl::lock& lock, MARL_NO_EXPORT inline bool wait(
const std::chrono::time_point<Clock, Duration>& timeout, marl::lock& lock,
const Predicate& pred); const std::chrono::time_point<Clock, Duration>& timeout,
const Predicate& pred);
// wait() suspends execution of this Fiber until the Fiber is woken up with // wait() suspends execution of this Fiber until the Fiber is woken up with
// a call to notify(). // a call to notify().
...@@ -190,7 +189,7 @@ class Scheduler { ...@@ -190,7 +189,7 @@ class Scheduler {
// wait() and notify() are made by the same thread. // wait() and notify() are made by the same thread.
// //
// Use with extreme caution. // Use with extreme caution.
inline void wait(); MARL_NO_EXPORT inline void wait();
// wait() suspends execution of this Fiber until the Fiber is woken up with // wait() suspends execution of this Fiber until the Fiber is woken up with
// a call to notify(), or sometime after the timeout is reached. // a call to notify(), or sometime after the timeout is reached.
...@@ -207,11 +206,13 @@ class Scheduler { ...@@ -207,11 +206,13 @@ class Scheduler {
// //
// Use with extreme caution. // Use with extreme caution.
template <typename Clock, typename Duration> template <typename Clock, typename Duration>
inline bool wait(const std::chrono::time_point<Clock, Duration>& timeout); MARL_NO_EXPORT inline bool wait(
const std::chrono::time_point<Clock, Duration>& timeout);
// notify() reschedules the suspended Fiber for execution. // notify() reschedules the suspended Fiber for execution.
// notify() is usually only called when the predicate for one or more wait() // notify() is usually only called when the predicate for one or more wait()
// calls will likely return true. // calls will likely return true.
MARL_EXPORT
void notify(); void notify();
// id is the thread-unique identifier of the Fiber. // id is the thread-unique identifier of the Fiber.
...@@ -277,10 +278,6 @@ class Scheduler { ...@@ -277,10 +278,6 @@ class Scheduler {
Scheduler& operator=(const Scheduler&) = delete; Scheduler& operator=(const Scheduler&) = delete;
Scheduler& operator=(Scheduler&&) = delete; Scheduler& operator=(Scheduler&&) = delete;
// Stack size in bytes of a new fiber.
// TODO: Make configurable so the default size can be reduced.
static constexpr size_t FiberStackSize = 1024 * 1024;
// Maximum number of worker threads. // Maximum number of worker threads.
static constexpr size_t MaxWorkerThreads = 256; static constexpr size_t MaxWorkerThreads = 256;
...@@ -349,12 +346,14 @@ class Scheduler { ...@@ -349,12 +346,14 @@ class Scheduler {
// wait() suspends execution of the current task until the predicate pred // wait() suspends execution of the current task until the predicate pred
// returns true or the optional timeout is reached. // returns true or the optional timeout is reached.
// See Fiber::wait() for more information. // See Fiber::wait() for more information.
MARL_EXPORT
bool wait(marl::lock& lock, const TimePoint* timeout, const Predicate& pred) bool wait(marl::lock& lock, const TimePoint* timeout, const Predicate& pred)
EXCLUDES(work.mutex); EXCLUDES(work.mutex);
// wait() suspends execution of the current task until the fiber is // wait() suspends execution of the current task until the fiber is
// notified, or the optional timeout is reached. // notified, or the optional timeout is reached.
// See Fiber::wait() for more information. // See Fiber::wait() for more information.
MARL_EXPORT
bool wait(const TimePoint* timeout) EXCLUDES(work.mutex); bool wait(const TimePoint* timeout) EXCLUDES(work.mutex);
// suspend() suspends the currenetly executing Fiber until the fiber is // suspend() suspends the currenetly executing Fiber until the fiber is
...@@ -491,12 +490,8 @@ class Scheduler { ...@@ -491,12 +490,8 @@ class Scheduler {
// The scheduler currently bound to the current thread. // The scheduler currently bound to the current thread.
static thread_local Scheduler* bound; static thread_local Scheduler* bound;
#if MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS // The immutable configuration used to build the scheduler.
Config cfg;
mutex threadInitFuncMutex;
#else
const Config cfg; const Config cfg;
#endif
std::array<std::atomic<int>, 8> spinningWorkers; std::array<std::atomic<int>, 8> spinningWorkers;
std::atomic<unsigned int> nextSpinningWorkerIdx = {0x8000000}; std::atomic<unsigned int> nextSpinningWorkerIdx = {0x8000000};
...@@ -525,6 +520,11 @@ Scheduler::Config& Scheduler::Config::setAllocator(Allocator* alloc) { ...@@ -525,6 +520,11 @@ Scheduler::Config& Scheduler::Config::setAllocator(Allocator* alloc) {
return *this; return *this;
} }
Scheduler::Config& Scheduler::Config::setFiberStackSize(size_t size) {
fiberStackSize = size;
return *this;
}
Scheduler::Config& Scheduler::Config::setWorkerThreadCount(int count) { Scheduler::Config& Scheduler::Config::setWorkerThreadCount(int count) {
workerThread.count = count; workerThread.count = count;
return *this; return *this;
...@@ -592,7 +592,7 @@ inline void schedule(Function&& f, Args&&... args) { ...@@ -592,7 +592,7 @@ inline void schedule(Function&& f, Args&&... args) {
MARL_ASSERT_HAS_BOUND_SCHEDULER("marl::schedule"); MARL_ASSERT_HAS_BOUND_SCHEDULER("marl::schedule");
auto scheduler = Scheduler::get(); auto scheduler = Scheduler::get();
scheduler->enqueue( scheduler->enqueue(
std::bind(std::forward<Function>(f), std::forward<Args>(args)...)); Task(std::bind(std::forward<Function>(f), std::forward<Args>(args)...)));
} }
// schedule() schedules the function f to be asynchronously called using the // schedule() schedules the function f to be asynchronously called using the
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#ifndef marl_task_h #ifndef marl_task_h
#define marl_task_h #define marl_task_h
#include "export.h"
#include <functional> #include <functional>
namespace marl { namespace marl {
...@@ -34,24 +36,25 @@ class Task { ...@@ -34,24 +36,25 @@ class Task {
SameThread = 1, SameThread = 1,
}; };
inline Task(); MARL_NO_EXPORT inline Task();
inline Task(const Task&); MARL_NO_EXPORT inline Task(const Task&);
inline Task(Task&&); MARL_NO_EXPORT inline Task(Task&&);
inline Task(const Function& function, Flags flags = Flags::None); MARL_NO_EXPORT inline Task(const Function& function,
inline Task(Function&& function, Flags flags = Flags::None); Flags flags = Flags::None);
inline Task& operator=(const Task&); MARL_NO_EXPORT inline Task(Function&& function, Flags flags = Flags::None);
inline Task& operator=(Task&&); MARL_NO_EXPORT inline Task& operator=(const Task&);
inline Task& operator=(const Function&); MARL_NO_EXPORT inline Task& operator=(Task&&);
inline Task& operator=(Function&&); MARL_NO_EXPORT inline Task& operator=(const Function&);
MARL_NO_EXPORT inline Task& operator=(Function&&);
// operator bool() returns true if the Task has a valid function. // operator bool() returns true if the Task has a valid function.
inline operator bool() const; MARL_NO_EXPORT inline operator bool() const;
// operator()() runs the task. // operator()() runs the task.
inline void operator()() const; MARL_NO_EXPORT inline void operator()() const;
// is() returns true if the Task was created with the given flag. // is() returns true if the Task was created with the given flag.
inline bool is(Flags flag) const; MARL_NO_EXPORT inline bool is(Flags flag) const;
private: private:
Function function; Function function;
......
...@@ -15,9 +15,10 @@ ...@@ -15,9 +15,10 @@
#ifndef marl_thread_h #ifndef marl_thread_h
#define marl_thread_h #define marl_thread_h
#include <functional>
#include "containers.h" #include "containers.h"
#include "export.h"
#include <functional>
namespace marl { namespace marl {
...@@ -42,8 +43,8 @@ class Thread { ...@@ -42,8 +43,8 @@ class Thread {
}; };
// Comparison functions // Comparison functions
inline bool operator==(const Core&) const; MARL_NO_EXPORT inline bool operator==(const Core&) const;
inline bool operator<(const Core&) const; MARL_NO_EXPORT inline bool operator<(const Core&) const;
}; };
// Affinity holds the affinity mask for a thread - a description of what cores // Affinity holds the affinity mask for a thread - a description of what cores
...@@ -70,7 +71,7 @@ class Thread { ...@@ -70,7 +71,7 @@ class Thread {
// Windows requires that each thread is only associated with a // Windows requires that each thread is only associated with a
// single affinity group, so the Policy's returned affinity will contain // single affinity group, so the Policy's returned affinity will contain
// cores all from the same group. // cores all from the same group.
static std::shared_ptr<Policy> anyOf( MARL_EXPORT static std::shared_ptr<Policy> anyOf(
Affinity&& affinity, Affinity&& affinity,
Allocator* allocator = Allocator::Default); Allocator* allocator = Allocator::Default);
...@@ -78,36 +79,39 @@ class Thread { ...@@ -78,36 +79,39 @@ class Thread {
// core from affinity. The single enabled core in the Policy's returned // core from affinity. The single enabled core in the Policy's returned
// affinity is: // affinity is:
// affinity[threadId % affinity.count()] // affinity[threadId % affinity.count()]
static std::shared_ptr<Policy> oneOf( MARL_EXPORT static std::shared_ptr<Policy> oneOf(
Affinity&& affinity, Affinity&& affinity,
Allocator* allocator = Allocator::Default); Allocator* allocator = Allocator::Default);
// get() returns the thread Affinity for the for the given thread by id. // get() returns the thread Affinity for the for the given thread by id.
virtual Affinity get(uint32_t threadId, Allocator* allocator) const = 0; MARL_EXPORT virtual Affinity get(uint32_t threadId,
Allocator* allocator) const = 0;
}; };
Affinity(Allocator*); MARL_EXPORT Affinity(Allocator*);
Affinity(Affinity&&);
Affinity(const Affinity&, Allocator* allocator); MARL_EXPORT Affinity(Affinity&&);
MARL_EXPORT Affinity(const Affinity&, Allocator* allocator);
// all() returns an Affinity with all the cores available to the process. // all() returns an Affinity with all the cores available to the process.
static Affinity all(Allocator* allocator = Allocator::Default); MARL_EXPORT static Affinity all(Allocator* allocator = Allocator::Default);
Affinity(std::initializer_list<Core>, Allocator* allocator); MARL_EXPORT Affinity(std::initializer_list<Core>, Allocator* allocator);
// count() returns the number of enabled cores in the affinity. // count() returns the number of enabled cores in the affinity.
size_t count() const; MARL_EXPORT size_t count() const;
// operator[] returns the i'th enabled core from this affinity. // operator[] returns the i'th enabled core from this affinity.
Core operator[](size_t index) const; MARL_EXPORT Core operator[](size_t index) const;
// add() adds the cores from the given affinity to this affinity. // add() adds the cores from the given affinity to this affinity.
// This affinity is returned to allow for fluent calls. // This affinity is returned to allow for fluent calls.
Affinity& add(const Affinity&); MARL_EXPORT Affinity& add(const Affinity&);
// remove() removes the cores from the given affinity from this affinity. // remove() removes the cores from the given affinity from this affinity.
// This affinity is returned to allow for fluent calls. // This affinity is returned to allow for fluent calls.
Affinity& remove(const Affinity&); MARL_EXPORT Affinity& remove(const Affinity&);
private: private:
Affinity(const Affinity&) = delete; Affinity(const Affinity&) = delete;
...@@ -115,25 +119,27 @@ class Thread { ...@@ -115,25 +119,27 @@ class Thread {
containers::vector<Core, 32> cores; containers::vector<Core, 32> cores;
}; };
Thread() = default; MARL_EXPORT Thread() = default;
Thread(Thread&&);
Thread& operator=(Thread&&); MARL_EXPORT Thread(Thread&&);
MARL_EXPORT Thread& operator=(Thread&&);
// Start a new thread using the given affinity that calls func. // Start a new thread using the given affinity that calls func.
Thread(Affinity&& affinity, Func&& func); MARL_EXPORT Thread(Affinity&& affinity, Func&& func);
~Thread(); MARL_EXPORT ~Thread();
// join() blocks until the thread completes. // join() blocks until the thread completes.
void join(); MARL_EXPORT void join();
// setName() sets the name of the currently executing thread for displaying // setName() sets the name of the currently executing thread for displaying
// in a debugger. // in a debugger.
static void setName(const char* fmt, ...); MARL_EXPORT static void setName(const char* fmt, ...);
// numLogicalCPUs() returns the number of available logical CPU cores for // numLogicalCPUs() returns the number of available logical CPU cores for
// the system. // the system.
static unsigned int numLogicalCPUs(); MARL_EXPORT static unsigned int numLogicalCPUs();
private: private:
Thread(const Thread&) = delete; Thread(const Thread&) = delete;
......
...@@ -68,45 +68,45 @@ class Ticket { ...@@ -68,45 +68,45 @@ class Ticket {
class Queue { class Queue {
public: public:
// take() returns a single ticket from the queue. // take() returns a single ticket from the queue.
inline Ticket take(); MARL_NO_EXPORT inline Ticket take();
// take() retrieves count tickets from the queue, calling f() with each // take() retrieves count tickets from the queue, calling f() with each
// retrieved ticket. // retrieved ticket.
// F must be a function of the signature: void(Ticket&&) // F must be a function of the signature: void(Ticket&&)
template <typename F> template <typename F>
inline void take(size_t count, const F& f); MARL_NO_EXPORT inline void take(size_t count, const F& f);
private: private:
std::shared_ptr<Shared> shared = std::make_shared<Shared>(); std::shared_ptr<Shared> shared = std::make_shared<Shared>();
UnboundedPool<Record> pool; UnboundedPool<Record> pool;
}; };
inline Ticket() = default; MARL_NO_EXPORT inline Ticket() = default;
inline Ticket(const Ticket& other) = default; MARL_NO_EXPORT inline Ticket(const Ticket& other) = default;
inline Ticket(Ticket&& other) = default; MARL_NO_EXPORT inline Ticket(Ticket&& other) = default;
inline Ticket& operator=(const Ticket& other) = default; MARL_NO_EXPORT inline Ticket& operator=(const Ticket& other) = default;
// wait() blocks until the ticket is called. // wait() blocks until the ticket is called.
inline void wait() const; MARL_NO_EXPORT inline void wait() const;
// done() marks the ticket as finished and calls the next ticket. // done() marks the ticket as finished and calls the next ticket.
inline void done() const; MARL_NO_EXPORT inline void done() const;
// onCall() registers the function f to be invoked when this ticket is // onCall() registers the function f to be invoked when this ticket is
// called. If the ticket is already called prior to calling onCall(), then // called. If the ticket is already called prior to calling onCall(), then
// f() will be executed immediately. // f() will be executed immediately.
// F must be a function of the OnCall signature. // F must be a function of the OnCall signature.
template <typename F> template <typename F>
inline void onCall(F&& f) const; MARL_NO_EXPORT inline void onCall(F&& f) const;
private: private:
// Internal doubly-linked-list data structure. One per ticket instance. // Internal doubly-linked-list data structure. One per ticket instance.
struct Record { struct Record {
inline ~Record(); MARL_NO_EXPORT inline ~Record();
inline void done(); MARL_NO_EXPORT inline void done();
inline void callAndUnlock(marl::lock& lock); MARL_NO_EXPORT inline void callAndUnlock(marl::lock& lock);
inline void unlink(); // guarded by shared->mutex MARL_NO_EXPORT inline void unlink(); // guarded by shared->mutex
ConditionVariable isCalledCondVar; ConditionVariable isCalledCondVar;
...@@ -124,7 +124,7 @@ class Ticket { ...@@ -124,7 +124,7 @@ class Ticket {
Record tail; Record tail;
}; };
inline Ticket(Loan<Record>&& record); MARL_NO_EXPORT inline Ticket(Loan<Record>&& record);
Loan<Record> record; Loan<Record> record;
}; };
...@@ -148,7 +148,7 @@ template <typename Function> ...@@ -148,7 +148,7 @@ template <typename Function>
void Ticket::onCall(Function&& f) const { void Ticket::onCall(Function&& f) const {
marl::lock lock(record->shared->mutex); marl::lock lock(record->shared->mutex);
if (record->isCalled) { if (record->isCalled) {
marl::schedule(std::move(f)); marl::schedule(std::forward<Function>(f));
return; return;
} }
if (record->onCall) { if (record->onCall) {
...@@ -159,9 +159,10 @@ void Ticket::onCall(Function&& f) const { ...@@ -159,9 +159,10 @@ void Ticket::onCall(Function&& f) const {
} }
OnCall a, b; OnCall a, b;
}; };
record->onCall = std::move(Joined{std::move(record->onCall), std::move(f)}); record->onCall =
std::move(Joined{std::move(record->onCall), std::forward<Function>(f)});
} else { } else {
record->onCall = std::move(f); record->onCall = std::forward<Function>(f);
} }
} }
......
...@@ -51,22 +51,22 @@ namespace marl { ...@@ -51,22 +51,22 @@ namespace marl {
class WaitGroup { class WaitGroup {
public: public:
// Constructs the WaitGroup with the specified initial count. // Constructs the WaitGroup with the specified initial count.
inline WaitGroup(unsigned int initialCount = 0, MARL_NO_EXPORT inline WaitGroup(unsigned int initialCount = 0,
Allocator* allocator = Allocator::Default); Allocator* allocator = Allocator::Default);
// add() increments the internal counter by count. // add() increments the internal counter by count.
inline void add(unsigned int count = 1) const; MARL_NO_EXPORT inline void add(unsigned int count = 1) const;
// done() decrements the internal counter by one. // done() decrements the internal counter by one.
// Returns true if the internal count has reached zero. // Returns true if the internal count has reached zero.
inline bool done() const; MARL_NO_EXPORT inline bool done() const;
// wait() blocks until the WaitGroup counter reaches zero. // wait() blocks until the WaitGroup counter reaches zero.
inline void wait() const; MARL_NO_EXPORT inline void wait() const;
private: private:
struct Data { struct Data {
inline Data(Allocator* allocator); MARL_NO_EXPORT inline Data(Allocator* allocator);
std::atomic<unsigned int> count = {0}; std::atomic<unsigned int> count = {0};
ConditionVariable cv; ConditionVariable cv;
......
#!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
set -e # Fail on any error.
license-checker
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/license-check/presubmit.sh"
#!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
set -e # Fail on any error.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
docker run --rm -i \
--volume "${ROOT_DIR}:${ROOT_DIR}" \
--workdir "${ROOT_DIR}" \
--entrypoint "${SCRIPT_DIR}/presubmit-docker.sh" \
"gcr.io/shaderc-build/radial-build:latest"
#!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
set -e # Fail on any error.
cd "${ROOT_DIR}"
function show_cmds { set -x; }
function hide_cmds { { set +x; } 2>/dev/null; }
function status {
echo ""
echo "*****************************************************************"
echo "* $@"
echo "*****************************************************************"
echo ""
}
status "Fetching submodules"
git submodule update --init
status "Setting up environment"
if [ "$BUILD_SYSTEM" == "cmake" ]; then
SRC_DIR=$(pwd)
BUILD_DIR=/tmp/marl-build
INSTALL_DIR=${BUILD_DIR}/install
COMMON_CMAKE_FLAGS=""
COMMON_CMAKE_FLAGS+=" -DCMAKE_BUILD_TYPE=${BUILD_TYPE}"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_EXAMPLES=1"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_TESTS=1"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_BENCHMARKS=1"
COMMON_CMAKE_FLAGS+=" -DMARL_WARNINGS_AS_ERRORS=1"
COMMON_CMAKE_FLAGS+=" -DMARL_DEBUG_ENABLED=1"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_SHARED=${BUILD_SHARED:-0}"
COMMON_CMAKE_FLAGS+=" -DBENCHMARK_ENABLE_INSTALL=0"
COMMON_CMAKE_FLAGS+=" -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}"
if [ "$BUILD_SANITIZER" == "asan" ]; then
COMMON_CMAKE_FLAGS+=" -DMARL_ASAN=1"
elif [ "$BUILD_SANITIZER" == "msan" ]; then
COMMON_CMAKE_FLAGS+=" -DMARL_MSAN=1"
elif [ "$BUILD_SANITIZER" == "tsan" ]; then
COMMON_CMAKE_FLAGS+=" -DMARL_TSAN=1"
fi
# clean
# Ensures BUILD_DIR is empty.
function clean {
if [ -d ${BUILD_DIR} ]; then
rm -fr ${BUILD_DIR}
fi
mkdir ${BUILD_DIR}
}
# build <description> <flags>
# Cleans build directory and performs a build using the provided CMake flags.
function build {
DESCRIPTION=$1
CMAKE_FLAGS=$2
status "Building ${DESCRIPTION}"
clean
cd ${BUILD_DIR}
show_cmds
cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS}
make --jobs=$(sysctl -n hw.logicalcpu)
hide_cmds
}
# test <description>
# Runs the pre-built unit tests (if not an NDK build).
function test {
DESCRIPTION=$1
status "Testing ${DESCRIPTION}"
cd ${BUILD_DIR}
show_cmds
if [ "$BUILD_TOOLCHAIN" != "ndk" ]; then
./marl-unittests
./fractal
./hello_task
./primes > /dev/null
./tasks_in_tasks
fi
hide_cmds
}
# install <description>
# Installs the pre-built library to ${INSTALL_DIR}.
function install {
DESCRIPTION=$1
status "Installing ${DESCRIPTION}"
cd ${BUILD_DIR}
show_cmds
make install
hide_cmds
}
# build <description> <flags>
# Cleans build directory and performs a build using the provided CMake
# flags, then runs tests.
function buildAndTest {
DESCRIPTION=$1
CMAKE_FLAGS=$2
build "$DESCRIPTION" "$CMAKE_FLAGS"
test "$DESCRIPTION"
}
# build <description> <flags>
# Cleans build directory and performs a build using the provided CMake
# flags, then installs the library to ${INSTALL_DIR}.
function buildAndInstall {
DESCRIPTION=$1
CMAKE_FLAGS=$2
build "$DESCRIPTION" "$CMAKE_FLAGS"
install "$DESCRIPTION"
}
if [ -n "$RUN_TESTS" ]; then
buildAndTest "marl for test" ""
fi
buildAndInstall "marl for install" "-DMARL_INSTALL=1"
if [ -n "$BUILD_ARTIFACTS" ]; then
status "Copying build artifacts"
show_cmds
tar -czvf "$BUILD_ARTIFACTS/build.tar.gz" -C "$INSTALL_DIR" .
hide_cmds
fi
elif [ "$BUILD_SYSTEM" == "bazel" ]; then
# Get bazel
BAZEL_DIR="${ROOT_DIR}/bazel"
curl -L -k -O -s https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-installer-darwin-x86_64.sh
mkdir "${BAZEL_DIR}"
sh bazel-0.29.1-installer-darwin-x86_64.sh --prefix="${BAZEL_DIR}"
rm bazel-0.29.1-installer-darwin-x86_64.sh
BAZEL="${BAZEL_DIR}/bin/bazel"
show_cmds
"${BAZEL}" test //:tests --test_output=all
"${BAZEL}" run //examples:fractal
"${BAZEL}" run //examples:hello_task
"${BAZEL}" run //examples:primes > /dev/null
"${BAZEL}" run //examples:tasks_in_tasks
hide_cmds
else
status "Unknown build system: $BUILD_SYSTEM"
exit 1
fi
status "Done"
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/macos/presubmit.sh" build_file: "marl/kokoro/macos/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/macos/presubmit.sh" build_file: "marl/kokoro/macos/presubmit.sh"
env_vars { env_vars {
......
#!/bin/bash #!/bin/bash
set -e # Fail on any error. # Copyright 2020 The Marl Authors.
set -x # Display commands being run. #
# Licensed under the Apache License, Version 2.0 (the "License");
BUILD_ROOT=$PWD # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
cd github/marl #
# https://www.apache.org/licenses/LICENSE-2.0
git submodule update --init #
# Unless required by applicable law or agreed to in writing, software
if [ "$BUILD_SYSTEM" == "cmake" ]; then # distributed under the License is distributed on an "AS IS" BASIS,
mkdir build # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
cd build # See the License for the specific language governing permissions and
# limitations under the License.
cmake .. -DMARL_BUILD_EXAMPLES=1 \
-DMARL_BUILD_TESTS=1 \ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
-DMARL_BUILD_BENCHMARKS=1 \ ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
-DMARL_WARNINGS_AS_ERRORS=1 \ RUN_TESTS=1
-DMARL_DEBUG_ENABLED=1
. "${SCRIPT_DIR}/build.sh"
make -j$(sysctl -n hw.logicalcpu)
./marl-unittests
./fractal
./primes > /dev/null
elif [ "$BUILD_SYSTEM" == "bazel" ]; then
# Get bazel
curl -L -k -O -s https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-installer-darwin-x86_64.sh
mkdir $BUILD_ROOT/bazel
sh bazel-0.29.1-installer-darwin-x86_64.sh --prefix=$BUILD_ROOT/bazel
rm bazel-0.29.1-installer-darwin-x86_64.sh
# Build and run
$BUILD_ROOT/bazel/bin/bazel test //:tests --test_output=all
$BUILD_ROOT/bazel/bin/bazel run //examples:fractal
$BUILD_ROOT/bazel/bin/bazel run //examples:primes > /dev/null
else
echo "Unknown build system: $BUILD_SYSTEM"
exit 1
fi
\ No newline at end of file
#!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
BUILD_ARTIFACTS=${KOKORO_ARTIFACTS_DIR}
. "${SCRIPT_DIR}/build.sh"
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/ubuntu/release.sh"
action {
define_artifacts {
regex: "*"
}
}
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
# env_vars {
# key: "BUILD_TYPE"
# value: "Debug"
# }
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_TOOLCHAIN"
value: "gcc"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/ubuntu/release.sh"
action {
define_artifacts {
regex: "*"
}
}
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
# env_vars {
# key: "BUILD_TYPE"
# value: "Release"
# }
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_TOOLCHAIN"
value: "gcc"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/macos/release.sh"
action {
define_artifacts {
regex: "*"
}
}
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
# env_vars {
# key: "BUILD_TYPE"
# value: "Debug"
# }
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/macos/release.sh"
action {
define_artifacts {
regex: "*"
}
}
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
# env_vars {
# key: "BUILD_TYPE"
# value: "Release"
# }
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/windows/release.bat"
action {
define_artifacts {
regex: "*"
}
}
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
# env_vars {
# key: "BUILD_TYPE"
# value: "Debug"
# }
env_vars {
key: "BUILD_GENERATOR"
value: "Visual Studio 16 2019"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/windows/release.bat"
action {
define_artifacts {
regex: "*"
}
}
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
# env_vars {
# key: "BUILD_TYPE"
# value: "Release"
# }
env_vars {
key: "BUILD_GENERATOR"
value: "Visual Studio 16 2019"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "arm64-v8a"
}
env_vars {
key: "BUILD_TOOLCHAIN"
value: "ndk"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "armeabi-v7a"
}
env_vars {
key: "BUILD_TOOLCHAIN"
value: "ndk"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x86_64"
}
env_vars {
key: "BUILD_TOOLCHAIN"
value: "ndk"
}
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFS+1SABEACnmkESkY7eZq0GhDjbkWpKmURGk9+ycsfAhA44NqUvf4tk1GPM
5SkJ/fYedYZJaDVhIp98fHgucD0O+vjOzghtgwtITusYjiPHPFBd/MN+MQqSEAP+
LUa/kjHLjgyXxKhFUIDGVaDWL5tKOA7/AQKl1TyJ8lz89NHQoUHFsF/hu10+qhJe
V65d32MXFehIUSvegh8DrPuExrliSiORO4HOhuc6151dWA4YBWVg4rX5kfKrGMMT
pTWnSSZtgoRhkKW2Ey8cmZUqPuUJIfWyeNVu1e4SFtAivLvu/Ymz2WBJcNA1ZlTr
RCOR5SIRgZ453pQnI/Bzna2nnJ/TV1gGJIGRahj/ini0cs2x1CILfS/YJQ3rWGGo
OxwG0BVmPk0cmLVtyTq8gUPwxcPUd6WcBKhot3TDMlrffZACnQwQjlVjk5S1dEEz
atUfpEuNitU9WOM4jr/gjv36ZNCOWm95YwLhsuci/NddBN8HXhyvs+zYTVZEXa2W
l/FqOdQsQqZBcJjjWckGKhESdd7934+cesGD3O8KaeSGxww7slJrS0+6QJ8oBoAB
P/WCn/y2AiY2syEKp3wYIGJyAbsm542zMZ4nc7pYfSu49mcyhQQICmqN5QvOyYUx
OSqwbAOUNtlOyeRLZNIKoXtTqWDEu5aEiDROTw6Rkq+dIcxPNgOLdeQ3HwARAQAB
tCFIYW5zIFdlbm5ib3JnIDxoYW5zQGNocm9taXVtLm9yZz6JAlQEEwEKAD4WIQS2
yPmCgrlE47DVwlMPwwQuNFrQXQUCXKW+LwIbAwUJDwUmjQULCQgHAgYVCgkICwIE
FgIDAQIeAQIXgAAKCRAPwwQuNFrQXXw+EACc4n7pYF89qmi6k4u1H5PLPcRVw4Ch
zY293N5JT8dM7c5Q0opPcgSS625SzAzEA8I3kRakFMsYZmJ7NFeFwIV7iJnaolft
iGCinbnB6bF8NnaEUOU0Pl4ByAuPiZqq8t5ORWUnZX/iRtOFEmCyRWHJPxCPFcJG
XCmQHTwnucePFdvNoIHN8vbkrHU32SUQ3iL4aEH92Y2s4D3WoNMW7g3b7srRynO1
pzrT+bhihrl1MAnR6FiS4lSjw7VaEon1PJyaxs6OYO2x/fEz+uUnNPYZGhHQDTQ8
DUyXNlXQ1mOOTMAwxg5JmqWfA2y1pmgJGpKe92t6vpVe9E90GBS9oCvSFXzItNg+
p+9ogNDxMWnT48fygCqDVpk/PLdlyuNAQfuvtcZb8h5y1bzcwwBGHWb9McG12Z/K
JpcWvSQe/eZ9uHcyj2+b7SQHIJL9eaBsyhgvv573PK62Rc8fze+HtwZMWMvw5Fsc
+q5pJ8JS8y3s/EZYJ8URQ00QWOL6DDN1ik0vjxZ6zf+dpK1/3jToSrTnsY5TxXAM
gxeoFVhAtccnoAYY2zp2Dp7JonGNqXrE8rjMe67QBWzVUADgWMlCvFZ4W7ZGcj9y
2XgA4DbOgJVsx3xAGA6FuEIV0UDwDo4WweWnD4Jo+KVC3nWGW8AjNQb9EAn33WlI
K/mivl/oxH2rx7kCDQRUvtUgARAA7EHGtB6wKGOsKoqNjk+dKxJil5vh+ui5ysLz
3wAXDYOA39nP5bvC1JNu3P8ZFwK6uPNm83ujasK42TSPT6zWyBlmbYF2V2VpsvL5
QX+RJbWtvmqF9dwYa5u7jw4x21J+iT2U5zRDUvgc2UYTiVQGRnOYjtiSp+X4HCub
2umLniDi5r08iKIcgCYyhkhxu04bUpoOvoKhdGT/eDZmIZTCGreMUauiIGwoRqnY
UnVuHk0mTYSDylXt8w4XuFRAoFms060g+7yEDlYSCS7dTdViNFIjdIOLpBecMv7E
fFqOJakq0XcmNmHzL8IJMPw/I/fhiN9m4WaR2yR7lx3HofRXZQKIfjnedyAVV1AN
eRjif7QxPOHLbG7QhVWcHFgNg2GL7cyNMcl30LjEyL237ki4S8MA+GB9mMOlBqQQ
/PqFWaCPSaUoiBGKUFEr3+Q7GTL260GkaTeMQkau7+Eo2WgU2ymhi1jrMBMCvwRw
6CgIVATSciS1yDfAX344ISdXbz9rtdnBRnsaX+p84e12vfvjCjyR3xHdXx3Yb2rn
DT+4JX001DR8ZZkM8Ohi3rCc8vqBm/+ckzyhlj67SsLbhbBJxkieJqvILgkcNqwC
GvZLYK2AK8GCyUrp/eAPXoofE9kwGlfvdPM5giEwQ/+9eBUltQPp1iG35T1zg6EQ
MmjCfR0AEQEAAYkCPAQYAQIAJgIbDBYhBLbI+YKCuUTjsNXCUw/DBC40WtBdBQJa
XfpLBQkPBSarAAoJEA/DBC40WtBdPX8P/1ilEM2BomXdhUO1Vmh5DCHsFDpQtlN5
cU+iBiQXaPdVaDyz1SYCziyD/hr70otJqe1eNf4kWxG/SVB7kav9WXxVDgsoRcF+
IaZKK+Mhnt6il13dg/bDoblPdIDh3YJB+yDiuck+dciPMo2JI6LfrzJue318vRja
vZqotOY/pjuKywNQ74nVNbVcebfj0k9HQeXhxO42dabgm5fabYIkRzlcGUMCFr2l
RWz4nkLYPRQUWTJ47N4k/DLrHkClYebzifwCOFBKm7WpErEpd3B6Lq2RBZYwe6L5
OBJj/MKSYP3+hjXkSLlq8nhaAhtMslShkyLvSuI+ZTxOGOnMDtL42TSDusw+r5eX
XCGMpT+7S52WysgmPOSHp+2opSYiRvFhOmOGcS6M2sSvmbZLpnrHfL0TlBqAExF3
FGF+T4dvIAJw/+n2tc7OXgzb3UOgp4AAfvQYeeIbHI2z2sCgyv+EPldb9avPd1wo
xzaznnkToxkgsTZmKiVxGf5tg4w9m1aVvH3y3y6ox/j2BjgUZAFkDA+CUyvHuaub
sdMiJdqFOFAY4mDqLMkMAPlHBIQaUBwvbxPwoC4zoIsuSGUF9DCIqxQE2eH2vzBX
eUH6lXQaEv7eLTvuBNh9kFHAvOMV2Gb3FQoRpnqs3UFf2XOLHh5I0rmeWfSNSrXr
sfYgf//ax/x3uQINBFylxXABEAC2Qt89UYDndAxNoCIJktuSBWh9BxC1JPPQtmLd
XTsG5vd2h63rBN64ZYTGuW2AQxGV24ngP8rv5F1QzSPY0UgOt25r7pS3+1MZbv+d
sZTtN4LWTXRdIVU+wcqKX1FZCGDSuGs5EpyElnKHxxGh7Wi0KFZMN64t83WPrbzq
aiKrpp9/QHMUtrNqPgUBNKvH8k5g/AGa21+fF1kRsUtmsZbre4IK9bakIjmAfNMA
ZA/YnJy0Ou06HcFWzkfTRLMrQHINUzOzNOhhXuYx3h4qSrvcJnqoGMJ9pZkOfrEJ
VPQexYq3hvL1jwMLdFKDozViUx520/7K8frusf+Df0RlucEVF4QjAV4RAuHBtrzP
LkH/0v6U3u1rX+5VMK8otud43cXcNet/cZ97jRm2rPzviRgYI9EljjD9vGPCIzmo
aJYs+eNJRIJGPqzVV+AELiH9Bc9jCad8XeECBsTCVNx+kEijKclQWr+3y610SXNY
JRKzlPBlMrqJ0U+/vNo59TUgZlwC8KdbiWtxEQ3JYFT7rHVH9cQeAlLXAE0yIfZK
+ss2HpIXgBvJ4nNyNBcFzoqF/iKBcH6yYRILNSGLEKOBnX3/XpAlvnOB1gcTSOQY
frNoXHpA7yzpGh1MeypdCeOqOicZZRF/xX1KR6YDC5YDOFM2paydDNS1ql0Wp0VW
WcIp1wARAQABiQI8BBgBCgAmFiEEtsj5goK5ROOw1cJTD8MELjRa0F0FAlylxXAC
GwwFCQlmAYAACgkQD8MELjRa0F3Quw/+MVB3lHyIORyth4q9KsTUUXBW11UtjKqq
SML0nMuNiqHefNd9P1+zVougyF002TfjkSnOpOoH2Uub3iCX0Cfyigo0rcjBXAvO
j9N9g8eL1xBenTdxYiiHvvIm0BadikfsdoqQebv3ONFda7eoQl689LqMKZ9ZEOxi
w7xQKcIPiNEt2WvBVv4mpEFx1pDbLZ/bUgbR3t7v/t6ijAVdIOjQvW/WPemyRTcB
7iJd68H6Uou/Ofy5EPUH4c/heyCw+eUUFnC9msDIvwtTbkz0Aaa7awbpoegFMz2L
LmSRMLybFn5lQTRR7TizzUvrprOx+UalbUASJS+TONZmVltz0eVVeJ3IHylUM/24
cBh2wXqR63osDCZZkXVxbN9AtyoezEVvg8+XhDLyXeh+o05A/lRjMA33BkwyoKzi
5nZb7iaVYWlKM8Zs6PrB8zq9ErDGcka7gikvUuJ2KLKjJqj19/6Z90oCtJQa9ifi
glN+ER3y4hLHFmKI6ns+GNf0FwpgwD7WD9XBQR9uxBPCrVjXXv4IT9rBidzXT8rK
iXYX9tHBHn2wAk28uJOtdDNcsOdOEqfdmIVfBXNv2df6r8ewEzpNd2MpEOZRW8mc
cn+5dkF+W2mGn8Vky04ewU2+Bo9rApv3zJ76s0Skt2c8axKKtLhHY/H5HPiLNC29
Qk8uiuyeUfE=
=H/uX
-----END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
#!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
set -e # Fail on any error.
function show_cmds { set -x; }
function hide_cmds { { set +x; } 2>/dev/null; }
function status {
echo ""
echo "*****************************************************************"
echo "* $@"
echo "*****************************************************************"
echo ""
}
. /bin/using.sh # Declare the bash `using` function for configuring toolchains.
status "Fetching submodules"
git submodule update --init
status "Setting up environment"
using gcc-9 # Always update gcc so we get a newer standard library.
if [ "$BUILD_SYSTEM" == "cmake" ]; then
using cmake-3.17.2
SRC_DIR=$(pwd)
BUILD_DIR=/tmp/marl-build
INSTALL_DIR=${BUILD_DIR}/install
COMMON_CMAKE_FLAGS=""
COMMON_CMAKE_FLAGS+=" -DCMAKE_BUILD_TYPE=${BUILD_TYPE}"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_EXAMPLES=1"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_TESTS=1"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_BENCHMARKS=1"
COMMON_CMAKE_FLAGS+=" -DMARL_WARNINGS_AS_ERRORS=1"
COMMON_CMAKE_FLAGS+=" -DMARL_DEBUG_ENABLED=1"
COMMON_CMAKE_FLAGS+=" -DMARL_BUILD_SHARED=${BUILD_SHARED:-0}"
COMMON_CMAKE_FLAGS+=" -DBENCHMARK_ENABLE_INSTALL=0"
COMMON_CMAKE_FLAGS+=" -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}"
if [ "$BUILD_TOOLCHAIN" == "ndk" ]; then
using ndk-r21d
COMMON_CMAKE_FLAGS+=" -DANDROID_ABI=$BUILD_TARGET_ARCH"
COMMON_CMAKE_FLAGS+=" -DANDROID_NATIVE_API_LEVEL=18"
COMMON_CMAKE_FLAGS+=" -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake"
else # !ndk
if [ "$BUILD_TOOLCHAIN" == "clang" ]; then
using clang-10.0.0
fi
if [ "$BUILD_TARGET_ARCH" == "x86" ]; then
COMMON_CMAKE_FLAGS+=" -DCMAKE_CXX_FLAGS=-m32"
COMMON_CMAKE_FLAGS+=" -DCMAKE_C_FLAGS=-m32"
COMMON_CMAKE_FLAGS+=" -DCMAKE_ASM_FLAGS=-m32"
fi
fi
if [ "$BUILD_SANITIZER" == "asan" ]; then
COMMON_CMAKE_FLAGS+=" -DMARL_ASAN=1"
elif [ "$BUILD_SANITIZER" == "msan" ]; then
COMMON_CMAKE_FLAGS+=" -DMARL_MSAN=1"
elif [ "$BUILD_SANITIZER" == "tsan" ]; then
COMMON_CMAKE_FLAGS+=" -DMARL_TSAN=1"
fi
# clean
# Ensures BUILD_DIR is empty.
function clean {
if [ -d ${BUILD_DIR} ]; then
rm -fr ${BUILD_DIR}
fi
mkdir ${BUILD_DIR}
}
# build <description> <flags>
# Cleans build directory and performs a build using the provided CMake flags.
function build {
DESCRIPTION=$1
CMAKE_FLAGS=$2
status "Building ${DESCRIPTION}"
clean
cd ${BUILD_DIR}
show_cmds
cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS}
make --jobs=$(nproc)
hide_cmds
}
# test <description>
# Runs the pre-built unit tests (if not an NDK build).
function test {
DESCRIPTION=$1
status "Testing ${DESCRIPTION}"
cd ${BUILD_DIR}
show_cmds
if [ "$BUILD_TOOLCHAIN" != "ndk" ]; then
./marl-unittests
./fractal
./hello_task
./primes > /dev/null
./tasks_in_tasks
fi
hide_cmds
}
# install <description>
# Installs the pre-built library to ${INSTALL_DIR}.
function install {
DESCRIPTION=$1
status "Installing ${DESCRIPTION}"
cd ${BUILD_DIR}
show_cmds
make install
hide_cmds
}
# build <description> <flags>
# Cleans build directory and performs a build using the provided CMake
# flags, then runs tests.
function buildAndTest {
DESCRIPTION=$1
CMAKE_FLAGS=$2
build "$DESCRIPTION" "$CMAKE_FLAGS"
test "$DESCRIPTION"
}
# build <description> <flags>
# Cleans build directory and performs a build using the provided CMake
# flags, then installs the library to ${INSTALL_DIR}.
function buildAndInstall {
DESCRIPTION=$1
CMAKE_FLAGS=$2
build "$DESCRIPTION" "$CMAKE_FLAGS"
install "$DESCRIPTION"
}
if [ -n "$RUN_TESTS" ]; then
buildAndTest "marl with ucontext fibers" "-DMARL_FIBERS_USE_UCONTEXT=1"
buildAndTest "marl with assembly fibers" "-DMARL_FIBERS_USE_UCONTEXT=0"
fi
buildAndInstall "marl for install" "-DMARL_INSTALL=1"
if [ -n "$BUILD_ARTIFACTS" ]; then
status "Copying build artifacts"
show_cmds
tar -czvf "$BUILD_ARTIFACTS/build.tar.gz" -C "$INSTALL_DIR" .
hide_cmds
fi
elif [ "$BUILD_SYSTEM" == "bazel" ]; then
using bazel-3.1.0
show_cmds
bazel test //:tests --test_output=all
bazel run //examples:fractal
bazel run //examples:hello_task
bazel run //examples:primes > /dev/null
bazel run //examples:tasks_in_tasks
hide_cmds
else
status "Unknown build system: $BUILD_SYSTEM"
exit 1
fi
status "Done"
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
env_vars {
key: "BUILD_TOOLCHAIN"
value: "gcc"
}
env_vars {
key: "BUILD_SHARED"
value: "1"
}
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/ubuntu/presubmit.sh" build_file: "marl/kokoro/ubuntu/presubmit.sh"
env_vars { env_vars {
......
#!/bin/bash
set -e # Fail on any error.
. /bin/using.sh # Declare the bash `using` function for configuring toolchains.
set -x # Display commands being run.
cd github/marl
git submodule update --init
using gcc-9 # Always update gcc so we get a newer standard library.
if [ "$BUILD_SYSTEM" == "cmake" ]; then
using cmake-3.17.2
mkdir build
cd build
if [ "$BUILD_TOOLCHAIN" == "clang" ]; then
using clang-10.0.0
fi
EXTRA_CMAKE_FLAGS=""
if [ "$BUILD_TARGET_ARCH" == "x86" ]; then
EXTRA_CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=-m32 -DCMAKE_C_FLAGS=-m32 -DCMAKE_ASM_FLAGS=-m32"
fi
if [ "$BUILD_SANITIZER" == "asan" ]; then
EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DMARL_ASAN=1"
elif [ "$BUILD_SANITIZER" == "msan" ]; then
EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DMARL_MSAN=1"
elif [ "$BUILD_SANITIZER" == "tsan" ]; then
EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DMARL_TSAN=1"
fi
cmake .. ${EXTRA_CMAKE_FLAGS} \
-DMARL_BUILD_EXAMPLES=1 \
-DMARL_BUILD_TESTS=1 \
-DMARL_BUILD_BENCHMARKS=1 \
-DMARL_WARNINGS_AS_ERRORS=1 \
-DMARL_DEBUG_ENABLED=1
make --jobs=$(nproc)
./marl-unittests
./fractal
./hello_task
./primes > /dev/null
./tasks_in_tasks
elif [ "$BUILD_SYSTEM" == "bazel" ]; then
using bazel-3.1.0
bazel test //:tests --test_output=all
bazel run //examples:fractal
bazel run //examples:hello_task
bazel run //examples:primes > /dev/null
bazel run //examples:tasks_in_tasks
else
echo "Unknown build system: $BUILD_SYSTEM"
exit 1
fi
\ No newline at end of file
#!/bin/bash #!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
set -e # Fail on any error. set -e # Fail on any error.
ROOT_DIR=`pwd`
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
docker run --rm -i \ docker run --rm -i \
--volume "${ROOT_DIR}:${ROOT_DIR}" \ --volume "${ROOT_DIR}:${ROOT_DIR}" \
--volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \
--workdir "${ROOT_DIR}" \ --workdir "${ROOT_DIR}" \
--env BUILD_SYSTEM=$BUILD_SYSTEM \ --env BUILD_SYSTEM=${BUILD_SYSTEM} \
--env BUILD_TOOLCHAIN=$BUILD_TOOLCHAIN \ --env BUILD_TOOLCHAIN=${BUILD_TOOLCHAIN} \
--env BUILD_TARGET_ARCH=$BUILD_TARGET_ARCH \ --env BUILD_TYPE=${BUILD_SHARED:-Debug} \
--env BUILD_SANITIZER=$BUILD_SANITIZER \ --env BUILD_TARGET_ARCH=${BUILD_TARGET_ARCH} \
--entrypoint "${SCRIPT_DIR}/presubmit-docker.sh" \ --env BUILD_SHARED=${BUILD_SHARED:-0} \
--env BUILD_SANITIZER=${BUILD_SANITIZER} \
--env RUN_TESTS=1 \
--entrypoint "${SCRIPT_DIR}/docker.sh" \
"gcr.io/shaderc-build/radial-build:latest" "gcr.io/shaderc-build/radial-build:latest"
#!/bin/bash
# Copyright 2020 The Marl Authors.
#
# 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
#
# https://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.
set -e # Fail on any error.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
docker run --rm -i \
--volume "${ROOT_DIR}:${ROOT_DIR}" \
--volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \
--workdir "${ROOT_DIR}" \
--env BUILD_SYSTEM=${BUILD_SYSTEM} \
--env BUILD_TOOLCHAIN=${BUILD_TOOLCHAIN} \
--env BUILD_TYPE=${BUILD_SHARED:-Debug} \
--env BUILD_TARGET_ARCH=${BUILD_TARGET_ARCH} \
--env BUILD_SHARED=${BUILD_SHARED:-0} \
--env BUILD_SANITIZER=${BUILD_SANITIZER} \
--env BUILD_ARTIFACTS="/mnt/artifacts" \
--entrypoint "${SCRIPT_DIR}/docker.sh" \
"gcr.io/shaderc-build/radial-build:latest"
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/windows/presubmit.bat" build_file: "marl/kokoro/windows/presubmit.bat"
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/windows/presubmit.bat" build_file: "marl/kokoro/windows/presubmit.bat"
env_vars { env_vars {
...@@ -10,7 +9,7 @@ env_vars { ...@@ -10,7 +9,7 @@ env_vars {
env_vars { env_vars {
key: "BUILD_GENERATOR" key: "BUILD_GENERATOR"
value: "Visual Studio 15 2017 Win64" value: "Visual Studio 15 2017"
} }
env_vars { env_vars {
......
# Format: //devtools/kokoro/config/proto/build.proto # Format: //devtools/kokoro/config/proto/build.proto
# Location of the continuous bash script in Git.
build_file: "marl/kokoro/windows/presubmit.bat" build_file: "marl/kokoro/windows/presubmit.bat"
env_vars { env_vars {
...@@ -15,5 +14,5 @@ env_vars { ...@@ -15,5 +14,5 @@ env_vars {
env_vars { env_vars {
key: "BUILD_TARGET_ARCH" key: "BUILD_TARGET_ARCH"
value: "x86" value: "Win32"
} }
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/windows/presubmit.bat"
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
env_vars {
key: "BUILD_GENERATOR"
value: "Visual Studio 16 2019"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "x64"
}
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "marl/kokoro/windows/presubmit.bat"
env_vars {
key: "BUILD_SYSTEM"
value: "cmake"
}
env_vars {
key: "BUILD_GENERATOR"
value: "Visual Studio 16 2019"
}
env_vars {
key: "BUILD_TARGET_ARCH"
value: "Win32"
}
REM Copyright 2020 The Marl Authors.
REM
REM Licensed under the Apache License, Version 2.0 (the "License");
REM you may not use this file except in compliance with the License.
REM You may obtain a copy of the License at
REM
REM https://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
@echo on @echo on
SETLOCAL ENABLEDELAYEDEXPANSION SETLOCAL ENABLEDELAYEDEXPANSION
SET BUILD_ROOT=%cd% SET BUILD_ROOT=%cd%
SET PATH=C:\python36;C:\Program Files\cmake\bin;%PATH% SET PATH=C:\python36;C:\Program Files\cmake\bin;%PATH%
SET SRC=%cd%\github\marl SET ROOT_DIR=%cd%\github\marl
SET BUILD_DIR=%ROOT_DIR%\build
cd %SRC% cd %ROOT_DIR%
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
git submodule update --init git submodule update --init
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
SET MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild"
SET CONFIG=Release SET CONFIG=Release
mkdir %SRC%\build mkdir %BUILD_DIR%
cd %SRC%\build cd %BUILD_DIR%
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
IF /I "%BUILD_SYSTEM%"=="cmake" ( IF /I "%BUILD_SYSTEM%"=="cmake" (
cmake .. -G "%BUILD_GENERATOR%" "-DMARL_BUILD_TESTS=1" "-DMARL_BUILD_EXAMPLES=1" "-DMARL_BUILD_BENCHMARKS=1" "-DMARL_WARNINGS_AS_ERRORS=1" "-DMARL_DEBUG_ENABLED=1" cmake "%ROOT_DIR%" ^
-G "%BUILD_GENERATOR%" ^
-A "%BUILD_TARGET_ARCH%" ^
"-DMARL_BUILD_TESTS=1" ^
"-DMARL_BUILD_EXAMPLES=1" ^
"-DMARL_BUILD_BENCHMARKS=1" ^
"-DMARL_WARNINGS_AS_ERRORS=1" ^
"-DMARL_DEBUG_ENABLED=1"
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
%MSBUILD% /p:Configuration=%CONFIG% Marl.sln cmake --build . --config %CONFIG%
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
Release\marl-unittests.exe %CONFIG%\marl-unittests.exe
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
Release\fractal.exe %CONFIG%\fractal.exe
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
Release\primes.exe > nul %CONFIG%\primes.exe > nul
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
) ELSE IF /I "%BUILD_SYSTEM%"=="bazel" ( ) ELSE IF /I "%BUILD_SYSTEM%"=="bazel" (
REM Fix up the MSYS environment. REM Fix up the MSYS environment.
......
REM Copyright 2020 The Marl Authors.
REM
REM Licensed under the Apache License, Version 2.0 (the "License");
REM you may not use this file except in compliance with the License.
REM You may obtain a copy of the License at
REM
REM https://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
@echo on
SETLOCAL ENABLEDELAYEDEXPANSION
SET BUILD_ROOT=%cd%
SET PATH=C:\python36;C:\Program Files\cmake\bin;%PATH%
SET ROOT_DIR=%cd%\github\marl
SET BUILD_DIR=%ROOT_DIR%\build
cd %ROOT_DIR%
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
git submodule update --init
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
SET CONFIG=Release
mkdir %BUILD_DIR%
cd %BUILD_DIR%
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
cmake "%ROOT_DIR%" ^
-G "%BUILD_GENERATOR%" ^
-A "%BUILD_TARGET_ARCH%" ^
"-DMARL_BUILD_TESTS=1" ^
"-DMARL_BUILD_EXAMPLES=1" ^
"-DMARL_BUILD_BENCHMARKS=1" ^
"-DMARL_WARNINGS_AS_ERRORS=1" ^
"-DMARL_DEBUG_ENABLED=1" ^
"-DMARL_INSTALL=1" ^
"-DBENCHMARK_ENABLE_INSTALL=0" ^
"-DCMAKE_INSTALL_PREFIX=%INSTALL_DIR%"
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
cmake --build . --config %CONFIG% --target install
{
"licenses": [
"Apache-2.0-Header"
],
"paths": [
{
"exclude": [
".clang-format",
".gitignore",
".gitmodules",
".vscode/*.json",
"**.md",
"AUTHORS",
"LICENSE",
"VERSION",
"build/**",
"docs/imgs/*.svg",
"kokoro/**.cfg",
"third_party/benchmark/**",
"third_party/googletest/**"
]
}
]
}
// Copyright 2020 The Marl Authors.
//
// 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
//
// https://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 "marl/dag.h"
#include "marl_test.h"
using namespace testing;
namespace {
struct Data {
std::mutex mutex;
std::vector<std::string> order;
void push(std::string&& s) {
std::unique_lock<std::mutex> lock(mutex);
order.emplace_back(std::move(s));
}
};
template <typename T>
std::vector<T> slice(const std::vector<T>& in, size_t from, size_t to) {
return {in.begin() + from, in.begin() + to};
}
} // namespace
// [A] --> [B] --> [C] |
TEST_P(WithBoundScheduler, DAGChainNoArg) {
marl::DAG<>::Builder builder;
Data data;
builder.root()
.then([&] { data.push("A"); })
.then([&] { data.push("B"); })
.then([&] { data.push("C"); });
auto dag = builder.build();
dag->run();
ASSERT_THAT(data.order, ElementsAre("A", "B", "C"));
}
// [A] --> [B] --> [C] |
TEST_P(WithBoundScheduler, DAGChain) {
marl::DAG<Data&>::Builder builder;
builder.root()
.then([](Data& data) { data.push("A"); })
.then([](Data& data) { data.push("B"); })
.then([](Data& data) { data.push("C"); });
auto dag = builder.build();
Data data;
dag->run(data);
ASSERT_THAT(data.order, ElementsAre("A", "B", "C"));
}
// [A] --> [B] --> [C] |
TEST_P(WithBoundScheduler, DAGRunRepeat) {
marl::DAG<Data&>::Builder builder;
builder.root()
.then([](Data& data) { data.push("A"); })
.then([](Data& data) { data.push("B"); })
.then([](Data& data) { data.push("C"); });
auto dag = builder.build();
Data dataA, dataB;
dag->run(dataA);
dag->run(dataB);
dag->run(dataA);
ASSERT_THAT(dataA.order, ElementsAre("A", "B", "C", "A", "B", "C"));
ASSERT_THAT(dataB.order, ElementsAre("A", "B", "C"));
}
// /--> [A] |
// [root] --|--> [B] |
// \--> [C] |
TEST_P(WithBoundScheduler, DAGFanOutFromRoot) {
marl::DAG<Data&>::Builder builder;
auto root = builder.root();
root.then([](Data& data) { data.push("A"); });
root.then([](Data& data) { data.push("B"); });
root.then([](Data& data) { data.push("C"); });
auto dag = builder.build();
Data data;
dag->run(data);
ASSERT_THAT(data.order, UnorderedElementsAre("A", "B", "C"));
}
// /--> [A] |
// [root] -->[N]--|--> [B] |
// \--> [C] |
TEST_P(WithBoundScheduler, DAGFanOutFromNonRoot) {
marl::DAG<Data&>::Builder builder;
auto root = builder.root();
auto node = root.then([](Data& data) { data.push("N"); });
node.then([](Data& data) { data.push("A"); });
node.then([](Data& data) { data.push("B"); });
node.then([](Data& data) { data.push("C"); });
auto dag = builder.build();
Data data;
dag->run(data);
ASSERT_THAT(data.order, UnorderedElementsAre("N", "A", "B", "C"));
ASSERT_EQ(data.order[0], "N");
ASSERT_THAT(slice(data.order, 1, 4), UnorderedElementsAre("A", "B", "C"));
}
// /--> [A0] --\ /--> [C0] --\ /--> [E0] --\ |
// [root] --|--> [A1] --|-->[B]--|--> [C1] --|-->[D]--|--> [E1] --|-->[F] |
// \--> [C2] --/ |--> [E2] --| |
// \--> [E3] --/ |
TEST_P(WithBoundScheduler, DAGFanOutFanIn) {
marl::DAG<Data&>::Builder builder;
auto root = builder.root();
auto a0 = root.then([](Data& data) { data.push("A0"); });
auto a1 = root.then([](Data& data) { data.push("A1"); });
auto b = builder.node([](Data& data) { data.push("B"); }, {a0, a1});
auto c0 = b.then([](Data& data) { data.push("C0"); });
auto c1 = b.then([](Data& data) { data.push("C1"); });
auto c2 = b.then([](Data& data) { data.push("C2"); });
auto d = builder.node([](Data& data) { data.push("D"); }, {c0, c1, c2});
auto e0 = d.then([](Data& data) { data.push("E0"); });
auto e1 = d.then([](Data& data) { data.push("E1"); });
auto e2 = d.then([](Data& data) { data.push("E2"); });
auto e3 = d.then([](Data& data) { data.push("E3"); });
builder.node([](Data& data) { data.push("F"); }, {e0, e1, e2, e3});
auto dag = builder.build();
Data data;
dag->run(data);
ASSERT_THAT(data.order,
UnorderedElementsAre("A0", "A1", "B", "C0", "C1", "C2", "D", "E0",
"E1", "E2", "E3", "F"));
ASSERT_THAT(slice(data.order, 0, 2), UnorderedElementsAre("A0", "A1"));
ASSERT_THAT(data.order[2], "B");
ASSERT_THAT(slice(data.order, 3, 6), UnorderedElementsAre("C0", "C1", "C2"));
ASSERT_THAT(data.order[6], "D");
ASSERT_THAT(slice(data.order, 7, 11),
UnorderedElementsAre("E0", "E1", "E2", "E3"));
ASSERT_THAT(data.order[11], "F");
}
...@@ -57,6 +57,7 @@ class WithBoundScheduler : public testing::TestWithParam<SchedulerParams> { ...@@ -57,6 +57,7 @@ class WithBoundScheduler : public testing::TestWithParam<SchedulerParams> {
marl::Scheduler::Config cfg; marl::Scheduler::Config cfg;
cfg.setAllocator(allocator); cfg.setAllocator(allocator);
cfg.setWorkerThreadCount(params.numWorkerThreads); cfg.setWorkerThreadCount(params.numWorkerThreads);
cfg.setFiberStackSize(0x10000);
auto scheduler = new marl::Scheduler(cfg); auto scheduler = new marl::Scheduler(cfg);
scheduler->bind(); scheduler->bind();
......
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
#include "osfiber_asm_aarch64.h" #include "osfiber_asm_aarch64.h"
#include "marl/export.h"
MARL_EXPORT
void marl_fiber_trampoline(void (*target)(void*), void* arg) { void marl_fiber_trampoline(void (*target)(void*), void* arg) {
target(arg); target(arg);
} }
MARL_EXPORT
void marl_fiber_set_target(struct marl_fiber_context* ctx, void marl_fiber_set_target(struct marl_fiber_context* ctx,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
......
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
#include "osfiber_asm_arm.h" #include "osfiber_asm_arm.h"
#include "marl/export.h"
MARL_EXPORT
void marl_fiber_trampoline(void (*target)(void*), void* arg) { void marl_fiber_trampoline(void (*target)(void*), void* arg) {
target(arg); target(arg);
} }
MARL_EXPORT
void marl_fiber_set_target(struct marl_fiber_context* ctx, void marl_fiber_set_target(struct marl_fiber_context* ctx,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#error "Unsupported target" #error "Unsupported target"
#endif #endif
#include "marl/export.h"
#include "marl/memory.h" #include "marl/memory.h"
#include <functional> #include <functional>
...@@ -45,11 +46,13 @@ ...@@ -45,11 +46,13 @@
extern "C" { extern "C" {
MARL_EXPORT
extern void marl_fiber_set_target(marl_fiber_context*, extern void marl_fiber_set_target(marl_fiber_context*,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
void (*target)(void*), void (*target)(void*),
void* arg); void* arg);
MARL_EXPORT
extern void marl_fiber_swap(marl_fiber_context* from, extern void marl_fiber_swap(marl_fiber_context* from,
const marl_fiber_context* to); const marl_fiber_context* to);
...@@ -64,22 +67,23 @@ class OSFiber { ...@@ -64,22 +67,23 @@ class OSFiber {
// createFiberFromCurrentThread() returns a fiber created from the current // createFiberFromCurrentThread() returns a fiber created from the current
// thread. // thread.
static inline Allocator::unique_ptr<OSFiber> createFiberFromCurrentThread( MARL_NO_EXPORT static inline Allocator::unique_ptr<OSFiber>
Allocator* allocator); createFiberFromCurrentThread(Allocator* allocator);
// createFiber() returns a new fiber with the given stack size that will // createFiber() returns a new fiber with the given stack size that will
// call func when switched to. func() must end by switching back to another // call func when switched to. func() must end by switching back to another
// fiber, and must not return. // fiber, and must not return.
static inline Allocator::unique_ptr<OSFiber> createFiber( MARL_NO_EXPORT static inline Allocator::unique_ptr<OSFiber> createFiber(
Allocator* allocator, Allocator* allocator,
size_t stackSize, size_t stackSize,
const std::function<void()>& func); const std::function<void()>& func);
// switchTo() immediately switches execution to the given fiber. // switchTo() immediately switches execution to the given fiber.
// switchTo() must be called on the currently executing fiber. // switchTo() must be called on the currently executing fiber.
inline void switchTo(OSFiber*); MARL_NO_EXPORT inline void switchTo(OSFiber*);
private: private:
MARL_NO_EXPORT
static inline void run(OSFiber* self); static inline void run(OSFiber* self);
Allocator* allocator; Allocator* allocator;
......
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
#include "osfiber_asm_mips64.h" #include "osfiber_asm_mips64.h"
#include "marl/export.h"
MARL_EXPORT
void marl_fiber_trampoline(void (*target)(void*), void* arg) { void marl_fiber_trampoline(void (*target)(void*), void* arg) {
target(arg); target(arg);
} }
MARL_EXPORT
void marl_fiber_set_target(struct marl_fiber_context* ctx, void marl_fiber_set_target(struct marl_fiber_context* ctx,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
...@@ -32,4 +36,4 @@ void marl_fiber_set_target(struct marl_fiber_context* ctx, ...@@ -32,4 +36,4 @@ void marl_fiber_set_target(struct marl_fiber_context* ctx,
ctx->sp = ((uintptr_t)stack_top) & ~(uintptr_t)15; ctx->sp = ((uintptr_t)stack_top) & ~(uintptr_t)15;
} }
#endif // defined(__mips__) && _MIPS_SIM == _ABI64 #endif // defined(__mips__) && _MIPS_SIM == _ABI64
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
#include "osfiber_asm_ppc64.h" #include "osfiber_asm_ppc64.h"
#include "marl/export.h"
MARL_EXPORT
void marl_fiber_trampoline(void (*target)(void*), void* arg) { void marl_fiber_trampoline(void (*target)(void*), void* arg) {
target(arg); target(arg);
} }
MARL_EXPORT
void marl_fiber_set_target(struct marl_fiber_context* ctx, void marl_fiber_set_target(struct marl_fiber_context* ctx,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
......
...@@ -95,9 +95,7 @@ Allocator::unique_ptr<OSFiber> OSFiber::createFiber( ...@@ -95,9 +95,7 @@ Allocator::unique_ptr<OSFiber> OSFiber::createFiber(
Args u; Args u;
u.a = a; u.a = a;
u.b = b; u.b = b;
std::function<void()> func; u.self->target();
std::swap(func, u.self->target);
func();
} }
}; };
...@@ -121,7 +119,7 @@ Allocator::unique_ptr<OSFiber> OSFiber::createFiber( ...@@ -121,7 +119,7 @@ Allocator::unique_ptr<OSFiber> OSFiber::createFiber(
out->context.uc_stack.ss_size = stackSize; out->context.uc_stack.ss_size = stackSize;
out->context.uc_link = nullptr; out->context.uc_link = nullptr;
Args args; Args args{};
args.self = out.get(); args.self = out.get();
makecontext(&out->context, reinterpret_cast<void (*)()>(&Target::Main), 2, makecontext(&out->context, reinterpret_cast<void (*)()>(&Target::Main), 2,
args.a, args.b); args.a, args.b);
......
...@@ -64,7 +64,7 @@ OSFiber::~OSFiber() { ...@@ -64,7 +64,7 @@ OSFiber::~OSFiber() {
Allocator::unique_ptr<OSFiber> OSFiber::createFiberFromCurrentThread( Allocator::unique_ptr<OSFiber> OSFiber::createFiberFromCurrentThread(
Allocator* allocator) { Allocator* allocator) {
auto out = allocator->make_unique<OSFiber>(); auto out = allocator->make_unique<OSFiber>();
out->fiber = ConvertThreadToFiberEx(nullptr,FIBER_FLAG_FLOAT_SWITCH); out->fiber = ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
out->isFiberFromThread = true; out->isFiberFromThread = true;
MARL_ASSERT(out->fiber != nullptr, MARL_ASSERT(out->fiber != nullptr,
"ConvertThreadToFiberEx() failed with error 0x%x", "ConvertThreadToFiberEx() failed with error 0x%x",
...@@ -77,8 +77,10 @@ Allocator::unique_ptr<OSFiber> OSFiber::createFiber( ...@@ -77,8 +77,10 @@ Allocator::unique_ptr<OSFiber> OSFiber::createFiber(
size_t stackSize, size_t stackSize,
const std::function<void()>& func) { const std::function<void()>& func) {
auto out = allocator->make_unique<OSFiber>(); auto out = allocator->make_unique<OSFiber>();
// stackSize is rounded up to the system's allocation granularity (typically 64 KB). // stackSize is rounded up to the system's allocation granularity (typically
out->fiber = CreateFiberEx(stackSize - 1,stackSize,FIBER_FLAG_FLOAT_SWITCH,&OSFiber::run, out.get()); // 64 KB).
out->fiber = CreateFiberEx(stackSize - 1, stackSize, FIBER_FLAG_FLOAT_SWITCH,
&OSFiber::run, out.get());
out->target = func; out->target = func;
MARL_ASSERT(out->fiber != nullptr, "CreateFiberEx() failed with error 0x%x", MARL_ASSERT(out->fiber != nullptr, "CreateFiberEx() failed with error 0x%x",
int(GetLastError())); int(GetLastError()));
......
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
#include "osfiber_asm_x64.h" #include "osfiber_asm_x64.h"
#include "marl/export.h"
MARL_EXPORT
void marl_fiber_trampoline(void (*target)(void*), void* arg) { void marl_fiber_trampoline(void (*target)(void*), void* arg) {
target(arg); target(arg);
} }
MARL_EXPORT
void marl_fiber_set_target(struct marl_fiber_context* ctx, void marl_fiber_set_target(struct marl_fiber_context* ctx,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
......
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
#include "osfiber_asm_x86.h" #include "osfiber_asm_x86.h"
#include "marl/export.h"
MARL_EXPORT
void marl_fiber_trampoline(void (*target)(void*), void* arg) { void marl_fiber_trampoline(void (*target)(void*), void* arg) {
target(arg); target(arg);
} }
MARL_EXPORT
void marl_fiber_set_target(struct marl_fiber_context* ctx, void marl_fiber_set_target(struct marl_fiber_context* ctx,
void* stack, void* stack,
uint32_t stack_size, uint32_t stack_size,
......
...@@ -67,6 +67,16 @@ inline void nop() { ...@@ -67,6 +67,16 @@ inline void nop() {
#endif #endif
} }
inline marl::Scheduler::Config setConfigDefaults(
const marl::Scheduler::Config& cfgIn) {
marl::Scheduler::Config cfg{cfgIn};
if (cfg.workerThread.count > 0 && !cfg.workerThread.affinityPolicy) {
cfg.workerThread.affinityPolicy = marl::Thread::Affinity::Policy::anyOf(
marl::Thread::Affinity::all(cfg.allocator), cfg.allocator);
}
return cfg;
}
} // anonymous namespace } // anonymous namespace
namespace marl { namespace marl {
...@@ -113,11 +123,9 @@ void Scheduler::unbind() { ...@@ -113,11 +123,9 @@ void Scheduler::unbind() {
} }
Scheduler::Scheduler(const Config& config) Scheduler::Scheduler(const Config& config)
: cfg(config), workerThreads{}, singleThreadedWorkers(config.allocator) { : cfg(setConfigDefaults(config)),
if (cfg.workerThread.count > 0 && !cfg.workerThread.affinityPolicy) { workerThreads{},
cfg.workerThread.affinityPolicy = Thread::Affinity::Policy::anyOf( singleThreadedWorkers(config.allocator) {
Thread::Affinity::all(cfg.allocator), cfg.allocator);
}
for (size_t i = 0; i < spinningWorkers.size(); i++) { for (size_t i = 0; i < spinningWorkers.size(); i++) {
spinningWorkers[i] = -1; spinningWorkers[i] = -1;
} }
...@@ -150,61 +158,6 @@ Scheduler::~Scheduler() { ...@@ -150,61 +158,6 @@ Scheduler::~Scheduler() {
} }
} }
#if MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS
Scheduler::Scheduler(Allocator* allocator /* = Allocator::Default */)
: workerThreads{}, singleThreadedWorkers(allocator) {
cfg.allocator = allocator;
for (size_t i = 0; i < spinningWorkers.size(); i++) {
spinningWorkers[i] = -1;
}
}
void Scheduler::setThreadInitializer(const std::function<void()>& init) {
marl::lock lock(threadInitFuncMutex);
cfg.workerThread.initializer = [=](int) { init(); };
}
std::function<void()> Scheduler::getThreadInitializer() {
marl::lock lock(threadInitFuncMutex);
if (!cfg.workerThread.initializer) {
return {};
}
auto init = cfg.workerThread.initializer;
return [=]() { init(0); };
}
void Scheduler::setWorkerThreadCount(int newCount) {
MARL_ASSERT(newCount >= 0, "count must be positive");
if (newCount > int(MaxWorkerThreads)) {
MARL_WARN(
"marl::Scheduler::setWorkerThreadCount() called with a count of %d, "
"which exceeds the maximum of %d. Limiting the number of threads to "
"%d.",
newCount, int(MaxWorkerThreads), int(MaxWorkerThreads));
newCount = MaxWorkerThreads;
}
auto oldCount = cfg.workerThread.count;
for (int idx = oldCount - 1; idx >= newCount; idx--) {
workerThreads[idx]->stop();
}
for (int idx = oldCount - 1; idx >= newCount; idx--) {
cfg.allocator->destroy(workerThreads[idx]);
}
for (int idx = oldCount; idx < newCount; idx++) {
workerThreads[idx] =
cfg.allocator->create<Worker>(this, Worker::Mode::MultiThreaded, idx);
}
cfg.workerThread.count = newCount;
for (int idx = oldCount; idx < newCount; idx++) {
workerThreads[idx]->start();
}
}
int Scheduler::getWorkerThreadCount() {
return cfg.workerThread.count;
}
#endif // MARL_ENABLE_DEPRECATED_SCHEDULER_GETTERS_SETTERS
void Scheduler::enqueue(Task&& task) { void Scheduler::enqueue(Task&& task) {
if (task.is(Task::Flags::SameThread)) { if (task.is(Task::Flags::SameThread)) {
Worker::getCurrent()->enqueue(std::move(task)); Worker::getCurrent()->enqueue(std::move(task));
...@@ -749,7 +702,8 @@ void Scheduler::Worker::runUntilIdle() { ...@@ -749,7 +702,8 @@ void Scheduler::Worker::runUntilIdle() {
Scheduler::Fiber* Scheduler::Worker::createWorkerFiber() { Scheduler::Fiber* Scheduler::Worker::createWorkerFiber() {
auto fiberId = static_cast<uint32_t>(workerFibers.size() + 1); auto fiberId = static_cast<uint32_t>(workerFibers.size() + 1);
DBG_LOG("%d: CREATE(%d)", (int)id, (int)fiberId); DBG_LOG("%d: CREATE(%d)", (int)id, (int)fiberId);
auto fiber = Fiber::create(scheduler->cfg.allocator, fiberId, FiberStackSize, auto fiber = Fiber::create(scheduler->cfg.allocator, fiberId,
scheduler->cfg.fiberStackSize,
[&]() REQUIRES(work.mutex) { run(); }); [&]() REQUIRES(work.mutex) { run(); });
auto ptr = fiber.get(); auto ptr = fiber.get();
workerFibers.emplace_back(std::move(fiber)); workerFibers.emplace_back(std::move(fiber));
......
...@@ -94,6 +94,20 @@ TEST_P(WithBoundScheduler, DestructWithPendingFibers) { ...@@ -94,6 +94,20 @@ TEST_P(WithBoundScheduler, DestructWithPendingFibers) {
(new marl::Scheduler(marl::Scheduler::Config()))->bind(); (new marl::Scheduler(marl::Scheduler::Config()))->bind();
} }
TEST_P(WithBoundScheduler, ScheduleWithArgs) {
std::string got;
marl::WaitGroup wg(1);
marl::schedule(
[wg, &got](std::string s, int i, bool b) {
got = "s: '" + s + "', i: " + std::to_string(i) +
", b: " + (b ? "true" : "false");
wg.done();
},
"a string", 42, true);
wg.wait();
ASSERT_EQ(got, "s: 'a string', i: 42, b: true");
}
TEST_P(WithBoundScheduler, FibersResumeOnSameThread) { TEST_P(WithBoundScheduler, FibersResumeOnSameThread) {
marl::WaitGroup fence(1); marl::WaitGroup fence(1);
marl::WaitGroup wg(1000); marl::WaitGroup wg(1000);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment