diff --git b/CMakeCommon/CheckEnableTests.cmake a/CMakeCommon/CheckEnableTests.cmake new file mode 100644 index 0000000..1a58ee2 --- /dev/null +++ a/CMakeCommon/CheckEnableTests.cmake @@ -0,0 +1,50 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +## CheckPartestInstall: Checks if partest install path is defined +# Reads: PARTEST_INSTALL_PREFIX +# Sets: partest_includepath_var +# partest_libpath_var +# +function(CheckPartestInstall BUILD_TESTS _partest_includepath _partest_libpath) + if (BUILD_TESTS STREQUAL ON) + set (PARTEST_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/partest_install PARENT_SCOPE) + set (PARTEST_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/partest_install) + + set(${_partest_includepath} ${PARTEST_INSTALL_PREFIX}/include PARENT_SCOPE) + set(${_partest_libpath} ${PARTEST_INSTALL_PREFIX}/lib PARENT_SCOPE) + + set (tar_extraction_directory ${CMAKE_CURRENT_BINARY_DIR}) + message("-- Extracting partest to directoy ${tar_extraction_directory}/partest") + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xf ${PROJECT_SOURCE_DIR}/partest.tar + WORKING_DIRECTORY ${tar_extraction_directory} + ) + + if (TARGET PARTEST) + else() + add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/partest ${CMAKE_CURRENT_BINARY_DIR}/partest_build) + endif() + endif() +endfunction() diff --git b/CMakeCommon/CopyInstallFiles.cmake a/CMakeCommon/CopyInstallFiles.cmake new file mode 100644 index 0000000..1ecf6b7 --- /dev/null +++ a/CMakeCommon/CopyInstallFiles.cmake @@ -0,0 +1,37 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Copies the binary target bin after build to the destination. +# +function(CopyBin + BIN bin + DEST destination + ) + + add_custom_command( + TARGET ${bin} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${destination} + ) +endfunction() diff --git b/CMakeCommon/CreateDoxygenDocumentationTarget.cmake a/CMakeCommon/CreateDoxygenDocumentationTarget.cmake new file mode 100644 index 0000000..547a9b8 --- /dev/null +++ a/CMakeCommon/CreateDoxygenDocumentationTarget.cmake @@ -0,0 +1,69 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +## DOXYGEN DOCUMENTATION +# +# Is only generated when option(.* ON) is set... +function (CreateDoxygenDocumentationTarget) + option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" ON) + if(BUILD_DOCUMENTATION) + FIND_PACKAGE(Doxygen) + #if (NOT DOXYGEN_FOUND) + # message(FATAL_ERROR + # "Doxygen is needed to build the documentation. Please install it correctly") + #endif() + #-- Configure the Template Doxyfile for our specific project + configure_file(doc/reference/Doxyfile.in + ${PROJECT_BINARY_DIR}/Doxyfile @ONLY IMMEDIATE) + #-- Add a custom target to run Doxygen when ever the project is built + if (TARGET doxygen) + # Do nothing, since the repeated adding causes an error + else() + + FILE(WRITE ${CMAKE_BINARY_DIR}/doxygen_makefile.cmake " +MESSAGE(STATUS \"Running Doxygen\") +EXECUTE_PROCESS( +COMMAND \${EXE} \${IN} +ERROR_VARIABLE DOXYGEN_OUT_ERR +RESULT_VARIABLE DOXYGEN_OUT_RESULT) +STRING(LENGTH \"\${DOXYGEN_OUT_ERR}\" LENGTH_ERR) +IF ( NOT \${LENGTH_ERR} STREQUAL \"0\" ) + MESSAGE (WARNING \"Doxygen produced following warnings and or/errors: \${DOXYGEN_OUT_ERR}\") + IF ( \${WARNINGS_ARE_ERRORS} STREQUAL ON OR NOT \${DOXYGEN_OUT_RESULT} STREQUAL \"0\" ) + MESSAGE (FATAL_ERROR \"Exiting doxygen generation due to errors (or warnings, if WARNINGS_ARE_ERRORS is enabled)\") + ENDIF () +ENDIF () +") + + add_custom_target(doxygen) + add_custom_command( + TARGET doxygen + COMMAND ${CMAKE_COMMAND} + -DEXE=${DOXYGEN_EXECUTABLE} + -DIN=${PROJECT_BINARY_DIR}/Doxyfile + -DWARNINGS_ARE_ERRORS=${WARNINGS_ARE_ERRORS} + -P ${CMAKE_BINARY_DIR}/doxygen_makefile.cmake) + endif() + endif() +endfunction() diff --git b/CMakeCommon/FindOpenCL.cmake a/CMakeCommon/FindOpenCL.cmake new file mode 100644 index 0000000..d6533f9 --- /dev/null +++ a/CMakeCommon/FindOpenCL.cmake @@ -0,0 +1,147 @@ +# Taken from CMake Version 3.2.1, modified to work on older versions +#.rst: +# FindOpenCL +# ---------- +# +# Try to find OpenCL +# +# Once done this will define:: +# +# OpenCL_FOUND - True if OpenCL was found +# OpenCL_INCLUDE_DIRS - include directories for OpenCL +# OpenCL_LIBRARIES - link against this library to use OpenCL +# OpenCL_VERSION_STRING - Highest supported OpenCL version (eg. 1.2) +# OpenCL_VERSION_MAJOR - The major version of the OpenCL implementation +# OpenCL_VERSION_MINOR - The minor version of the OpenCL implementation +# +# The module will also define two cache variables:: +# +# OpenCL_INCLUDE_DIR - the OpenCL include directory +# OpenCL_LIBRARY - the path to the OpenCL library +# + +#============================================================================= +# Copyright 2014 Matthaeus G. Chajdas +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +function(_FIND_OPENCL_VERSION) + include(CheckSymbolExists) + include(CMakePushCheckState) + set(CMAKE_REQUIRED_QUIET ${OpenCL_FIND_QUIETLY}) + + CMAKE_PUSH_CHECK_STATE() + foreach(VERSION "2_0" "1_2" "1_1" "1_0") + set(CMAKE_REQUIRED_INCLUDES "${OpenCL_INCLUDE_DIR}") + + if(APPLE) + CHECK_SYMBOL_EXISTS( + CL_VERSION_${VERSION} + "${OpenCL_INCLUDE_DIR}/OpenCL/cl.h" + OPENCL_VERSION_${VERSION}) + else() + CHECK_SYMBOL_EXISTS( + CL_VERSION_${VERSION} + "${OpenCL_INCLUDE_DIR}/CL/cl.h" + OPENCL_VERSION_${VERSION}) + endif() + + if(OPENCL_VERSION_${VERSION}) + string(REPLACE "_" "." VERSION "${VERSION}") + set(OpenCL_VERSION_STRING ${VERSION} PARENT_SCOPE) + string(REGEX MATCHALL "[0-9]+" version_components "${VERSION}") + list(GET version_components 0 major_version) + list(GET version_components 1 minor_version) + set(OpenCL_VERSION_MAJOR ${major_version} PARENT_SCOPE) + set(OpenCL_VERSION_MINOR ${minor_version} PARENT_SCOPE) + break() + endif() + endforeach() + CMAKE_POP_CHECK_STATE() +endfunction() + +find_path(OpenCL_INCLUDE_DIR + NAMES + CL/cl.h OpenCL/cl.h + PATHS + ENV "PROGRAMFILES(X86)" + ENV AMDAPPSDKROOT + ENV INTELOCLSDKROOT + ENV NVSDKCOMPUTE_ROOT + ENV CUDA_PATH + ENV ATISTREAMSDKROOT + PATH_SUFFIXES + include + OpenCL/common/inc + "AMD APP/include") + +_FIND_OPENCL_VERSION() + +if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + find_library(OpenCL_LIBRARY + NAMES OpenCL + PATHS + ENV "PROGRAMFILES(X86)" + ENV AMDAPPSDKROOT + ENV INTELOCLSDKROOT + ENV CUDA_PATH + ENV NVSDKCOMPUTE_ROOT + ENV ATISTREAMSDKROOT + PATH_SUFFIXES + "AMD APP/lib/x86" + lib/x86 + lib/Win32 + OpenCL/common/lib/Win32) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + find_library(OpenCL_LIBRARY + NAMES OpenCL + PATHS + ENV "PROGRAMFILES(X86)" + ENV AMDAPPSDKROOT + ENV INTELOCLSDKROOT + ENV CUDA_PATH + ENV NVSDKCOMPUTE_ROOT + ENV ATISTREAMSDKROOT + PATH_SUFFIXES + "AMD APP/lib/x86_64" + lib/x86_64 + lib/x64 + OpenCL/common/lib/x64) + endif() +else() + find_library(OpenCL_LIBRARY + NAMES OpenCL) +endif() + +set(OpenCL_LIBRARIES ${OpenCL_LIBRARY}) +set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR}) + + +#find_package_handle_standard_args not available in older CMake versions... +#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +#find_package_handle_standard_args( +# OpenCL +# FOUND_VAR OpenCL_FOUND +# REQUIRED_VARS OpenCL_LIBRARY OpenCL_INCLUDE_DIR +# VERSION_VAR OpenCL_VERSION_STRING) + +#mark_as_advanced( +# OpenCL_INCLUDE_DIR +# OpenCL_LIBRARY) + +# This replaces FindPackageHandleStandardArgs.cmake, which is not available in older +# CMake versions +if( OpenCL_LIBRARIES AND OpenCL_INCLUDE_DIRS ) + set(OpenCL_FOUND 1) +else() + set(OpenCL_FOUND 0) +endif() diff --git b/CMakeCommon/GroupSourcesMSVC.cmake a/CMakeCommon/GroupSourcesMSVC.cmake new file mode 100644 index 0000000..b16748b --- /dev/null +++ a/CMakeCommon/GroupSourcesMSVC.cmake @@ -0,0 +1,41 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +## Visual Studio Project Adaptions +# +# Make a browsable file hierarchy when opening +# the generated visual c++ solution project in visual studio +function(GroupSourcesMSVC directory) + file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${directory} ${PROJECT_SOURCE_DIR}/${directory}/*) + foreach(child ${children}) + if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${directory}/${child}) + GroupSourcesMSVC(${directory}/${child}) + else() + string(REPLACE "/" "\\" groupname ${directory}) + string(REPLACE "src" "Sources" groupname ${groupname}) + string(REPLACE "include" "Includes" groupname ${groupname}) + source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${directory}/${child}) + endif() + endforeach() +endfunction() diff --git b/CMakeCommon/SetCompilerFlags.cmake a/CMakeCommon/SetCompilerFlags.cmake new file mode 100644 index 0000000..9e18728 --- /dev/null +++ a/CMakeCommon/SetCompilerFlags.cmake @@ -0,0 +1,109 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +## DETERMINE COMPILER AND LINKER FLAGS +# +function(SetGNUCompilerFlags compiler_libs) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(compiler_libs pthread rt PARENT_SCOPE) + # -Wall -> All warnings + # -Wextra -> Even more warnings + # -Werror -> Warnings are errors + set(warning_flags "-Wall -Wextra") + if (WARNINGS_ARE_ERRORS STREQUAL ON) + set(warning_flags "${warning_flags} -Werror") + endif() + if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -std=c99 ${warning_flags}" + PARENT_SCOPE) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DEMBB_DEBUG" + PARENT_SCOPE) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG" + PARENT_SCOPE) + set(CMAKE_C_FLAGS_COVERAGE + "${CMAKE_C_FLAGS_COVERAGE} -O0 -fprofile-arcs -ftest-coverage" + PARENT_SCOPE) + endif() + if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++03 ${warning_flags}" + PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DEMBB_DEBUG" + PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG" + PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_COVERAGE + "${CMAKE_CXX_FLAGS_COVERAGE} -O0 -fprofile-arcs -ftest-coverage" + PARENT_SCOPE) + endif() + endif() +endfunction() + +function(SetVisualStudioCompilerFlags) + if(MSVC) + # /Wall -> All warnings + # /WX -> Warnings as errors + # + # Globally deactivated warning numbers (by flag \wd#): + # 4820 -> Deactivates warning "2/4/... bytes padding added after some type" + # 4514 -> Deactivates warning "'fct': unreferenced inline function has + # been removed" + # 4668 -> Deactivates warning "'macro' is not defined as preprocessor macro, + # replacing with '0' in #if/#elif" + # 4710 -> Deactivates warning "Function not inlined" + # 4350 -> Deactivates warning "Behavior change ...", which warns a + # behavior change since VS C++ 2002, when using R-values as + # L-value arguments. This warning occurs a lot in the VC libs. + # 4571 -> Deactivates warning that when compiling with /EHs, + # a catch(...) block will not catch a structured exception. + # 4625 -> Deactivates warning for derived classes + # when copy constructor could not be generated because + # a base class copy constructor is inaccessible + # 4626 -> Deactivates warning for derived classes + # when assignment operator could not be generated because + # a base class assignment operator is inaccessible + # 4711 -> Deactivates warning for inline functions + # This is only an informational warning about which functions + # have been inlined by the compiler. + # 4255 -> Deactivates warning "no function prototype given converting () to (void)" + # + # Locally suppressed warnings (should not be globally suppressed): + # 4640 -> Information that local static variable initialization is not + # thread-safe. + # + # VS 2015 specific warnings: + # 5026 -> Move constructor was implicitly defined as deleted + # 5027 -> Move assignment operator was implicitly defined as deleted + # + set(warning_flags "/Wall /wd4820 /wd4514 /wd4668 /wd4710 /wd4350 /wd4571 /wd4625 /wd4626 /wd4711 /wd4255") + if (WARNINGS_ARE_ERRORS STREQUAL ON) + set(warning_flags "${warning_flags} /WX") + endif() + string(FIND "${CMAKE_GENERATOR}" "Visual Studio 14 2015" vs2015_state) + if (vs2015_state EQUAL 0) + set(warning_flags "${warning_flags} /wd5026 /wd5027") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warning_flags}" PARENT_SCOPE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${warning_flags}" PARENT_SCOPE) + endif() +endfunction() diff --git b/CMakeCommon/SetInstallPaths.cmake a/CMakeCommon/SetInstallPaths.cmake new file mode 100644 index 0000000..bd98c78 --- /dev/null +++ a/CMakeCommon/SetInstallPaths.cmake @@ -0,0 +1,76 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +## Sets the install base path for headers, libraries, and the documentation +# +function(SetInstallPaths) + if(WIN32) + if (DEFINED INSTALL_PREFIX) + # User given install path given when calling cmake as "-DINSTALL_PREFIX=...". + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}) + else() + file(TO_CMAKE_PATH "$ENV{ProgramFiles}" _PROG_FILES) # 32-bit dir on win32, useless to us on win64 + file(TO_CMAKE_PATH "$ENV{ProgramFiles(x86)}" _PROG_FILES_X86) # 32-bit dir: only set on win64 + file(TO_CMAKE_PATH "$ENV{ProgramW6432}" _PROG_FILES_W6432) # 64-bit dir: only set on win64 + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # 64-bit build on win64 + set(_PROGFILESDIR "${_PROG_FILES_W6432}") + else() + if(_PROG_FILES_W6432) + # 32-bit build on win64 + set(_PROGFILESDIR "${_PROG_FILES_X86}") + else() + # 32-bit build on win32 + set(_PROGFILESDIR "${_PROG_FILES}") + endif() + endif() + set(CMAKE_INSTALL_PREFIX "${_PROGFILESDIR}/${CMAKE_PROJECT_NAME}-${EMBB_BASE_VERSION_MAJOR}.${EMBB_BASE_VERSION_MINOR}.${EMBB_BASE_VERSION_PATCH}") + endif() + set(INSTALL_PREFIX_DOCS "${CMAKE_INSTALL_PREFIX}/doc") + #STRING(REPLACE "\\" "\\\\" CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ) + #STRING(REPLACE "/" "\\\\" CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ) + #STRING(REPLACE "/" "\\\\" INSTALL_PREFIX_DOCS ${INSTALL_PREFIX_DOCS} ) + #STRING(REPLACE "\\" "\\\\" INSTALL_PREFIX_DOCS ${INSTALL_PREFIX_DOCS} ) + else() + if (DEFINED INSTALL_PREFIX) + # User given install path given when calling cmake as "-DINSTALL_PREFIX=...". + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}) + else() + set(CMAKE_INSTALL_PREFIX "/usr/local") + endif() + set(INSTALL_PREFIX_DOCS "${CMAKE_INSTALL_PREFIX}/share/doc/${CMAKE_PROJECT_NAME}-${EMBB_BASE_VERSION_MAJOR}.${EMBB_BASE_VERSION_MINOR}.${EMBB_BASE_VERSION_PATCH}") + endif() + + set(INSTALL_PREFIX ${INSTALL_PREFIX} PARENT_SCOPE) + set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} PARENT_SCOPE) + set(INSTALL_PREFIX_DOCS ${INSTALL_PREFIX_DOCS} PARENT_SCOPE) + + message("-- Installation path is ${CMAKE_INSTALL_PREFIX}") + if (INSTALL_DOCS STREQUAL "ON") + message("-- Installation path for documentation is ${INSTALL_PREFIX_DOCS}") + else() + message("-- Disabled installation of documentation") + endif() +endfunction() diff --git b/CMakeLists.txt a/CMakeLists.txt new file mode 100644 index 0000000..c79dbd3 --- /dev/null +++ a/CMakeLists.txt @@ -0,0 +1,212 @@ +# Copyright (c) 2014-2016, Siemens AG. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +project (EMBB) +cmake_minimum_required (VERSION 2.8.9) + +# Version number +set (EMBB_BASE_VERSION_MAJOR 0) +set (EMBB_BASE_VERSION_MINOR 4) +set (EMBB_BASE_VERSION_PATCH 0) + +# Fix compilation for CMake versions >= 3.1 +# +# New Policy 0054: +# CMake 3.1 and above no longer implicitly dereference variables +# or interpret keywords in an if() command argument when it is a +# Quoted Argument. +# See http://www.cmake.org/cmake/help/v3.1/policy/CMP0054.html +# +# New Policy 0053: +# CMake 3.1 introduced faster implementation of evaluation of the +# Variable References and Escape Sequences. This breaks compilation +# here. +# See http://www.cmake.org/cmake/help/v3.1/policy/CMP0053.html +# +# Set those policies to be treated the legacy (CMake < 3.1) way. +if(POLICY CMP0054) + cmake_policy(SET CMP0054 OLD) +endif(POLICY CMP0054) + +if(POLICY CMP0053) + cmake_policy(SET CMP0053 OLD) +endif(POLICY CMP0053) + +# give the user the possibility, to append compiler flags +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CMAKE_CXX_FLAGS}") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CMAKE_C_FLAGS}") + +if(NOT CMAKE_BUILD_TYPE) +set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: Debug Release + RelWithDebInfo MinSizeRel Coverage." FORCE) +endif(NOT CMAKE_BUILD_TYPE) + +# Check for clang, masquerade it as GNU +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_COMPILER_IS_GNUCC true) +endif () +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_COMPILER_IS_GNUCXX true) +endif() + +## Command line options +# +# The set option will be converted to uppercase letters by cmake!! --> ON/OFF +# Note that the help string (second argument) cannot be printed by cmake. +# +option(BUILD_TESTS "Specify whether tests should be built" ON) +option(BUILD_EXAMPLES "Specify whether examples should be built" OFF) +option(USE_EXCEPTIONS "Specify whether exceptions should be activated in C++" ON) +option(INSTALL_DOCS "Specify whether Doxygen docs should be installed" ON) +option(WARNINGS_ARE_ERRORS "Specify whether warnings should be treated as errors" OFF) +option(USE_AUTOMATIC_INITIALIZATION "Specify whether the MTAPI C++ interface, algorithms and dataflow should automatically intialize the MTAPI node if no explicit initialization is present" ON) +option(BUILD_OPENCL_PLUGIN "Specify whether the MTAPI OpenCL plugin should be built" OFF) + +## LOCAL INSTALLATION OF SUBPROJECT BINARIES +# +include(CMakeCommon/CopyInstallFiles.cmake) # Needed in all subprojects +set(local_install_dir ${CMAKE_CURRENT_BINARY_DIR}/binaries) + +if (WARNINGS_ARE_ERRORS STREQUAL ON) + message("-- Warnings are treated as errors") + set(EMBB_USE_EXCEPTIONS 1) +else() + message("-- Warnings are not treated as errors (default)") +endif() +message(" (set with command line option -DWARNINGS_ARE_ERRORS=ON/OFF)") + +if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) + message("-- MTAPI/Tasks automatic initialization enabled (default)") +else() + message("-- MTAPI/Tasks automatic initialization disabled") +endif() +message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)") + +include(CMakeCommon/SetCompilerFlags.cmake) +SetGNUCompilerFlags(compiler_libs compiler_flags) +SetVisualStudioCompilerFlags(compiler_libs compiler_flags) + +## Exception handling in C++ +# +if (USE_EXCEPTIONS STREQUAL ON) + message("-- Exceptions enabled (default) ") + set(EMBB_USE_EXCEPTIONS 1) +else() + message("-- Exceptions disabled") + set(EMBB_NO_EXCEPTIONS) # A preprocessor define + if (CMAKE_COMPILER_IS_GNUCXX) + LIST(APPEND ${CMAKE_CXX_FLAGS} "-fno-exceptions") + elseif (MSVC) + LIST(FIND ${CMAKE_CXX_FLAGS} "/EHsc" EXCEPTION_FLAG) + if (EXCEPTION_FLAG) + string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_HAS_EXCEPTIONS=0") + endif() +endif() +message(" (set with command line option -DUSE_EXCEPTIONS=ON/OFF)") + +# these are the test executables, we expect to be generated. +set(EXPECTED_EMBB_TEST_EXECUTABLES "embb_algorithms_cpp_test" + "embb_base_c_test" + "embb_base_cpp_test" + "embb_containers_cpp_test" + "embb_dataflow_cpp_test" + "embb_mtapi_c_test" + "embb_mtapi_cpp_test" + "embb_mtapi_network_c_test" + "embb_tasks_cpp_test" + ) + +# if opencl is there, we also expect the mtapi opencl test to be generated +if(BUILD_OPENCL_PLUGIN STREQUAL ON) + list(APPEND EXPECTED_EMBB_TEST_EXECUTABLES "embb_mtapi_opencl_c_test") +endif() + + +## Copy test execution script to local binaries folder + +if (DEFINED CYGWIN) + set(test_script_in run_tests_cygwin.sh) + set(test_script_out run_tests.sh) +elseif (DEFINED UNIX) + set(test_script_in run_tests_unix.sh) + set(test_script_out run_tests.sh) +else() + set(test_script_in run_tests_windows.bat) + set(test_script_out run_tests.bat) +endif() +CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/scripts/${test_script_in}.cmake binaries/${test_script_out} ) + +## Test and Partest build +# +include(CMakeCommon/CheckEnableTests.cmake) +if (BUILD_TESTS STREQUAL ON) + message("-- Building tests enabled (default)") +else() + message("-- Building tests disabled") +endif() +message(" (set with command line option -DBUILD_TESTS=ON/OFF)") +CheckPartestInstall(${BUILD_TESTS} partest_includepath partest_libpath) + +## SUBPROJECTS +# +add_subdirectory(base_c) +add_subdirectory(base_cpp) +add_subdirectory(mtapi_c) +add_subdirectory(mtapi_plugins_c/mtapi_network_c) +if(BUILD_OPENCL_PLUGIN STREQUAL ON) + add_subdirectory(mtapi_plugins_c/mtapi_opencl_c) +endif() +add_subdirectory(tasks_cpp) +add_subdirectory(mtapi_cpp) +add_subdirectory(containers_cpp) +add_subdirectory(algorithms_cpp) +add_subdirectory(dataflow_cpp) +if (BUILD_EXAMPLES STREQUAL ON) + message("-- Building examples enabled") + add_subdirectory(doc/examples) +else() + message("-- Building examples disabled (default)") +endif() +message(" (set with command line option -DBUILD_EXAMPLES=ON/OFF)") + +## INSTALLATION +# +include(CMakeCommon/SetInstallPaths.cmake) +SetInstallPaths() + +## DOXYGEN +# +if(EXISTS "${EMBB_SOURCE_DIR}/doc/reference/Doxyfile.in") + include(CMakeCommon/CreateDoxygenDocumentationTarget.cmake) + CreateDoxygenDocumentationTarget() +endif() + + +if (INSTALL_DOCS STREQUAL ON) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/ + DESTINATION ${INSTALL_PREFIX_DOCS} FILES_MATCHING PATTERN "*.*" PATTERN "CMakeLists.txt" EXCLUDE) +endif() diff --git b/TC_Toolchain.cmake a/TC_Toolchain.cmake new file mode 100644 index 0000000..49d5fd3 --- /dev/null +++ a/TC_Toolchain.cmake @@ -0,0 +1,18 @@ +# this one is important +SET(CMAKE_SYSTEM_NAME Generic) +#this one not so much +SET(CMAKE_SYSTEM_VERSION 1) +SET(CMAKE_SYSTEM_PROCESSOR TriCore) + +# specify the cross compiler +SET(CMAKE_C_COMPILER C:/HighTec/toolchains/tricore/v4.6.6.0-infineon-1.1/bin/tricore-gcc.exe) +SET(CMAKE_CXX_COMPILER C:/HighTec/toolchains/tricore/v4.6.6.0-infineon-1.1/bin/tricore-g++.exe) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH C:/HighTec/toolchains/tricore/v4.6.6.0-infineon-1.1) + +# search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) \ No newline at end of file diff --git b/algorithms_cpp/CMakeLists.txt a/algorithms_cpp/CMakeLists.txt new file mode 100644 index 0000000..5378bd3 --- /dev/null +++ a/algorithms_cpp/CMakeLists.txt @@ -0,0 +1,38 @@ +project (project_embb_algorithms) + +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_ALGORITHMS_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_ALGORITHMS_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../tasks_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../tasks_cpp/include) + +add_library(embb_algorithms_cpp ${EMBB_ALGORITHMS_CPP_SOURCES} + ${EMBB_ALGORITHMS_CPP_HEADERS}) +target_link_libraries(embb_algorithms_cpp embb_tasks_cpp) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_algorithms_cpp_test ${EMBB_ALGORITHMS_CPP_TEST_SOURCES}) + target_link_libraries(embb_algorithms_cpp_test embb_algorithms_cpp + embb_tasks_cpp embb_mtapi_c partest embb_base_cpp + embb_base_c ${compiler_libs}) + CopyBin(BIN embb_algorithms_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_algorithms_cpp DESTINATION lib) diff --git b/algorithms_cpp/include/embb/algorithms/algorithms.h a/algorithms_cpp/include/embb/algorithms/algorithms.h new file mode 100644 index 0000000..bac8709 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/algorithms.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_ALGORITHMS_H_ +#define EMBB_ALGORITHMS_ALGORITHMS_H_ + +/** + * \defgroup CPP_ALGORITHMS Algorithms + * High-level parallel algorithms and functionalities. + * \ingroup CPP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_ALGORITHMS_ALGORITHMS_H_ diff --git b/algorithms_cpp/include/embb/algorithms/count.h a/algorithms_cpp/include/embb/algorithms/count.h new file mode 100644 index 0000000..6016687 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/count.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_COUNT_H_ +#define EMBB_ALGORITHMS_COUNT_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_COUNT Counting + * Parallel count operation + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Counts in parallel the number of elements in a range that are equal to + * the specified value. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. + * + * \return The number of elements that are equal to \c value + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * operations. + * \see CountIf(), embb::mtapi::ExecutionPolicy + * \tparam RAI Random access iterator + * \tparam ValueType Type of \c value that is compared to the elements in the + * range using the \c operator==. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + const ValueType& value, + /**< [IN] Value that the elements in the range are compared to using + \c operator== */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the counting algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +/** + * Counts in parallel the number of elements in a range for which the comparison + * function returns \c true. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. + * + * \return The number of elements for which \c comparison returns true + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * function. + * \see Count(), embb::mtapi::ExecutionPolicy + * \tparam RAI Random access iterator + * \tparam ComparisonFunction Unary predicate with argument of type + * std::iterator_traits::value_type. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range + RAI last, */ + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison, + /**< [IN] Unary predicate used to test the elements in the range. Elements for + which \c comparison returns true are counted. */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the counting algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + RAI last, + const ValueType& value, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + RAI last, + const ValueType& value + ) { + return Count(first, last, value, embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + RAI last, + const ValueType& value, + const embb::tasks::ExecutionPolicy& policy + ) { + return Count(first, last, value, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + RAI last, + ComparisonFunction comparison + ) { + return CountIf(first, last, comparison, embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy + ) { + return CountIf(first, last, comparison, policy, 0); +} + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_COUNT_H_ diff --git b/algorithms_cpp/include/embb/algorithms/for_each.h a/algorithms_cpp/include/embb/algorithms/for_each.h new file mode 100644 index 0000000..54e57a0 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/for_each.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_FOR_EACH_H_ +#define EMBB_ALGORITHMS_FOR_EACH_H_ + +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_FOREACH Foreach + * Parallel foreach loop + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Applies a unary function to the elements of a range in parallel. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the order in which the function is applied to + * the elements. + * \see embb::mtapi::ExecutionPolicy, ZipIterator + * \tparam RAI Random access iterator + * \tparam Function Unary function with argument of type + * std::iterator_traits::value_type. + */ +template +void ForEach( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + Function unary, + /**< [IN] Unary function applied to each element in the range */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the foreach loop execution */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are treated in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy. + */ +template +void ForEach( + RAI first, + RAI last, + Function unary, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void ForEach( + RAI first, + RAI last, + Function unary + ) { + ForEach(first, last, unary, embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void ForEach( + RAI first, + RAI last, + Function unary, + const embb::tasks::ExecutionPolicy& policy + ) { + ForEach(first, last, unary, policy, 0); +} + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_FOR_EACH_H_ diff --git b/algorithms_cpp/include/embb/algorithms/identity.h a/algorithms_cpp/include/embb/algorithms/identity.h new file mode 100644 index 0000000..d2ae305 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/identity.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_IDENTITY_H_ +#define EMBB_ALGORITHMS_IDENTITY_H_ + +namespace embb { +namespace algorithms { + +/** + * Unary identity functor. + * + * \ingroup CPP_ALGORITHMS + */ +struct Identity { + /** + * Returns \c value unchanged. + * + * \return \c value + * \tparam Type Any type + */ + template + Type& operator()( + Type& value + /**< [IN] Value that is returned unchanged */ + ) { + return value; + } + + /** + * Returns \c value unchanged. + * + * \return \c value + * \tparam Type Any type + */ + template + const Type& operator()( + const Type& value + /**< [IN] Value that is returned unchanged */ + ) { + return value; + } +}; + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_IDENTITY_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/count-inl.h a/algorithms_cpp/include/embb/algorithms/internal/count-inl.h new file mode 100644 index 0000000..dd11e38 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/count-inl.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_COUNT_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_COUNT_INL_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace internal { + +template +class ValueComparisonFunction{ + public: + explicit ValueComparisonFunction(const ValueType& value) + : value_(value) {} + ValueComparisonFunction(const ValueComparisonFunction& other) + : value_(other.value_) {} + + template + int operator()(ElementType element) { + if (element == value_) { + return 1; + } else { + return 0; + } + } + private: + const ValueType &value_; + ValueComparisonFunction &operator=( + const ValueComparisonFunction& other); +}; + +template +class FunctionComparisonFunction{ + public: + explicit FunctionComparisonFunction(Function function) + : function_(function) {} + FunctionComparisonFunction(const FunctionComparisonFunction &other) + : function_(other.function_) {} + + template + int operator()(ElementType element) { + if (function_(element)) { + return 1; + } else { + return 0; + } + } + private: + Function function_; + FunctionComparisonFunction &operator=( + const FunctionComparisonFunction& other); +}; + +} // namespace internal + +template +typename std::iterator_traits::difference_type + Count(RAI first, RAI last, const ValueType& value, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type Difference; + return Reduce(first, last, Difference(0), std::plus(), + internal::ValueComparisonFunction(value), policy, + block_size); +} + +template +typename std::iterator_traits::difference_type + CountIf(RAI first, RAI last, ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type Difference; + return Reduce(first, last, Difference(0), std::plus(), + internal::FunctionComparisonFunction + (comparison), policy, block_size); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_COUNT_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h a/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h new file mode 100644 index 0000000..8a4457a --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_FOR_EACH_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_FOR_EACH_INL_H_ + +#include + +#include +#include +#include +#include + +namespace embb { +namespace algorithms { + +namespace internal { + +template +class ForEachFunctor { + public: + /** + * Constructs a for-each functor with arguments. + */ + ForEachFunctor(size_t chunk_first, size_t chunk_last, Function unary, + const embb::tasks::ExecutionPolicy& policy, + const BlockSizePartitioner& partitioner) + : chunk_first_(chunk_first), chunk_last_(chunk_last), + unary_(unary), policy_(policy), partitioner_(partitioner) { + } + + void Action(embb::tasks::TaskContext&) { + if (chunk_first_ == chunk_last_) { + // Leaf case, recursed to single chunk. Do work on chunk: + ChunkDescriptor chunk = partitioner_[chunk_first_]; + RAI first = chunk.GetFirst(); + RAI last = chunk.GetLast(); + for (RAI it = first; it != last; ++it) { + unary_(*it); + } + } else { + // Recurse further: + size_t chunk_split_index = (chunk_first_ + chunk_last_) / 2; + // Split chunks into left / right branches: + self_t functor_l(chunk_first_, + chunk_split_index, + unary_, policy_, partitioner_); + self_t functor_r(chunk_split_index + 1, + chunk_last_, + unary_, policy_, partitioner_); + embb::tasks::Task task_l = embb::tasks::Node::GetInstance().Spawn( + embb::tasks::Action( + base::MakeFunction(functor_l, &self_t::Action), + policy_)); + embb::tasks::Task task_r = embb::tasks::Node::GetInstance().Spawn( + embb::tasks::Action( + base::MakeFunction(functor_r, &self_t::Action), + policy_)); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + } + } + + private: + typedef ForEachFunctor self_t; + + private: + size_t chunk_first_; + size_t chunk_last_; + Function unary_; + const embb::tasks::ExecutionPolicy& policy_; + const BlockSizePartitioner& partitioner_; + + /** + * Disables assignment. + */ + ForEachFunctor& operator=(const ForEachFunctor&); +}; + +template +void ForEachRecursive(RAI first, RAI last, Function unary, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + if (distance == 0) { + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for ForEach"); + } + unsigned int num_cores = policy.GetCoreCount(); + if (num_cores == 0) { + EMBB_THROW(embb::base::ErrorException, "No cores in execution policy"); + } + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + // Determine actually used block size + if (block_size == 0) { + block_size = (static_cast(distance) / num_cores); + if (block_size == 0) { + block_size = 1; + } + } + // Check task number sufficiency + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Not enough MTAPI tasks available for parallel foreach"); + } + + BlockSizePartitioner partitioner(first, last, block_size); + ForEachFunctor functor(0, + partitioner.Size() - 1, + unary, policy, partitioner); + embb::tasks::Task task = node.Spawn(embb::tasks::Action( + base::MakeFunction(functor, + &ForEachFunctor::Action), + policy)); + task.Wait(MTAPI_INFINITE); +} + +template +void ForEachIteratorCheck(RAI first, RAI last, Function unary, + const embb::tasks::ExecutionPolicy& policy, size_t block_size, + std::random_access_iterator_tag) { + return ForEachRecursive(first, last, unary, policy, block_size); +} + +} // namespace internal + +template +void ForEach(RAI first, const RAI last, Function unary, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) { + typename std::iterator_traits::iterator_category category; + internal::ForEachIteratorCheck(first, last, unary, policy, block_size, + category); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_FOR_EACH_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h a/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h new file mode 100644 index 0000000..f278811 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_MERGE_SORT_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_MERGE_SORT_INL_H_ + +#include +#include +#include + +#include +#include +#include + +namespace embb { +namespace algorithms { + +namespace internal { + +/** + * Contains the merge sort MTAPI action function and data needed there. + */ +template +class MergeSortFunctor { + public: + typedef typename std::iterator_traits::value_type value_type; + + MergeSortFunctor(size_t chunk_first, size_t chunk_last, + RAITemp temporary_first, ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + const BlockSizePartitioner& partitioner, + const RAI& global_first, int depth) + : chunk_first_(chunk_first), chunk_last_(chunk_last), + temp_first_(temporary_first), + comparison_(comparison), policy_(policy), partitioner_(partitioner), + global_first_(global_first), depth_(depth) { + } + + void Action(embb::tasks::TaskContext&) { + size_t chunk_split_index = (chunk_first_ + chunk_last_) / 2; + if (chunk_first_ == chunk_last_) { + // Leaf case: recurse into a single chunk's elements: + ChunkDescriptor chunk = partitioner_[chunk_first_]; + MergeSortChunk(chunk.GetFirst(), chunk.GetLast(), depth_); + } else { + // Recurse further, split chunks: + self_t functor_l(chunk_first_, + chunk_split_index, + temp_first_, + comparison_, policy_, partitioner_, + global_first_, depth_ + 1); + self_t functor_r(chunk_split_index + 1, + chunk_last_, + temp_first_, + comparison_, policy_, partitioner_, + global_first_, depth_ + 1); + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + embb::tasks::Task task_l = node.Spawn( + embb::tasks::Action( + base::MakeFunction(functor_l, &self_t::Action), + policy_)); + embb::tasks::Task task_r = node.Spawn( + embb::tasks::Action( + base::MakeFunction(functor_r, &self_t::Action), + policy_)); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + + ChunkDescriptor ck_f = partitioner_[chunk_first_]; + ChunkDescriptor ck_m = partitioner_[chunk_split_index + 1]; + ChunkDescriptor ck_l = partitioner_[chunk_last_]; + if(CloneBackToInput(depth_)) { + // Merge from temp into input: + difference_type first = std::distance(global_first_, ck_f.GetFirst()); + difference_type mid = std::distance(global_first_, ck_m.GetFirst()); + difference_type last = std::distance(global_first_, ck_l.GetLast()); + SerialMerge(temp_first_ + first, temp_first_ + mid, temp_first_ + last, + ck_f.GetFirst(), + comparison_); + } else { + // Merge from input into temp: + SerialMerge(ck_f.GetFirst(), ck_m.GetFirst(), ck_l.GetLast(), + temp_first_ + std::distance(global_first_, ck_f.GetFirst()), + comparison_); + } + } + } + + /** + * Serial merge sort of elements within a single chunk. + */ + void MergeSortChunk(RAI first, + RAI last, + int depth) { + size_t distance = static_cast( + std::distance(first, last)); + if (distance <= 1) { + // Leaf case: + if (!CloneBackToInput(depth) && distance != 0) { + RAITemp temp_first = temp_first_; + std::advance(temp_first, std::distance(global_first_, first)); + *temp_first = *first; + } + return; + } + // Recurse further. Use binary split, ignoring chunk size as this + // recursion is serial and has leaf size 1: + ChunkPartitioner partitioner(first, last, 2); + ChunkDescriptor ck_l = partitioner[0]; + ChunkDescriptor ck_r = partitioner[1]; + MergeSortChunk( + ck_l.GetFirst(), + ck_l.GetLast(), + depth + 1); + MergeSortChunk( + ck_r.GetFirst(), + ck_r.GetLast(), + depth + 1); + if (CloneBackToInput(depth)) { + // Merge from temp into input: + difference_type d_first = std::distance(global_first_, ck_l.GetFirst()); + difference_type d_mid = std::distance(global_first_, ck_r.GetFirst()); + difference_type d_last = std::distance(global_first_, ck_r.GetLast()); + SerialMerge( + temp_first_ + d_first, temp_first_ + d_mid, temp_first_ + d_last, + ck_l.GetFirst(), + comparison_); + } else { + // Merge from input into temp: + SerialMerge( + ck_l.GetFirst(), ck_r.GetFirst(), ck_r.GetLast(), + temp_first_ + std::distance(global_first_, ck_l.GetFirst()), + comparison_); + } + } + + /** + * Determines the input and output arrays for one level in merge sort. + * + * \return \c true if the temporary data range is input and the array to be + * sorted is output. \c false, if the other way around. + */ + bool CloneBackToInput(int depth) { + return depth % 2 == 0 ? true : false; + } + + private: + typedef MergeSortFunctor self_t; + typedef typename std::iterator_traits::difference_type + difference_type; + + private: + size_t chunk_first_; + size_t chunk_last_; + RAITemp temp_first_; + ComparisonFunction comparison_; + const embb::tasks::ExecutionPolicy& policy_; + const BlockSizePartitioner& partitioner_; + const RAI& global_first_; + int depth_; + + MergeSortFunctor(const MergeSortFunctor&); + MergeSortFunctor& operator=(const MergeSortFunctor&); + + template + void SerialMerge(RAIIn first, RAIIn mid, RAIIn last, RAIOut out, + ComparisonFunction comparison) { + RAIIn save_mid = mid; + while ((first != save_mid) && (mid != last)) { + if (comparison(*first, *mid)) { + *out = *first; + ++out; + ++first; + } else { + *out = *mid; + ++out; + ++mid; + } + } + while (first != save_mid) { + *out = *first; + ++out; + ++first; + } + while(mid != last) { + *out = *mid; + ++out; + ++mid; + } + } +}; + +template +void MergeSortIteratorCheck( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size, + std::random_access_iterator_tag + ) { + typedef typename std::iterator_traits::difference_type difference_type; + typedef MergeSortFunctor + functor_t; + difference_type distance = std::distance(first, last); + if (distance == 0) { + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for MergeSort"); + } + unsigned int num_cores = policy.GetCoreCount(); + if (num_cores == 0) { + EMBB_THROW(embb::base::ErrorException, "No cores in execution policy"); + } + // Determine actually used block size + if (block_size == 0) { + block_size = (static_cast(distance) / num_cores); + if (block_size == 0) + block_size = 1; + } + // Check task number sufficiency + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Not enough MTAPI tasks available to perform merge sort"); + } + + BlockSizePartitioner partitioner(first, last, block_size); + functor_t functor(0, + partitioner.Size() - 1, + temporary_first, + comparison, + policy, + partitioner, + first, + 0); + embb::tasks::Task task = embb::tasks::Node::GetInstance().Spawn( + embb::tasks::Action( + base::MakeFunction(functor, &functor_t::Action), + policy)); + + task.Wait(MTAPI_INFINITE); +} + +} // namespace internal + +template +void MergeSort(RAI first, RAI last, RAITemp temporary_first, + ComparisonFunction comparison, const embb::tasks::ExecutionPolicy& policy, + size_t block_size) { + typedef typename std::iterator_traits::iterator_category category; + internal::MergeSortIteratorCheck(first, last, temporary_first, comparison, + policy, block_size, category()); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_MERGE_SORT_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/partition-inl.h a/algorithms_cpp/include/embb/algorithms/internal/partition-inl.h new file mode 100644 index 0000000..f1c2bca --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/partition-inl.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_PARTITION_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_PARTITION_INL_H_ + +namespace embb { +namespace algorithms { +namespace internal { + +template +ChunkDescriptor::ChunkDescriptor( + RAI first, RAI last) : + first_(first), last_(last) { +} + +template +RAI ChunkDescriptor::GetFirst() const { + return first_; +} + +template +RAI ChunkDescriptor::GetLast() const { + return last_; +} + +template +BlockSizePartitioner::BlockSizePartitioner( + RAI first, RAI last, size_t chunkSize) : + first_(first), last_(last), chunk_size_(chunkSize) { + elements_count_ = static_cast(std::distance(first_, last_)); + chunks_ = elements_count_ / chunk_size_; + if (elements_count_ % chunk_size_ != 0) { + chunks_++; + } +} + +template +size_t BlockSizePartitioner::Size() { + return chunks_; +} + +template +const ChunkDescriptor + BlockSizePartitioner::operator[]( + size_t const & index) const { + typedef typename std::iterator_traits::difference_type + difference_type; + RAI first_new(first_); + first_new += static_cast(chunk_size_ * index); + RAI last_new(first_new); + if (index >= chunks_ - 1) { + last_new = last_; + } else { + last_new += static_cast(chunk_size_); + } + return ChunkDescriptor(first_new, last_new); +} + +template +size_t ChunkPartitioner::Size() { + return size_; +} + +template +ChunkPartitioner::ChunkPartitioner( + RAI first, RAI last, size_t amountChunks) : + first_(first), last_(last) { + if (amountChunks > 0) { + size_ = amountChunks; + } else { + // if no concrete chunk size was given, use number of cores + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + size_ = node.GetWorkerThreadCount(); + } + elements_count_ = static_cast(std::distance(first_, last_)); + if (size_ > elements_count_) { + // if we want to make more chunks than we have elements, correct + // the number of chunks + size_ = elements_count_; + } + standard_chunk_size_ = elements_count_ / size_; + bigger_chunk_count_ = elements_count_ % size_; +} + +template +const ChunkDescriptor + ChunkPartitioner::operator[]( + size_t const& index) const { + typedef typename std::iterator_traits::difference_type + difference_type; + // Number of element preceding elements in the given chunk + size_t prec_elements_count = 0; + if (index <= bigger_chunk_count_) { + prec_elements_count = index * (standard_chunk_size_ + 1); + } else { + prec_elements_count = + (standard_chunk_size_ + 1) * bigger_chunk_count_ + + (standard_chunk_size_ * (index - bigger_chunk_count_)); + } + size_t cur_elements_count = (index < bigger_chunk_count_) + ? (standard_chunk_size_ + 1) + : standard_chunk_size_; + RAI first_new(first_); + first_new += static_cast(prec_elements_count); + RAI last_new(first_new); + last_new += static_cast(cur_elements_count); + return ChunkDescriptor(first_new, last_new); +} + +} // namespace internal +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_PARTITION_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/partition.h a/algorithms_cpp/include/embb/algorithms/internal/partition.h new file mode 100644 index 0000000..948eb28 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/partition.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_PARTITION_H_ +#define EMBB_ALGORITHMS_INTERNAL_PARTITION_H_ + +#include + +namespace embb { +namespace algorithms { +namespace internal { +/** + * A chunk descriptor. + * + * Describes a single partition of a 1-dimensional + * partitioning, using first and last iterator. + * + * \tparam RAI Type of the iterator. + */ + +template +class ChunkDescriptor { + private: + RAI first_; + RAI last_; + + public: + /** + * Constructor. + * + * \param first The first iterator. + * \param last The last iterator + */ + ChunkDescriptor(RAI first, RAI last); + + /** + * Gets the first iterator. + * + * \return The first iterator. + * + * \waitfree + */ + RAI GetFirst() const; + + /** + * Gets the last iterator. + * + * \return The last iterator. + * + * \waitfree + */ + RAI GetLast() const; +}; + +/** + * Partitioner Interface. + * + * Describes the interface for accessing a 1-dimensional partitioning. + * + * \tparam RAI Type of the iterator. + */ +template +class IPartitioner { + public: + virtual ~IPartitioner() {} + + private: + /** + * Gets the amount of partitions. + * + * \return A size_t. + * + * \waitfree + */ + virtual size_t Size() = 0; + + /** + * Gets a single partition. + * + * \param index Zero-based index of the partitioner. Range: [0;Size()-1] + * + * \return The indexed value. + * + * \waitfree + */ + virtual const ChunkDescriptor operator[]( + size_t const& index) const = 0; +}; + +/** + * A block size partitioner. + * + * Partitions a 1-dim. collection of elements with total order that provides a + * forward iterator into partitions of size chunkSize. If no chunkSize is given, + * chunkSize is set to 1. + * + * Example: + * + * int A[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; + * const int N = (sizeof(A) / sizeof(int) ); + * embb::algorithms::ChunkPartitioner< int* > partitioner(A, A + N, 5); + * + * With that, the array is partitioned into chunks of size 5. We therefore + * have following partitions: + * 1: [1,2,3,4,5] + * 2: [6,7,8,9,10] + * 3: [11,12,13] + * + * \tparam RAI Type of the iterator. + */ +template +class BlockSizePartitioner : IPartitioner < RAI > { + private: + RAI first_; + RAI last_; + size_t chunk_size_; + size_t elements_count_; + size_t chunks_; + + public: + /** + * Constructor. + * + * + * \param first The first iterator of the collection. + * \param last The last iterator of the collection. + * \param chunkSize (Optional) size of the chunk. + */ + BlockSizePartitioner( + RAI first, RAI last, size_t chunkSize = 1); + + /** + * See IPartitioner + * + * \waitfree + */ + virtual size_t Size(); + + /** + * See IPartitioner + * + * \waitfree + */ + virtual const ChunkDescriptor operator[]( + size_t const& index) const; +}; + +/** + * A chunk partitioner. + * + * Partitions a 1-dim. collection of elements with total order that provides a + * forward iterator into amountChunks partitions. If no amountChunks is given, + * the collection is split into number of cores partitions. + * + * It is avoided to have unbalanced partitions, so they are equally "filled" up. + * With that, we have at most two partition sizes: basic_size and basic_size+1. + * The partitions with basic_size+1 are the ones at the firstning. + * + * If a higher number of chunks shall be produced, is more than contained elements, + * the number of chunks is reduced to the number of elements. + * + * Example: + * + * int A[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; + * const int N = (sizeof(A) / sizeof(int) ); + * embb::algorithms::ChunkPartitioner< int* > partitioner(A, A + N, 5); + * + * With that, the array is partitioned into chunks of size 5. We therefore + * have following partitions: + * 1: [1,2,3] + * 2: [3,5,6] + * 3: [7,8,9] + * 4: [10,11] + * 5: [12,13] + * + * \tparam RAI Type of the iterator. + */ +template +class ChunkPartitioner : IPartitioner < RAI > { + private: + size_t size_; + size_t elements_count_; + RAI first_; + RAI last_; + size_t standard_chunk_size_; + size_t bigger_chunk_count_; + + public: + /** + * See IPartitioner + * + * \waitfree + */ + virtual size_t Size(); + + /** + * Constructor. + * + * See class documentation. + * + * \waitfree + * + * \param first The first. + * \param last The last. + * \param amountChunks (Optional) the amount chunks. + */ + ChunkPartitioner(RAI first, RAI last, + size_t amountChunks = 0); + + /** + * See IPartitioner + * + * \waitfree + */ + virtual const ChunkDescriptor operator[]( + size_t const& index) const; +}; + +} // namespace internal +} // namespace algorithms +} // namespace embb + +#include + + +#endif // EMBB_ALGORITHMS_INTERNAL_PARTITION_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h a/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h new file mode 100644 index 0000000..e87927e --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_QUICK_SORT_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_QUICK_SORT_INL_H_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace embb { +namespace algorithms { + +namespace internal { + +template +class QuickSortFunctor { + public: + /** + * Constructs a functor. + */ + QuickSortFunctor(RAI first, RAI last, ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) + : first_(first), last_(last), comparison_(comparison), policy_(policy), + block_size_(block_size) { + } + + /** + * MTAPI action function and starting point of the parallel quick sort. + */ + void Action(embb::tasks::TaskContext&) { + Difference distance = last_ - first_; + if (distance <= 1) { + return; + } else { + Difference pivot = MedianOfNine(first_, last_); + RAI mid = first_ + pivot; + mid = SerialPartition(first_, last_, mid); + if (distance <= static_cast(block_size_)) { + SerialQuickSort(first_, mid); + SerialQuickSort(mid, last_); + } else { + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + QuickSortFunctor functor_l(first_, mid, comparison_, policy_, + block_size_); + embb::tasks::Task task_l = node.Spawn(embb::tasks::Action( + base::MakeFunction(functor_l, &QuickSortFunctor::Action))); + QuickSortFunctor functor_r(mid, last_, comparison_, policy_, + block_size_); + embb::tasks::Task task_r = node.Spawn(embb::tasks::Action( + base::MakeFunction(functor_r, &QuickSortFunctor::Action))); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + } + } + } + + private: + RAI first_; + RAI last_; + ComparisonFunction comparison_; + const embb::tasks::ExecutionPolicy& policy_; + size_t block_size_; + + typedef typename std::iterator_traits::difference_type Difference; + + /** + * Computes the pseudo-median of nine by using MedianOfThree(). + */ + Difference MedianOfNine(RAI first, RAI last) { + Difference distance = last - first; + Difference offset = distance / static_cast(8); + if (offset == 0) { + return distance / 2; + } + Difference pseudo_median_of_nine = MedianOfThree( + first, + MedianOfThree(first, static_cast(0), offset, offset * 2), + MedianOfThree(first, offset * 3, offset * 4, offset * 5), + MedianOfThree(first, offset * 6, offset * 7, distance - 1)); + return pseudo_median_of_nine; + } + + /** + * Computes the median of three. + */ + Difference MedianOfThree(RAI first, Difference left, Difference mid, + Difference right) { + if (comparison_(*(first + left), *(first + mid))) { + if (comparison_(*(first + mid), *(first + right))) { + return mid; + } else { + if (comparison_(*(first + left), *(first + right))) + return right; + else + return left; + } + } else { + if (comparison_(*(first + right), *(first + mid))) { + return mid; + } else { + if (comparison_(*(first + right), *(first + left))) + return right; + else + return left; + } + } + } + + /** + * Performs a quick sort partitioning as serial computation. + */ + RAI SerialPartition(RAI first, RAI last, RAI pivot) { + while (first != last) { + while (comparison_(*first, *pivot)) { + ++first; + if (first == last) + return first; + } + do { + --last; + if (first == last) return first; + } while (comparison_(*pivot, *last)); + std::swap(*first, *last); + if(pivot == first) { + pivot = last; + } else if (pivot == last) { + pivot = first; + } + ++first; + } + return first; + } + + /** + * Performs the quick sort algorithm as serial computation. + */ + void SerialQuickSort(RAI first, RAI last) { + if (last - first <= 1) { + return; + } else { + Difference pivot = MedianOfNine(first, last); + RAI mid = first + pivot; + mid = SerialPartition(first, last, mid); + SerialQuickSort(first, mid); + SerialQuickSort(mid, last); + } + } + + /** + * Disables assignment. + */ + QuickSortFunctor& operator=(const QuickSortFunctor&); + + /** + * Disables Copying. + */ + QuickSortFunctor(const QuickSortFunctor&); +}; + +template +void QuickSortIteratorCheck(RAI first, RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size, + std::random_access_iterator_tag) { + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + if (distance == 0) { + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for QuickSort"); + } + unsigned int num_cores = policy.GetCoreCount(); + if (num_cores == 0) { + EMBB_THROW(embb::base::ErrorException, "No cores in execution policy"); + } + if (block_size == 0) { + block_size = (static_cast(distance) / num_cores); + if (block_size == 0) + block_size = 1; + } + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Not enough MTAPI tasks available for performing quick sort"); + } + QuickSortFunctor functor( + first, last, comparison, policy, block_size); + embb::tasks::Task task = node.Spawn(embb::tasks::Action(base::MakeFunction( + functor, &QuickSortFunctor::Action))); + task.Wait(MTAPI_INFINITE); +} + +} // namespace internal + +template +void QuickSort(RAI first, RAI last, ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::iterator_category category; + internal::QuickSortIteratorCheck(first, last, comparison, + policy, block_size, category()); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_QUICK_SORT_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h a/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h new file mode 100644 index 0000000..28c35ab --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_REDUCE_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_REDUCE_INL_H_ + +#include +#include + +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace internal { + +template +class ReduceFunctor { + public: + ReduceFunctor(size_t chunk_first, size_t chunk_last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + const BlockSizePartitioner& partitioner, + ReturnType& result) + : chunk_first_(chunk_first), chunk_last_(chunk_last), neutral_(neutral), + reduction_(reduction), transformation_(transformation), policy_(policy), + partitioner_(partitioner), result_(result) { + } + + void Action(embb::tasks::TaskContext&) { + if (chunk_first_ == chunk_last_) { + // Leaf case, recursed to single chunk. Do work on chunk: + ChunkDescriptor chunk = partitioner_[chunk_first_]; + RAI first = chunk.GetFirst(); + RAI last = chunk.GetLast(); + ReturnType result(neutral_); + for (RAI it = first; it != last; ++it) { + result = reduction_(result, transformation_(*it)); + } + result_ = result; + } else { + // Recurse further: + size_t chunk_split_index = (chunk_first_ + chunk_last_) / 2; + // Split chunks into left / right branches: + ReturnType result_l(neutral_); + ReturnType result_r(neutral_); + self_t functor_l(chunk_first_, + chunk_split_index, + neutral_, reduction_, transformation_, policy_, + partitioner_, + result_l); + self_t functor_r(chunk_split_index + 1, + chunk_last_, + neutral_, reduction_, transformation_, policy_, + partitioner_, + result_r); + embb::tasks::Task task_l = embb::tasks::Node::GetInstance().Spawn( + embb::tasks::Action( + base::MakeFunction( + functor_l, &self_t::Action), + policy_)); + embb::tasks::Task task_r = embb::tasks::Node::GetInstance().Spawn( + embb::tasks::Action( + base::MakeFunction( + functor_r, &self_t::Action), + policy_)); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + result_ = reduction_(result_l, result_r); + } + } + + private: + typedef ReduceFunctor self_t; + + private: + size_t chunk_first_; + size_t chunk_last_; + ReturnType neutral_; + ReductionFunction reduction_; + TransformationFunction transformation_; + const embb::tasks::ExecutionPolicy& policy_; + const BlockSizePartitioner& partitioner_; + ReturnType& result_; + + /** + * Disables assignment and copy-construction. + */ + ReduceFunctor& operator=(const ReduceFunctor&); + ReduceFunctor(const ReduceFunctor&); +}; + +template +ReturnType ReduceRecursive(RAI first, RAI last, ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size) { + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + if (distance == 0) { + return neutral; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for Reduce"); + } + unsigned int num_cores = policy.GetCoreCount(); + if (num_cores == 0) { + EMBB_THROW(embb::base::ErrorException, "No cores in execution policy"); + } + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + // Determine actually used block size + if (block_size == 0) { + block_size = (static_cast(distance) / num_cores); + if (block_size == 0) { + block_size = 1; + } + } + // Perform check of task number sufficiency + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Number of computation tasks required in reduction would " + "exceed MTAPI maximum number of tasks"); + } + typedef ReduceFunctor Functor; + BlockSizePartitioner partitioner(first, last, block_size); + ReturnType result = neutral; + Functor functor(0, + partitioner.Size() - 1, + neutral, + reduction, transformation, + policy, + partitioner, + result); + embb::tasks::Task task = node.Spawn( + embb::tasks::Action(base::MakeFunction( + functor, &Functor::Action), policy)); + task.Wait(MTAPI_INFINITE); + return result; +} + +template +ReturnType ReduceIteratorCheck(RAI first, RAI last, ReductionFunction reduction, + TransformationFunction transformation, + ReturnType neutral, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size, + std::random_access_iterator_tag) { + return ReduceRecursive(first, last, neutral, reduction, transformation, + policy, block_size); +} + +} // namespace internal + +template +ReturnType Reduce(RAI first, RAI last, ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size) { + typename std::iterator_traits::iterator_category category; + return internal::ReduceIteratorCheck(first, last, reduction, transformation, + neutral, policy, block_size, category); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_REDUCE_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h a/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h new file mode 100644 index 0000000..b81022e --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INTERNAL_SCAN_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_SCAN_INL_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace internal { + +template +class ScanFunctor { + public: + ScanFunctor(size_t chunk_first, size_t chunk_last, RAIOut output_iterator, + ReturnType neutral, ScanFunction scan, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + const BlockSizePartitioner& partitioner, + ReturnType* tree_values, size_t node_id, + bool going_down) + : policy_(policy), chunk_first_(chunk_first), chunk_last_(chunk_last), + output_iterator_(output_iterator), scan_(scan), + transformation_(transformation), + neutral_(neutral), partitioner_(partitioner), tree_values_(tree_values), + node_id_(node_id), parent_value_(neutral), is_first_pass_(going_down) { + } + + void Action(embb::tasks::TaskContext&) { + if (chunk_first_ == chunk_last_) { + ChunkDescriptor chunk = partitioner_[chunk_first_]; + RAIIn iter_in = chunk.GetFirst(); + RAIIn last_in = chunk.GetLast(); + RAIOut iter_out = output_iterator_; + // leaf case -> do work + if (is_first_pass_) { + ReturnType result = transformation_(*iter_in); + *iter_out = result; + ++iter_in; + ++iter_out; + for (; iter_in != last_in; ++iter_in, ++iter_out) { + result = scan_(result, transformation_(*iter_in)); + *iter_out = result; + } + SetTreeValue(result); + } else { + // Second pass + for (; iter_in != last_in; ++iter_in, ++iter_out) { + *iter_out = scan_(parent_value_, *iter_out); + } + } + } else { + // recurse further + size_t chunk_split_index = (chunk_first_ + chunk_last_) / 2; + // Split chunks into left / right branches: + ScanFunctor functor_l( + chunk_first_, chunk_split_index, + output_iterator_, neutral_, scan_, transformation_, + policy_, partitioner_, tree_values_, node_id_, + is_first_pass_); + ScanFunctor functor_r( + chunk_split_index + 1, chunk_last_, + output_iterator_, neutral_, scan_, transformation_, + policy_, partitioner_, tree_values_, node_id_, + is_first_pass_); + functor_l.SetID(LEFT); + functor_r.SetID(RIGHT); + // Advance output iterator of right branch: + ChunkDescriptor chunk_left = partitioner_[chunk_first_]; + ChunkDescriptor chunk_right = partitioner_[chunk_split_index + 1]; + std::advance(functor_r.output_iterator_, + std::distance(chunk_left.GetFirst(), chunk_right.GetFirst())); + if (!is_first_pass_) { + functor_l.parent_value_ = parent_value_; + functor_r.parent_value_ = functor_l.GetTreeValue() + parent_value_; + } + // Spawn tasks to recurse: + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + embb::tasks::Task task_l = node.Spawn( + embb::tasks::Action( + base::MakeFunction(functor_l, &ScanFunctor::Action), + policy_)); + embb::tasks::Task task_r = node.Spawn( + embb::tasks::Action( + base::MakeFunction(functor_r, &ScanFunctor::Action), + policy_)); + // Wait for tasks to complete: + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + SetTreeValue(scan_(functor_l.GetTreeValue(), functor_r.GetTreeValue())); + } + } + + ReturnType GetTreeValue() { + return tree_values_[node_id_]; + } + + void SetTreeValue(ReturnType value) { + tree_values_[node_id_] = value; + } + + private: + static const int LEFT = 1; + static const int RIGHT = 2; + const embb::tasks::ExecutionPolicy& policy_; + size_t chunk_first_; + size_t chunk_last_; + RAIOut output_iterator_; + ScanFunction scan_; + TransformationFunction transformation_; + ReturnType neutral_; + const BlockSizePartitioner& partitioner_; + ReturnType* tree_values_; + size_t node_id_; + ReturnType parent_value_; + bool is_first_pass_; + + void SetID(int branch) { + if (branch == LEFT) { + node_id_ = 2 * node_id_ + 1; + } else if (branch == RIGHT) { + node_id_ = 2 * node_id_ + 2; + } + } + + /** + * Disables assignment. + */ + ScanFunctor& operator=(const ScanFunctor&); + + /** + * Disables copying. + */ + ScanFunctor(const ScanFunctor&); +}; + +template +void ScanIteratorCheck(RAIIn first, RAIIn last, RAIOut output_iterator, + ReturnType neutral, ScanFunction scan, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size, + std::random_access_iterator_tag) { + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + if (distance == 0) { + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for Scan"); + } + unsigned int num_cores = policy.GetCoreCount(); + if (num_cores == 0) { + EMBB_THROW(embb::base::ErrorException, "No cores in execution policy"); + } + + ReturnType values[MTAPI_NODE_MAX_TASKS_DEFAULT]; + if (block_size == 0) { + block_size = static_cast(distance) / num_cores; + if (block_size == 0) { + block_size = 1; + } + } + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Not enough MTAPI tasks available for parallel scan"); + } + + // first pass. Calculates prefix sums for leaves and when recursion returns + // it creates the tree. + typedef ScanFunctor Functor; + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + + BlockSizePartitioner partitioner_down(first, last, block_size); + Functor functor_down(0, partitioner_down.Size() - 1, output_iterator, + neutral, scan, transformation, policy, partitioner_down, + values, 0, true); + embb::tasks::Task task_down = node.Spawn(embb::tasks::Action( + base::MakeFunction(functor_down, &Functor::Action), policy)); + task_down.Wait(MTAPI_INFINITE); + + // Second pass. Gives to each leaf the part of the prefix missing + BlockSizePartitioner partitioner_up(first, last, block_size); + Functor functor_up(0, partitioner_up.Size() - 1, output_iterator, + neutral, scan, transformation, policy, partitioner_up, + values, 0, false); + embb::tasks::Task task_up = node.Spawn(embb::tasks::Action( + base::MakeFunction(functor_up, &Functor::Action), policy)); + task_up.Wait(MTAPI_INFINITE); +} + +} // namespace internal + +template +void Scan(RAIIn first, RAIIn last, RAIOut output_iterator, ReturnType neutral, + ScanFunction scan, TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::iterator_category category; + internal::ScanIteratorCheck(first, last, output_iterator, neutral, + scan, transformation, policy, block_size, category()); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_SCAN_INL_H_ diff --git b/algorithms_cpp/include/embb/algorithms/invoke.h a/algorithms_cpp/include/embb/algorithms/invoke.h new file mode 100644 index 0000000..ced924f --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/invoke.h @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_INVOKE_H_ +#define EMBB_ALGORITHMS_INVOKE_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_INVOKE Invoke + * Parallel invocation of functions. + * \ingroup CPP_ALGORITHMS + */ + + +/** + * Function type used by Invoke. + * \ingroup CPP_ALGORITHMS_INVOKE + */ +typedef embb::base::Function InvokeFunctionType; + +#ifdef DOXYGEN + +/** + * Spawns two to ten function objects at once and runs them in parallel. + * + * Blocks until all of them are done. + * + * \ingroup CPP_ALGORITHMS_INVOKE + */ +template +void Invoke( + Function1 func1, + /**< [in] First function object to invoke */ + Function2 func2, + /**< [in] Second function object to invoke */ + ...); + +/** +* Spawns two to ten function objects at once and runs them in parallel using the +* given embb::mtapi::ExecutionPolicy. +* +* Blocks until all of them are done. +* +* \ingroup CPP_ALGORITHMS_INVOKE +*/ +template +void Invoke( + Function1 func1, + /**< [in] Function object to invoke */ + Function2 func2, + /**< [in] Second function object to invoke */ + ..., + const embb::tasks::ExecutionPolicy & policy + /**< [in] embb::tasks::ExecutionPolicy to use */ + ); + +#else // DOXYGEN + +namespace internal { + +/** + * Spawns an MTAPI task on construction and waits for it on destruction. + */ +template +class TaskWrapper { + public: + /** + * Wraps the function into an embb::tasks::Action and spawns an + * embb::tasks::Task. + */ + explicit TaskWrapper( + Function function, + const embb::tasks::ExecutionPolicy& policy) + : function_(function), task_() { + embb::tasks::Action action(embb::base::MakeFunction( + *this, &TaskWrapper::Run), policy); + task_ = embb::tasks::Node::GetInstance().Spawn(action); + } + + /** + * Waits until the task has finished execution. + */ + ~TaskWrapper() { + task_.Wait(MTAPI_INFINITE); + } + + private: + Function function_; + embb::tasks::Task task_; + + void Run(embb::tasks::TaskContext&) { + function_(); + } +}; +} // namespace internal + +template +void Invoke( + Function1 func1, + Function2 func2, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); + internal::TaskWrapper wrap8(func8, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); + internal::TaskWrapper wrap8(func8, policy); + internal::TaskWrapper wrap9(func9, policy); +} + +template + void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9, + Function10 func10, + const embb::tasks::ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); + internal::TaskWrapper wrap8(func8, policy); + internal::TaskWrapper wrap9(func9, policy); + internal::TaskWrapper wrap10(func10, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2) { + Invoke(func1, func2, embb::tasks::ExecutionPolicy()); +} +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3) { + Invoke(func1, func2, func3, embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4) { + Invoke(func1, func2, func3, func4, embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5) { + Invoke(func1, func2, func3, func4, func5, embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6) { + Invoke(func1, func2, func3, func4, func5, func6, + embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7) { + Invoke(func1, func2, func3, func4, func5, func6, func7, + embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8) { + Invoke(func1, func2, func3, func4, func5, func6, func7, func8, + embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9) { + Invoke(func1, func2, func3, func4, func5, func6, func7, func8, func9, + embb::tasks::ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9, + Function10 func10) { + Invoke(func1, func2, func3, func4, func5, func6, func7, func8, func9, func10, + embb::tasks::ExecutionPolicy()); +} + +#endif // else DOXYGEN + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INVOKE_H_ diff --git b/algorithms_cpp/include/embb/algorithms/merge_sort.h a/algorithms_cpp/include/embb/algorithms/merge_sort.h new file mode 100644 index 0000000..a09cc90 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/merge_sort.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_MERGE_SORT_H_ +#define EMBB_ALGORITHMS_MERGE_SORT_H_ + +#include +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_SORTING Sorting + * Parallel merge sort and quick sort algorithms + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Sorts a range of elements using a parallel merge sort algorithm with implicit + * allocation of dynamic memory. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. Since the algorithm does not sort in-place, it requires + * additional memory which is implicitly allocated by the function. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \memory Array with last-first elements of type + * std::iterator_traits::value_type. + * \threadsafe if the elements in the range [first,last) are not + * modified by another thread while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + operations. + * \see embb::mtapi::ExecutionPolicy, MergeSort() + * \tparam RAI Random access iterator + * \tparam ComparisonFunction Binary predicate with both arguments of type + * std::iterator_traits::value_type. + */ +template +void MergeSortAllocate( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison + = std::less::value_type>(), + /**< [IN] Binary predicate used to establish the sorting order. An element + \c a appears before an element \c b in the sorted range if + comparison(a, b) == true. The default value uses the + less-than relation. */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the merge sort algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +/** + * Sorts a range of elements using a parallel merge sort algorithm without + * implicit allocation of dynamic memory. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. Since the algorithm does not sort in-place, it requires + * additional memory which must be provided by the user. The range pointed to + * by \c temporary_first must have the same number of elements as the range to + * be sorted, and the elements of both ranges must have the same type. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the ranges [first,last) and + * [temporary_first,temporary_first+(last-first) are not + * modified by another thread while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * operations. + * \see embb::mtapi::ExecutionPolicy, MergeSortAllocate() + * \tparam RAI Random access iterator + * \tparam RAITemp Random access iterator for temporary memory. Has to have the + * same value type as RAI. + * \tparam ComparisonFunction Binary predicate with both arguments of type + * std::iterator_traits::value_type. + */ +template +void MergeSort( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator to last plus one element to be sorted */ + RAITemp temporary_first, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison + = std::less::value_type>(), + /**< [IN] Binary predicate used to establish the sorting order. An element + \c a appears before an element \c b in the sorted range if + comparison(a, b) == true. The default value uses the + less-than relation. */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the merge sort algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy. + */ +template +void MergeSortAllocate( + RAI first, + RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ) { + typedef base::Allocation Alloc; + typename std::iterator_traits::difference_type distance = last - first; + typedef typename std::iterator_traits::value_type value_type; + if (distance == 0) { + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for MergeSort"); + } + value_type* temporary = static_cast( + Alloc::Allocate(distance * sizeof(value_type))); + + EMBB_TRY { + MergeSort(first, last, temporary, comparison, policy, block_size); + } EMBB_CATCH (embb::base::ErrorException & e) { + // embb exception handling does not support catch(...) and rethrow yet. + Alloc::Free(temporary); + + // Rethrow only, if exceptions are enabled... Otherwise, the parameter + // e cannot be used, as it is not defined. +#ifdef EMBB_USE_EXCEPTIONS + EMBB_THROW(embb::base::ErrorException, e.what()); +#endif + } + Alloc::Free(temporary); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSortAllocate( + RAI first, + RAI last + ) { + MergeSortAllocate(first, last, + std::less::value_type>(), + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSortAllocate( + RAI first, + RAI last, + ComparisonFunction comparison + ) { + MergeSortAllocate(first, last, comparison, embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSortAllocate( + RAI first, + RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy + ) { + MergeSortAllocate(first, last, comparison, policy, 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first + ) { + MergeSort(first, last, temporary_first, + std::less::value_type>(), + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison + ) { + MergeSort(first, last, temporary_first, comparison, + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy + ) { + MergeSort(first, last, temporary_first, comparison, policy, 0); +} + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_MERGE_SORT_H_ diff --git b/algorithms_cpp/include/embb/algorithms/quick_sort.h a/algorithms_cpp/include/embb/algorithms/quick_sort.h new file mode 100644 index 0000000..adefe09 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/quick_sort.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_QUICK_SORT_H_ +#define EMBB_ALGORITHMS_QUICK_SORT_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \ingroup CPP_ALGORITHMS_SORTING + * \{ + */ + +#ifdef DOXYGEN + +/** + * Sorts a range of elements using a parallel quick sort algorithm. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. The algorithm sorts in-place and requires no additional memory. + * It has, however, a worst-case time complexity of + * O((last-first)2). + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range [first,last) are not + * modified by another thread while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * operations. + * \see embb::mtapi::ExecutionPolicy, MergeSort() + * \tparam RAI Random access iterator + * \tparam ComparisonFunction Binary predicate with both arguments of type + * std::iterator_traits::value_type. + */ +template +void QuickSort( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison + = std::less::value_type>(), + /**< [IN] Binary predicate used to establish the sorting order. An element + \c a appears before an element \c b in the sorted range if + comparison(a, b) == true. The default value uses the + less-than relation. */ + const embb::tasks::ExecutionPolicy& policy = embb::tasks::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the quick sort algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. Note that quick sort does not guarantee a + partitioning into evenly sized blocks, as the partitions depend on + the values to be sorted. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy. + */ +template +void QuickSort( + RAI first, + RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void QuickSort( + RAI first, + RAI last + ) { + QuickSort(first, last, + std::less::value_type>(), + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void QuickSort( + RAI first, + RAI last, + ComparisonFunction comparison + ) { + QuickSort(first, last, comparison, embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void QuickSort( + RAI first, + RAI last, + ComparisonFunction comparison, + const embb::tasks::ExecutionPolicy& policy + ) { + QuickSort(first, last, comparison, policy, 0); +} + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb +#include + +#endif // EMBB_ALGORITHMS_QUICK_SORT_H_ diff --git b/algorithms_cpp/include/embb/algorithms/reduce.h a/algorithms_cpp/include/embb/algorithms/reduce.h new file mode 100644 index 0000000..1cf5d86 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/reduce.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_REDUCE_H_ +#define EMBB_ALGORITHMS_REDUCE_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_REDUCTION Reduction + * Parallel reduction computation + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Performs a parallel reduction operation on a range of elements. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. The type of the result (\c ReturnType) is deduced from the + * \c neutral element. + * + * \return + * reduction(transformation(*first), ..., transformation(*(last-1))) + * where the reduction function is applied pairwise. + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the order in which the functions \c reduction + * and \c transformation are applied to the elements.\n + * For all \c x of type \c ReturnType it must hold that + * reduction(x, neutral) == x. \n + * The reduction operation need not be commutative but must be + * associative, i.e., reduction(x, reduction(y, z)) == + * reduction(reduction(x, y), z)) for all \c x, \c y, \c z of type + * \c ReturnType. + * \see embb::mtapi::ExecutionPolicy, ZipIterator, Identity + * \tparam RAI Random access iterator + * \tparam ReturnType Type of result of reduction operation, deduced from + * \c neutral + * \tparam ReductionFunction Binary reduction function object with signature + * ReturnType ReductionFunction(ReturnType, ReturnType). + * \tparam TransformationFunction Unary transformation function object with + * signature ReturnType TransformationFunction(typename + * std::iterator_traits::value_type) + */ +template +ReturnType Reduce( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ReturnType neutral, + /**< [IN] Neutral element of the reduction operation. */ + ReductionFunction reduction, + /**< [IN] Reduction operation to be applied to the elements of the range */ + TransformationFunction transformation = Identity(), + /**< [IN] Transforms the elements of the range before the reduction operation + is applied */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the reduction computation */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are treated in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction + ) { + return Reduce(first, last, neutral, reduction, Identity(), + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation + ) { + return Reduce(first, last, neutral, reduction, transformation, + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy + ) { + return Reduce(first, last, neutral, reduction, transformation, policy, 0); +} + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_REDUCE_H_ diff --git b/algorithms_cpp/include/embb/algorithms/scan.h a/algorithms_cpp/include/embb/algorithms/scan.h new file mode 100644 index 0000000..11d1315 --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/scan.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_SCAN_H_ +#define EMBB_ALGORITHMS_SCAN_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_SCAN Scan + * Parallel scan computation + * \ingroup CPP_ALGORITHMS + * + * \{ + */ + +#ifdef DOXYGEN + +/** + * Performs a parallel scan (or prefix) computation on a range of elements. + * + * The algorithm reads an input range and writes its result to a separate output + * range. The input range consists of the elements from \c first to \c last, + * excluding the last element. The output range consists of the elements from + * \c output_first to output_first + std::difference(last - first). + * + * The algorithm performs two runs on the given range. Hence, a performance + * speedup can only be expected on processors with more than two cores. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the order in which the functions \c scan + * and \c transformation are applied to the elements.\n + * For all \c x of type \c ReturnType it must hold that + * reduction(x, neutral) == x. \n + * The reduction operation need not be commutative but must be + * associative, i.e., reduction(x, reduction(y, z)) == + * reduction(reduction(x, y), z)) for all \c x, \c y, \c z of type + * \c ReturnType. + * \see embb::mtapi::ExecutionPolicy, Identity, ZipIterator + * \tparam RAIIn Random access iterator type of input range + * \tparam RAIOut Random access iterator type of output range + * \tparam ReturnType Type of output elements of scan operation, deduced from + * \c neutral + * \tparam ScanFunction Binary scan function object with signature + * ReturnType ScanFunction(ReturnType, ReturnType) + * \tparam TransformationFunction Unary transformation function object with + * signature ReturnType TransformationFunction(typename + * std::iterator_traits::value_type). + */ +template +void Scan( + RAIIn first, + /**< [IN] Random access iterator pointing to the first element of the input + range */ + RAIIn last, + /**< [IN] Random access iterator pointing to the last plus one element of the + input range */ + RAIOut output_first, + /**< [IN] Random access iterator pointing to the first element of the output + range */ + ReturnType neutral, + /**< [IN] Neutral element of the \c scan operation. */ + ScanFunction scan, + /**< [IN] Scan operation to be applied to the elements of the input range */ + TransformationFunction transformation = Identity(), + /**< [IN] Transforms the elements of the input range before the scan operation + is applied */ + const embb::mtapi::ExecutionPolicy& policy = embb::mtapi::ExecutionPolicy(), + /**< [IN] embb::mtapi::ExecutionPolicy for the scan computation */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are treated in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan + ) { + Scan(first, last, output_iterator, neutral, scan, Identity(), + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan, + TransformationFunction transformation + ) { + Scan(first, last, output_iterator, neutral, scan, transformation, + embb::tasks::ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan, + TransformationFunction transformation, + const embb::tasks::ExecutionPolicy& policy + ) { + Scan(first, last, output_iterator, neutral, scan, transformation, policy, 0); +} + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_SCAN_H_ diff --git b/algorithms_cpp/include/embb/algorithms/zip_iterator.h a/algorithms_cpp/include/embb/algorithms/zip_iterator.h new file mode 100644 index 0000000..0de6ebf --- /dev/null +++ a/algorithms_cpp/include/embb/algorithms/zip_iterator.h @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_ZIP_ITERATOR_H_ +#define EMBB_ALGORITHMS_ZIP_ITERATOR_H_ + +#include +#include +#include + +namespace embb { +namespace algorithms { +/** + * \defgroup CPP_ALGORITHMS_ZIP_ITERATOR Zip Iterator + * Zip two iterators + * \ingroup CPP_ALGORITHMS + * \{ + */ + +/** + * Container for the values of two dereferenced iterators. + * The values contained are of type + * std::iterator_traits::reference. + * \notthreadsafe + * \tparam TypeA Type of the first value + * \tparam TypeB Type of the first value + * + */ +template +class ZipPair { + public: + /** + * Constructs a pair from two values. + */ + ZipPair( + TypeA first, + /**< [IN] First value*/ + TypeB second + /**< [IN] Second value*/ + ) + :first_(first), second_(second) {} + + /** + * Copies a pair. + */ + ZipPair( + const ZipPair& other + /**< [IN] pair to copy */ + ) + :first_(other.first_), second_(other.second_) {} + + /** + * Returns the first value of the pair. + * + * \return The first value of the pair. + */ + TypeA First() { + return first_; + } + + /** + * Returns the second value of the pair. + * + * \return The second value of the pair + */ + TypeB Second() { + return second_; + } + + /** + * Returns the first value of the pair. + * + * \return The first value of the pair. + */ + const TypeA First() const { + return first_; + } + + /** + * Returns the second value of the pair. + * + * \return The second value of the pair + */ + const TypeB Second() const { + return second_; + } + + private: + TypeA first_; + TypeB second_; + + /** + * Disable assignment since references cannot be assigned. + */ + ZipPair &operator=(const ZipPair&); +}; + +/** + * Zip container for two iterators. + * This container allows zipping two iterators together, thus enabling them + * to be used in situations where only one iterator can be used. Any operation + * applied to the zip iterator will subsequently be applied to the contained + * iterators. Dereferencing the iterator will return a ZipPair containing the + * values referenced by the iterators. + * \notthreadsafe + * \note It is required that the two iterators have the same \c difference_type + * or that at least the first iterator's \c difference_type can be implicitly + * converted to the second iterator's \c difference_type. Moreover, when + * calculating the distance between two ZipIterators, the distances between both + * pairs of iterators are equal. + * \see ZipPair + * \tparam IteratorA First iterator + * \tparam IteratorB Second iterator + */ +template +class ZipIterator{ + public: + /** + * \name Iterator Typedefs + * Necessary typedefs for iterators (std compatibility). + * \{ + */ + typedef std::random_access_iterator_tag iterator_category; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef typename std::iterator_traits::reference RefA; + typedef typename std::iterator_traits::reference RefB; + typedef typename std::iterator_traits::value_type ValueA; + typedef typename std::iterator_traits::value_type ValueB; + typedef ZipPair value_type; + typedef ZipPair reference; + typedef ZipPair pointer; + /** + * \} + */ + + /** + * Constructs a zip iterator from two iterators of any type. + */ + ZipIterator( + IteratorA iter_a, + /**< [IN] First iterator*/ + IteratorB iter_b + /**< [IN] Second iterator*/ + ) + :iter_a_(iter_a), iter_b_(iter_b) {} + + /** + * Compares two zip iterators for equality. + * + * \return \c true if zip iterators are equal, otherwise \c false + */ + bool operator==( + const ZipIterator &other + /**< [IN] Reference to right-hand side of equality operator */ + ) const { + return iter_a_ == other.iter_a_ && iter_b_ == other.iter_b_; + } + + /** + * Compares two zip iterators for inequality. + * + * \return \c true if any iterator doesn't equal the other, otherwise \c false + */ + bool operator!=( + const ZipIterator &other + /**< [IN] Reference to right-hand side of inequality operator */ + ) const { + return iter_a_ != other.iter_a_ || iter_b_ != other.iter_b_; + } + + /** + * Applies prefix increment on both iterators. + */ + void operator++() { + ++iter_a_; + ++iter_b_; + } + + /** + * Applies prefix decrement on both iterators. + */ + void operator--() { + --iter_a_; + --iter_b_; + } + + /** + * Returns an instance of a zip iterator where both iterators have been + * advanced by the specified distance. + * + * \return New zip iterator containing the advanced iterators + */ + ZipIterator operator+( + difference_type distance + /**< [IN] Number of elements to advance the iterators */ + ) const { + ZipIterator new_iterator(*this); + new_iterator.iter_a_ += distance; + new_iterator.iter_b_ += distance; + + return new_iterator; + } + + /** + * Returns an instance of a zip iterator where both iterators have been + * regressed by the specified distance. + * + * \return New zip iterator containing the regressed iterators + */ + ZipIterator operator-( + difference_type distance + /**< [IN] Number of elements to regress the iterators */ + ) const { + ZipIterator new_iterator(*this); + new_iterator.iter_a_ -= distance; + new_iterator.iter_b_ -= distance; + + return new_iterator; + } + + /** + * Advances both iterators by the specified distance. + * + * \return Reference to \c *this + */ + ZipIterator& operator+=( + difference_type distance + /**< [IN] Number of elements to advance the iterators */ + ) { + iter_a_ += distance; + iter_b_ += distance; + return *this; + } + + /** + * Regresses both iterators by the specified distance. + * + * \return Reference to \c *this + */ + ZipIterator& operator-=( + difference_type distance + /**< [IN] Number of elements to regress the iterators */ + ) { + iter_a_ -= distance; + iter_b_ -= distance; + return *this; + } + + /** + * Computes the distance between two zip iterators. + * It is assumed that both iterator pairs have the same distance. + * + * \return The distance between the zip iterators + */ + difference_type operator-( + const ZipIterator &other + /**< [IN] Reference to right-hand side of subtraction operator */ + ) const { + assert(iter_a_ - other.iter_a_ == iter_b_ - other.iter_b_); + return iter_a_ - other.iter_a_; + } + + /** + * Dereferences the zip iterator. + * + * \return ZipPair containing the dereferenced values. + */ + reference operator*() const { + return ZipPair(*iter_a_, *iter_b_); + } + + private: + IteratorA iter_a_; + IteratorB iter_b_; +}; + +/** + * Creates a zip iterator from two iterators. + * This is a convenience function which avoids calling the constructor of the + * templated class. + * + * \return Constructed zip iterator + * \tparam IteratorA Type of first iterator + * \tparam IteratorB Type of second iterator + */ +template +ZipIterator Zip( + IteratorA iter_a, + /**< [IN] First iterator */ + IteratorB iter_b + /**< [IN] Second iterator */ + ) { + return ZipIterator(iter_a, iter_b); +} + +/** + * \} + */ +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_ZIP_ITERATOR_H_ diff --git b/algorithms_cpp/src/dummy.cc a/algorithms_cpp/src/dummy.cc new file mode 100644 index 0000000..f2ce3d4 --- /dev/null +++ a/algorithms_cpp/src/dummy.cc @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void dummy_so_that_cmake_can_determinie_linker_language_for_algorithms() { +} diff --git b/algorithms_cpp/test/count_test.cc a/algorithms_cpp/test/count_test.cc new file mode 100644 index 0000000..6678383 --- /dev/null +++ a/algorithms_cpp/test/count_test.cc @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +struct IsEven{ + bool operator()(int val) const { + if (val % 2 == 0) + return true; + else + return false; + } +}; + +bool IsEvenFunction(int val) { + if (val % 2 == 0) + return true; + else + return false; +} + +CountTest::CountTest() { + CreateUnit("Different data structures") + .Add(&CountTest::TestDataStructures, this); + CreateUnit("CountIf").Add(&CountTest::TestCountIf, this); + CreateUnit("Ranges").Add(&CountTest::TestRanges, this); + CreateUnit("Block sizes").Add(&CountTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&CountTest::TestPolicy, this); + CreateUnit("Stress test").Add(&CountTest::StressTest, this); +} + +void CountTest::TestDataStructures() { + using embb::algorithms::Count; + const int size = 10; + int array[] = { 10, 20, 30, 30, 20, 10, 10, 20, 20, 20 }; + std::vector vector(array, array + size); + std::deque deque(array, array + size); + const std::vector const_vector(array, array + size); + + PT_EXPECT_EQ(Count(array, array + size, 10), 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10), 3); + PT_EXPECT_EQ(Count(deque.begin(), deque.end(), 10), 3); + PT_EXPECT_EQ(Count(const_vector.begin(), const_vector.end(), 10), 3); +} + +void CountTest::TestCountIf() { + using embb::algorithms::CountIf; + const int size = 10; + int array[] = { 10, 21, 30, 31, 20, 11, 10, 21, 20, 20 }; + PT_EXPECT_EQ(CountIf(array, array + size, IsEven()), 6); + PT_EXPECT_EQ(CountIf(array, array + size, &IsEvenFunction), 6); +} + +void CountTest::TestRanges() { + using embb::algorithms::Count; + size_t count = 4; + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + vector[i] = static_cast(-1); + } + + // Ommit first element + PT_EXPECT_EQ(Count(vector.begin() + 1, vector.end(), -1), 3); + + // Ommit last element + PT_EXPECT_EQ(Count(vector.begin(), vector.end() - 1, -1), 3); + + // Ommit first and last element + PT_EXPECT_EQ(Count(vector.begin() + 1, vector.end() - 1, -1), 2); + + // Only do first element + PT_EXPECT_EQ(Count(vector.begin(), vector.begin() + 1, -1), 1); + + // Only do last element + PT_EXPECT_EQ(Count(vector.end() - 1, vector.end(), -1), 1); + + // Only do second element + PT_EXPECT_EQ(Count(vector.begin() + 1, vector.begin() + 2, -1), 1); +} + +void CountTest::TestBlockSizes() { + using embb::algorithms::Count; + size_t count = 4; + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + vector[i] = -1; + } + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), -1), + static_cast(count)); + } +} + +void CountTest::TestPolicy() { + using embb::algorithms::Count; + using embb::tasks::ExecutionPolicy; + int a[] = { 10, 20, 30, 30, 20, 10, 10, 20, 20, 20 }; + std::vector vector(a, a + (sizeof a / sizeof a[0])); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, ExecutionPolicy()), 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, ExecutionPolicy(true)), + 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, + ExecutionPolicy(true, 1)), 3); +} + +void CountTest::StressTest() { + using embb::algorithms::Count; + size_t count = embb::tasks::Node::GetInstance().GetCoreCount() * 10; + std::vector large_vector(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast(0); + } + PT_EXPECT_EQ(Count(large_vector.begin(), large_vector.end(), 0), + static_cast(count)); +} diff --git b/algorithms_cpp/test/count_test.h a/algorithms_cpp/test/count_test.h new file mode 100644 index 0000000..55862c7 --- /dev/null +++ a/algorithms_cpp/test/count_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_COUNT_TEST_H_ +#define ALGORITHMS_CPP_TEST_COUNT_TEST_H_ + +#include + +/** + * Provides tests for the Reduce method. + */ +class CountTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + CountTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the count if functionality. + */ + void TestCountIf(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_COUNT_TEST_H_ diff --git b/algorithms_cpp/test/for_each_test.cc a/algorithms_cpp/test/for_each_test.cc new file mode 100644 index 0000000..b42179e --- /dev/null +++ a/algorithms_cpp/test/for_each_test.cc @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +/** + * Functor to compute the square of a number. + * + * The result overwrites the original number. + */ +struct Square { + template + void operator()(Type& l) const { + l = l * l; + } +}; + +/** + * Free function to compute the square of a number. + * + * The result overwrites the original number. + */ +static void SquareFunction(int &val) { + val = val * val; +} + +ForEachTest::ForEachTest() { + CreateUnit("Different data structures") + .Add(&ForEachTest::TestDataStructures, this); + CreateUnit("Function Pointers").Add(&ForEachTest::TestFunctionPointers, this); + CreateUnit("Ranges").Add(&ForEachTest::TestRanges, this); + CreateUnit("Block sizes").Add(&ForEachTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&ForEachTest::TestPolicy, this); + CreateUnit("Stress test").Add(&ForEachTest::StressTest, this); +} + +void ForEachTest::TestDataStructures() { + using embb::algorithms::ForEach; + + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + + ForEach(array, array + kCountSize, Square()); + ForEach(vector.begin(), vector.end(), Square()); + ForEach(deque.begin(), deque.end(), Square()); + + for (size_t i = 0; i < kCountSize; i++) { + int expected = static_cast(i+2); + expected = expected * expected; + PT_EXPECT_EQ(expected, array[i]); + PT_EXPECT_EQ(expected, vector[i]); + PT_EXPECT_EQ(expected, deque[i]); + } +} + +void ForEachTest::TestFunctionPointers() { + using embb::algorithms::ForEach; + + std::vector vector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + } + ForEach(vector.begin(), vector.end(), &SquareFunction); + for (size_t i = 0; i < kCountSize; i++) { + int expected = static_cast(i+2); + expected = expected * expected; + PT_EXPECT_EQ(expected, vector[i]); + } +} + +void ForEachTest::TestRanges() { + using embb::algorithms::ForEach; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + // Ommit first element + vector = init; + ForEach(vector.begin() + 1, vector.end(), Square()); + PT_EXPECT_EQ(vector[0], init[0]); + for (size_t i = 1; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + // Ommit last element + vector = init; + ForEach(vector.begin(), vector.end() - 1, Square()); + for (size_t i = 0; i < count - 1; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + PT_EXPECT_EQ(vector[count - 1], init[count - 1]); + + // Ommit first and last element + vector = init; + ForEach(vector.begin() + 1, vector.end() - 1, Square()); + PT_EXPECT_EQ(vector[0], init[0]); + for (size_t i = 1; i < count - 1; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + PT_EXPECT_EQ(vector[count - 1], init[count - 1]); + + // Only do first element + vector = init; + ForEach(vector.begin(), vector.begin() + 1, Square()); + PT_EXPECT_EQ(vector[0], init[0] * init[0]); + for (size_t i = 1; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]); + } + + // Only do last element + vector = init; + ForEach(vector.end() - 1, vector.end(), Square()); + for (size_t i = 0; i < count - 1; i++) { + PT_EXPECT_EQ(vector[i], init[i]); + } + PT_EXPECT_EQ(vector[count - 1], init[count - 1] * init[count - 1]); + + // Only do second element + vector = init; + ForEach(vector.begin() + 1, vector.begin() + 2, Square()); + for (size_t i = 1; i < count; i++) { + if (i != 1) { + PT_EXPECT_EQ(vector[i], init[i]); + } else { + PT_EXPECT_EQ(vector[i], init[i] * init[i]); + } + } +} + +void ForEachTest::TestBlockSizes() { + using embb::algorithms::ForEach; + using embb::tasks::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(), + block_size); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + } +} + +void ForEachTest::TestPolicy() { + using embb::algorithms::ForEach; + using embb::tasks::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(true)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(true, 1)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + // ForEach on empty list should not throw: + ForEach(vector.begin(), vector.begin(), Square()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + ForEach(second, vector.begin(), Square()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif +} + +void ForEachTest::StressTest() { + using embb::algorithms::ForEach; + using embb::tasks::ExecutionPolicy; + size_t count = embb::tasks::Node::GetInstance().GetCoreCount() * 10; + std::vector large_vector(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast((i + 2) % 1000); + } + ForEach(large_vector.begin(), large_vector.end(), Square(), ExecutionPolicy(), + 2000); + for (size_t i = 0; i < count; i++) { + int expected = static_cast((i + 2) % 1000); + expected = expected * expected; + PT_EXPECT_EQ(large_vector[i], expected); + } +} diff --git b/algorithms_cpp/test/for_each_test.h a/algorithms_cpp/test/for_each_test.h new file mode 100644 index 0000000..efe7875 --- /dev/null +++ a/algorithms_cpp/test/for_each_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_FOR_EACH_TEST_H_ +#define ALGORITHMS_CPP_TEST_FOR_EACH_TEST_H_ + +#include + +/** + * Provides tests for the ForEach method. + */ +class ForEachTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + ForEachTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_FOR_EACH_TEST_H_ diff --git b/algorithms_cpp/test/invoke_test.cc a/algorithms_cpp/test/invoke_test.cc new file mode 100644 index 0000000..2776ed3 --- /dev/null +++ a/algorithms_cpp/test/invoke_test.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +InvokeTest::InvokeTest() { + CreateUnit("Preliminary").Add(&InvokeTest::Test, this); +} + +static void Invocable1() {} +static void Invocable2() {} +static void Invocable3() {} +static void Invocable4() {} +static void Invocable5() {} +static void Invocable6() {} +static void Invocable7() {} +static void Invocable8() {} +static void Invocable9() {} +static void Invocable10() {} + +void InvokeTest::Test() { + using embb::algorithms::Invoke; + Invoke(&Invocable1, &Invocable2); + Invoke(&Invocable1, &Invocable2, &Invocable3); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10); + + embb::tasks::ExecutionPolicy policy; + Invoke(&Invocable1, &Invocable2, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9, policy); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10, + policy); +} diff --git b/algorithms_cpp/test/invoke_test.h a/algorithms_cpp/test/invoke_test.h new file mode 100644 index 0000000..4679b26 --- /dev/null +++ a/algorithms_cpp/test/invoke_test.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_INVOKE_TEST_H_ +#define ALGORITHMS_CPP_TEST_INVOKE_TEST_H_ + +#include + +/** + * Provides tests for the Invoke method. + */ +class InvokeTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + InvokeTest(); + + private: + /** + * Tests ... + */ + void Test(); +}; + +#endif // ALGORITHMS_CPP_TEST_INVOKE_TEST_H_ diff --git b/algorithms_cpp/test/main.cc a/algorithms_cpp/test/main.cc new file mode 100644 index 0000000..5ae28d5 --- /dev/null +++ a/algorithms_cpp/test/main.cc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +int compute2_(int a) { + return a * 2; +} + +int compute1_() { + return 5; +} + +::std::string float_to_string(float val) { + std::stringstream s; + s << "Float: " << val; + + return std::string(s.str()); +} + +PT_MAIN("Algorithms") { + embb::tasks::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + PT_RUN(PartitionerTest); + PT_RUN(ForEachTest); + PT_RUN(ReduceTest); + PT_RUN(ScanTest); + PT_RUN(CountTest); + PT_RUN(ZipIteratorTest); + PT_RUN(QuickSortTest); + PT_RUN(MergeSortTest); + PT_RUN(InvokeTest); + + embb::tasks::Node::Finalize(); + + PT_EXPECT(embb_get_bytes_allocated() == 0); + + // std::cout << "please press return to continue..." << std::endl; + // std::cin.get(); +} diff --git b/algorithms_cpp/test/merge_sort_test.cc a/algorithms_cpp/test/merge_sort_test.cc new file mode 100644 index 0000000..43c9dc1 --- /dev/null +++ a/algorithms_cpp/test/merge_sort_test.cc @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static bool DescendingComparisonFunction(double lhs, double rhs) { + return lhs < rhs ? true : false; +} + +MergeSortTest::MergeSortTest() { + CreateUnit("Different data structures") + .Add(&MergeSortTest::TestDataStructures, this); + CreateUnit("Function Pointers").Add(&MergeSortTest::TestFunctionPointers, + this); + CreateUnit("Ranges").Add(&MergeSortTest::TestRanges, this); + //CreateUnit("Block sizes").Add(&MergeSortTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&MergeSortTest::TestPolicy, this); + CreateUnit("Stress test").Add(&MergeSortTest::StressTest, this); +} + +void MergeSortTest::TestDataStructures() { + using embb::algorithms::MergeSortAllocate; + using embb::tasks::ExecutionPolicy; + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end()); + MergeSortAllocate(array, array + kCountSize); + MergeSortAllocate(vector.begin(), vector.end()); + MergeSortAllocate(array, array + kCountSize, std::less(), + ExecutionPolicy(), 2); + MergeSortAllocate(deque.begin(), deque.end()); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], array[i]); + PT_EXPECT_EQ(vector_copy[i], vector[i]); + PT_EXPECT_EQ(vector_copy[i], deque[i]); + } +} + +void MergeSortTest::TestFunctionPointers() { + using embb::algorithms::MergeSortAllocate; + using embb::tasks::ExecutionPolicy; + + std::vector vector(kCountSize); + for (size_t i = kCountSize - 1; i > 0; i--) { + vector[i] = static_cast(i + 2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end(), + &DescendingComparisonFunction); + MergeSortAllocate(vector.begin(), vector.end(), + &DescendingComparisonFunction); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void MergeSortTest::TestRanges() { + using embb::algorithms::MergeSortAllocate; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = count - 1; i > 0; i--) { + init[i] = static_cast(i+2); + } + + // Ommit first element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end()); + MergeSortAllocate(vector.begin() + 1, vector.end()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end() - 1); + MergeSortAllocate(vector.begin(), vector.end() - 1); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit first and last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end() - 1); + MergeSortAllocate(vector.begin() + 1, vector.end() - 1); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do first two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.begin() + 2); + MergeSortAllocate(vector.begin(), vector.begin() + 2); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do last two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.end() - 2, vector_copy.end()); + MergeSortAllocate(vector.end() - 2, vector.end()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do second & third elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.begin() + 3); + MergeSortAllocate(vector.begin() + 1, vector.begin() + 3); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void MergeSortTest::TestBlockSizes() { + using embb::algorithms::MergeSortAllocate; + using embb::tasks::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = count - 1; i > 0; i--) { + init[i] = static_cast(i+2); + } + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end()); + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + vector = init; + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(), block_size); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], vector_copy[i]); + } + } +} + +void MergeSortTest::TestPolicy() { + using embb::algorithms::MergeSortAllocate; + using embb::tasks::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = count - 1; i > 0; i--) { + init[i] = static_cast(i+2); + } + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end()); + + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(true)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(true, 1)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // MergeSort on empty list should not throw: + MergeSortAllocate(vector.begin(), vector.begin(), std::less()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + MergeSortAllocate(second, vector.begin(), std::less()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif +} + +void MergeSortTest::StressTest() { + using embb::algorithms::MergeSortAllocate; + size_t count = embb::tasks::Node::GetInstance().GetCoreCount() * 10; + std::vector large_vector(count); + std::vector vector_copy(count); + for (size_t i = count - 1; i > 0; i--) { + large_vector[i] = static_cast((i + 2) % 1000); + } + vector_copy = large_vector; + std::sort(vector_copy.begin(), vector_copy.end()); + MergeSortAllocate(large_vector.begin(), large_vector.end()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(large_vector[i], vector_copy[i]); + } +} diff --git b/algorithms_cpp/test/merge_sort_test.h a/algorithms_cpp/test/merge_sort_test.h new file mode 100644 index 0000000..45bc4dc --- /dev/null +++ a/algorithms_cpp/test/merge_sort_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_MERGE_SORT_TEST_H_ +#define ALGORITHMS_CPP_TEST_MERGE_SORT_TEST_H_ + +#include + +/** + * Provides tests for the QuickSort method. + */ +class MergeSortTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + MergeSortTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_MERGE_SORT_TEST_H_ diff --git b/algorithms_cpp/test/partitioner_test.cc a/algorithms_cpp/test/partitioner_test.cc new file mode 100644 index 0000000..2ce02c9 --- /dev/null +++ a/algorithms_cpp/test/partitioner_test.cc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include +#include + +PartitionerTest::PartitionerTest() +: partitioned_array_size_(16384) { + // Size of array to be partitioned should be power of 2 + CreateUnit("TestBasic") + .Add(&PartitionerTest::TestBasic, this); + CreateUnit("TestLargeRange") + .Pre(&PartitionerTest::TestLargeRangePre, this) + .Add(&PartitionerTest::TestLargeRange, this) + .Post(&PartitionerTest::TestLargeRangePost, this); +} + +void PartitionerTest::TestBasic() { + int A[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + const int N = (sizeof(A) / sizeof(int) ); + + embb::algorithms::internal::ChunkPartitioner< int* > + partitioner(A, A + N, 5); + PT_EXPECT_EQ_MSG(*(partitioner[0].GetFirst()), 1, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[0].GetLast()-1), 3, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[1].GetFirst()), 4, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[1].GetLast()-1), 6, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[2].GetFirst()), 7, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[2].GetLast()-1), 9, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[3].GetFirst()), 10, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[3].GetLast()-1), 11, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[4].GetFirst()), 12, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[4].GetLast()-1), 13, "Get end iterator"); + + PT_EXPECT_EQ_MSG(partitioner.Size(), size_t(5), "Check count of partitions"); + + embb::algorithms::internal::BlockSizePartitioner< int* > + partitioner2(A, A + N, 5); + + PT_EXPECT_EQ_MSG(*(partitioner2[0].GetFirst()), 1, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[0].GetLast() - 1), 5, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[1].GetFirst()), 6, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[1].GetLast() - 1), 10, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[2].GetFirst()), 11, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[2].GetLast() - 1), 13, "Get end iterator"); + + PT_EXPECT_EQ_MSG(partitioner2.Size(), size_t(3), "Check count of partitions"); +} + +void PartitionerTest::TestLargeRangePre() { + partitioned_array_ = new int[partitioned_array_size_]; + for (size_t i = 0; i < partitioned_array_size_; ++i) { + partitioned_array_[i] = static_cast(i); + } +} + +void PartitionerTest::TestLargeRangePost() { + delete[] partitioned_array_; +} + +void PartitionerTest::TestLargeRange() { + // Test chunk partitioner with increasing number of chunks: + for (size_t num_chunks = 2; + num_chunks < partitioned_array_size_; + num_chunks *= 2) { + embb::algorithms::internal::ChunkPartitioner + chunk_partitioner( + partitioned_array_, + partitioned_array_ + partitioned_array_size_, + num_chunks); + int last_value_prev = -1; + PT_EXPECT_EQ(num_chunks, chunk_partitioner.Size()); + // Iterate over chunks in partition: + for (size_t chunk = 0; chunk < chunk_partitioner.Size(); ++chunk) { + int first_value = *(chunk_partitioner[chunk].GetFirst()); + int last_value = *(chunk_partitioner[chunk].GetLast() - 1); + PT_EXPECT_LT(first_value, last_value); + // Test seams between chunks: chunk[i].last + 1 == chunk[i+1].first + PT_EXPECT_EQ((last_value_prev + 1), first_value); + last_value_prev = last_value; + } + } + // Test block size partitioner with increasing chunk size: + for (size_t block_size = 1; + block_size < partitioned_array_size_; + block_size *= 2) { + embb::algorithms::internal::BlockSizePartitioner + chunk_partitioner( + partitioned_array_, + partitioned_array_ + partitioned_array_size_, + block_size); + int last_value_prev = -1; + // Iterate over chunks in partition: + for (size_t chunk = 0; chunk < chunk_partitioner.Size(); ++chunk) { + int first_value = *(chunk_partitioner[chunk].GetFirst()); + int last_value = *(chunk_partitioner[chunk].GetLast() - 1); + if (block_size == 1) { + PT_EXPECT_EQ(first_value, last_value); + } else { + PT_EXPECT_LT(first_value, last_value); + } + // Test seams between chunks: chunk[i].last + 1 == chunk[i+1].first + PT_EXPECT_EQ((last_value_prev + 1), first_value); + last_value_prev = last_value; + } + } +} + diff --git b/algorithms_cpp/test/partitioner_test.h a/algorithms_cpp/test/partitioner_test.h new file mode 100644 index 0000000..31001bb --- /dev/null +++ a/algorithms_cpp/test/partitioner_test.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ +#define ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ + +#include + +class PartitionerTest : public partest::TestCase { + public: + PartitionerTest(); + + private: + void TestBasic(); + + void TestLargeRangePre(); + void TestLargeRangePost(); + void TestLargeRange(); + + int * partitioned_array_; + size_t partitioned_array_size_; +}; + +#endif // ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ diff --git b/algorithms_cpp/test/quick_sort_test.cc a/algorithms_cpp/test/quick_sort_test.cc new file mode 100644 index 0000000..ef1faba --- /dev/null +++ a/algorithms_cpp/test/quick_sort_test.cc @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static bool DescendingComparisonFunction(double lhs, double rhs) { + if (lhs < rhs) { + return true; + } else { + return false; + } +} + +QuickSortTest::QuickSortTest() { + CreateUnit("Different data structures") + .Add(&QuickSortTest::TestDataStructures, this); + CreateUnit("Function Pointers").Add(&QuickSortTest::TestFunctionPointers, + this); + CreateUnit("Ranges").Add(&QuickSortTest::TestRanges, this); + CreateUnit("Block sizes").Add(&QuickSortTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&QuickSortTest::TestPolicy, this); + CreateUnit("Stress test").Add(&QuickSortTest::StressTest, this); +} + +void QuickSortTest::TestDataStructures() { + using embb::algorithms::QuickSort; + using embb::tasks::ExecutionPolicy; + + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end()); + QuickSort(array, array + kCountSize, std::less()); + QuickSort(vector.begin(), vector.end()); + QuickSort(array, array + kCountSize, std::less(), ExecutionPolicy(), 1); + QuickSort(deque.begin(), deque.end()); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], array[i]); + PT_EXPECT_EQ(vector_copy[i], vector[i]); + PT_EXPECT_EQ(vector_copy[i], deque[i]); + } +} + +void QuickSortTest::TestFunctionPointers() { + using embb::algorithms::QuickSort; + + std::vector vector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i + 2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end(), + &DescendingComparisonFunction); + QuickSort(vector.begin(), vector.end(), &DescendingComparisonFunction); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void QuickSortTest::TestRanges() { + using embb::algorithms::QuickSort; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + // Ommit first element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end(), std::greater()); + QuickSort(vector.begin() + 1, vector.end(), std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end() - 1, std::greater()); + QuickSort(vector.begin(), vector.end() - 1, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit first and last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end() - 1, + std::greater()); + QuickSort(vector.begin() + 1, vector.end() - 1, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + + // Only do first two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.begin() + 2, std::greater()); + QuickSort(vector.begin(), vector.begin() + 2, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do last two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.end() - 2, vector_copy.end(), std::greater()); + QuickSort(vector.end() - 2, vector.end(), std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do second & third elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.begin() + 3, + std::greater()); + QuickSort(vector.begin() + 1, vector.begin() + 3, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void QuickSortTest::TestBlockSizes() { + using embb::algorithms::QuickSort; + using embb::tasks::ExecutionPolicy; + + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end(), std::greater()); + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(), block_size); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], vector_copy[i]); + } + } +} + +void QuickSortTest::TestPolicy() { + using embb::algorithms::QuickSort; + using embb::tasks::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end(), std::greater()); + + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(true)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(true, 1)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // MergeSort on empty list should not throw: + QuickSort(vector.begin(), vector.begin(), std::less()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + QuickSort(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + QuickSort(second, vector.begin(), std::less()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif +} + +void QuickSortTest::StressTest() { + using embb::algorithms::QuickSort; + size_t count = embb::tasks::Node::GetInstance().GetCoreCount() * 10; + std::vector large_vector(count); + std::vector vector_copy(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast((i + 2) % 1000); + } + vector_copy = large_vector; + std::sort(vector_copy.begin(), vector_copy.end(), std::greater()); + + QuickSort(large_vector.begin(), large_vector.end(), std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(large_vector[i], vector_copy[i]); + } +} diff --git b/algorithms_cpp/test/quick_sort_test.h a/algorithms_cpp/test/quick_sort_test.h new file mode 100644 index 0000000..1523117 --- /dev/null +++ a/algorithms_cpp/test/quick_sort_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_QUICK_SORT_TEST_H_ +#define ALGORITHMS_CPP_TEST_QUICK_SORT_TEST_H_ + +#include + +/** + * Provides tests for the QuickSort method. + */ +class QuickSortTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + QuickSortTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_QUICK_SORT_TEST_H_ diff --git b/algorithms_cpp/test/reduce_test.cc a/algorithms_cpp/test/reduce_test.cc new file mode 100644 index 0000000..cf66a0a --- /dev/null +++ a/algorithms_cpp/test/reduce_test.cc @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +/** + * Functor to compute the square of a number. + */ +struct Square { + template + Type operator()(Type& l) const { + return l * l; + } +}; + +static int SquareFunction(int &val) { + return val * val; +} + +static int AddFunction(int lhs, int rhs) { + return lhs + rhs; +} + +ReduceTest::ReduceTest() { + CreateUnit("Different data structures") + .Add(&ReduceTest::TestDataStructures, this); + CreateUnit("Transform").Add(&ReduceTest::TestTransform, this); + CreateUnit("Function Pointers").Add(&ReduceTest::TestFunctionPointers, this); + CreateUnit("Ranges").Add(&ReduceTest::TestRanges, this); + CreateUnit("Block sizes").Add(&ReduceTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&ReduceTest::TestPolicy, this); + CreateUnit("Stress test").Add(&ReduceTest::StressTest, this); +} + +void ReduceTest::TestDataStructures() { + using embb::algorithms::Reduce; + int sum = 0; + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + + PT_EXPECT_EQ(Reduce(array, array + kCountSize, 0, std::plus()), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), static_cast(0), + std::plus()), sum); + PT_EXPECT_EQ(Reduce(deque.begin(), deque.end(), 0, std::plus()), sum); +} + +void ReduceTest::TestTransform() { + using embb::algorithms::Reduce; + int sum = 0; + std::vector vector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + sum += static_cast((i + 2) * (i + 2)); + } + + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Square()), sum); +} + +void ReduceTest::TestFunctionPointers() { + using embb::algorithms::Reduce; + std::vector vector(kCountSize); + int sum = 0; + int sqr_sum = 0; + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i + 2); + sum += static_cast(i + 2); + sqr_sum += static_cast((i + 2) * (i + 2)); + } + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, &AddFunction), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, &AddFunction, + &SquareFunction), sqr_sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + &SquareFunction), sqr_sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, &AddFunction, + Square()), sqr_sum); +} + +void ReduceTest::TestRanges() { + using embb::algorithms::Reduce; + size_t count = 4; + int sum = 0; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + vector = init; + + // Ommit first element + PT_EXPECT_EQ(Reduce(vector.begin() + 1, vector.end(), 0, std::plus()), + sum - vector[0]); + // Ommit last element + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end() - 1, 0, std::plus()), + sum - vector[vector.size() - 1]); + // Ommit first and last element + PT_EXPECT_EQ(Reduce(vector.begin() + 1, vector.end() - 1, 0, + std::plus()), sum - vector[0] - vector[vector.size() - 1]); + // Only do first element + PT_EXPECT_EQ(Reduce(vector.begin(), vector.begin() + 1, 0, std::plus()), + vector[0]); + // Only do last element + PT_EXPECT_EQ(Reduce(vector.end() - 1, vector.end(), 0, std::plus()), + vector[vector.size() - 1]); + // Only do second element + PT_EXPECT_EQ(Reduce(vector.begin() + 1, vector.begin() + 2, 0, + std::plus()), vector[1]); +} + +void ReduceTest::TestBlockSizes() { + using embb::algorithms::Reduce; + size_t count = 4; + int sum = 0; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + vector = init; + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus()), + sum); + } +} + +void ReduceTest::TestPolicy() { + using embb::algorithms::Reduce; + using embb::tasks::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = 4; + int sum = 0; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + vector = init; + + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Identity(), ExecutionPolicy()), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Identity(), ExecutionPolicy(true)), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Identity(), ExecutionPolicy(true, 1)), sum); + // Empty list should return neutral element: + PT_EXPECT_EQ(Reduce(vector.begin(), vector.begin(), 41, std::plus(), + Identity(), ExecutionPolicy(true, 1)), 41); +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + Reduce(vector.begin(), vector.end(), 0, + std::plus(), Identity(), + ExecutionPolicy(false)); + } catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + Reduce(second, vector.begin(), 0, std::plus()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif +} + +void ReduceTest::StressTest() { + using embb::algorithms::Reduce; + using embb::tasks::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = embb::tasks::Node::GetInstance().GetCoreCount() * 10; + std::vector large_vector(count); + mtapi_int32_t expected = 0; + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast(i+2); + expected += large_vector[i]; + } + PT_EXPECT_EQ(Reduce(large_vector.begin(), large_vector.end(), + mtapi_int32_t(0), std::plus(), Identity(), + ExecutionPolicy(), 1960), expected); +} diff --git b/algorithms_cpp/test/reduce_test.h a/algorithms_cpp/test/reduce_test.h new file mode 100644 index 0000000..e2d8e13 --- /dev/null +++ a/algorithms_cpp/test/reduce_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_REDUCE_TEST_H_ +#define ALGORITHMS_CPP_TEST_REDUCE_TEST_H_ + +#include + +/** + * Provides tests for the Reduce method. + */ +class ReduceTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + ReduceTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the reduce function with the transform interface. + */ + void TestTransform(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_REDUCE_TEST_H_ diff --git b/algorithms_cpp/test/scan_test.cc a/algorithms_cpp/test/scan_test.cc new file mode 100644 index 0000000..ffab634 --- /dev/null +++ a/algorithms_cpp/test/scan_test.cc @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +/** + * Functor to compute the square of a number. + */ +struct Square { + template + Type operator()(Type& l) const { + return l * l; + } +}; + +static int SquareFunction(int &val) { + return val * val; +} + +static int AddFunction(int lhs, int rhs) { + return lhs + rhs; +} + +ScanTest::ScanTest() { + CreateUnit("Different data structures") + .Add(&ScanTest::TestDataStructures, this); + CreateUnit("Transform").Add(&ScanTest::TestTransform, this); + CreateUnit("Function Pointers").Add(&ScanTest::TestFunctionPointers, this); + CreateUnit("Ranges").Add(&ScanTest::TestRanges, this); + CreateUnit("Block sizes").Add(&ScanTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&ScanTest::TestPolicy, this); + CreateUnit("Stress test").Add(&ScanTest::StressTest, this); +} + +void ScanTest::TestDataStructures() { + using embb::algorithms::Scan; + + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + + int outputArray[kCountSize]; + std::vector outputVector(kCountSize); + std::deque outputDeque(kCountSize); + + Scan(array, array + kCountSize, outputArray, 0, std::plus()); + Scan(vector.begin(), vector.end(), outputVector.begin(), + static_cast(0), std::plus()); + Scan(deque.begin(), deque.end(), outputDeque.begin(), 0, std::plus()); + + int expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += array[i]; + + PT_EXPECT_EQ(expected, outputArray[i]); + PT_EXPECT_EQ(expected, outputVector[i]); + PT_EXPECT_EQ(expected, outputDeque[i]); + } +} + +void ScanTest::TestTransform() { + using embb::algorithms::Scan; + + std::vector vector(kCountSize); + std::vector outputVector(kCountSize); + + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + } + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Square()); + + int expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } +} + +void ScanTest::TestFunctionPointers() { + using embb::algorithms::Scan; + + std::vector vector(kCountSize); + std::vector init(kCountSize); + std::vector outputVector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + init[i] = 0; + } + + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction); + int expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction, + &SquareFunction); + expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction, + Square()); + expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + &SquareFunction); + expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } +} + +void ScanTest::TestRanges() { + using embb::algorithms::Scan; + using embb::algorithms::Identity; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector outputVector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + + // Ommit first element + outputVector = init; + Scan(vector.begin() + 1, vector.end(), outputVector.begin() + 1, + 0, std::plus()); + PT_EXPECT_EQ(outputVector[0], vector[0]); + int expected = 0; + for (size_t i = 1; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + // Ommit last element + outputVector = init; + Scan(vector.begin(), vector.end() - 1, outputVector.begin(), 0, + std::plus()); + expected = 0; + for (size_t i = 0; i < count - 1; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + PT_EXPECT_EQ(outputVector[count - 1], vector[count - 1]); + + // Ommit first and last element + outputVector = init; + Scan(vector.begin() + 1, vector.end() - 1, outputVector.begin() + 1, 0, + std::plus()); + PT_EXPECT_EQ(outputVector[0], vector[0]); + expected = 0; + for (size_t i = 1; i < count - 1; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + PT_EXPECT_EQ(outputVector[count - 1], vector[count - 1]); + + // Only do first element + outputVector = init; + Scan(vector.begin(), vector.begin() + 1, outputVector.begin(), 0, + std::plus()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(outputVector[i], vector[i]); + } + + // Only do last element + outputVector = init; + Scan(vector.end() - 1, vector.end(), outputVector.end() - 1, 0, + std::plus()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(outputVector[i], vector[i]); + } + + // Only do second element + outputVector = init; + Scan(vector.begin() + 1, vector.begin() + 2, outputVector.begin() + 1, 0, + std::plus()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(outputVector[i], vector[i]); + } +} + +void ScanTest::TestBlockSizes() { + using embb::algorithms::Scan; + using embb::tasks::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector outputVector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, + std::plus(), Identity(), ExecutionPolicy(), block_size); + int expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + } +} + +void ScanTest::TestPolicy() { + using embb::algorithms::Scan; + using embb::tasks::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector outputVector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy()); + int expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy(true)); + expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy(true, 1)); + expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + // Empty list should not throw and not change output: + outputVector = init; + std::vector::iterator out_it = outputVector.begin(); + Scan(vector.begin(), vector.begin(), out_it, 0, std::plus()); + PT_EXPECT(out_it == outputVector.begin()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + Scan(vector.begin(), vector.end(), outputVector.begin(), + 0, std::plus(), Identity(), + ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + Scan(second, vector.begin(), outputVector.begin(), 0, std::plus()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif +} + +void ScanTest::StressTest() { + using embb::algorithms::Scan; + using embb::algorithms::Identity; + using embb::tasks::ExecutionPolicy; + size_t count = embb::tasks::Node::GetInstance().GetCoreCount() *10; + std::vector large_vector(count); + std::vector large_vector_output(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast((i + 2) % 1000); + } + Scan(large_vector.begin(), large_vector.end(), large_vector_output.begin(), 0, + std::plus(), Identity(), ExecutionPolicy(), 2000); + int expected = 0; + for (size_t i = 0; i < count; i++) { + expected += large_vector[i]; + PT_EXPECT_EQ(expected, large_vector_output[i]); + } +} diff --git b/algorithms_cpp/test/scan_test.h a/algorithms_cpp/test/scan_test.h new file mode 100644 index 0000000..44e66b5 --- /dev/null +++ a/algorithms_cpp/test/scan_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_SCAN_TEST_H_ +#define ALGORITHMS_CPP_TEST_SCAN_TEST_H_ + +#include + +/** + * Provides tests for the Scan method. + */ +class ScanTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + ScanTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the scan function with the transform interface. + */ + void TestTransform(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_SCAN_TEST_H_ diff --git b/algorithms_cpp/test/zip_iterator_test.cc a/algorithms_cpp/test/zip_iterator_test.cc new file mode 100644 index 0000000..2e67e7c --- /dev/null +++ a/algorithms_cpp/test/zip_iterator_test.cc @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +typedef std::vector::iterator VectorIterator; +typedef std::vector::const_iterator constVectorIterator; + +struct DotProductFunctor { + template + int operator()(embb::algorithms::ZipPair pair) const { + return pair.First() * pair.Second(); + } + + template + int operator()(int lhs, embb::algorithms::ZipPair rhs) const { + return lhs + rhs.First() * rhs.Second(); + } + + int operator()(int lhs, int rhs) const { + return lhs + rhs; + } +}; + +/** + * Functor to compute the square of a number. + * The result overwrites the original number. + */ +struct Square { + void operator()(embb::algorithms::ZipPair pair) const { + pair.First() = pair.First() * pair.First(); + pair.Second() = pair.Second() * pair.Second(); + } +}; + +ZipIteratorTest::ZipIteratorTest() { + CreateUnit("Zip foreach") + .Add(&ZipIteratorTest::TestZipForEach, this); + CreateUnit("Zip Reduce") + .Add(&ZipIteratorTest::TestZipReduce, this); + CreateUnit("Zip Scan") + .Add(&ZipIteratorTest::TestZipScan, this); + CreateUnit("Iterator Types") + .Add(&ZipIteratorTest::TestIteratorTypes, this); + CreateUnit("Double Zip") + .Add(&ZipIteratorTest::TestDoubleZip, this); +} + +void ZipIteratorTest::TestZipForEach() { + using embb::algorithms::ForEach; + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast((i + 1) % 1000); + vectorB[i] = static_cast((i + 2) % 1000); + } + ForEach( + embb::algorithms::Zip(vectorA.begin(), vectorB.begin()), + embb::algorithms::Zip(vectorA.end(), vectorB.end()), + Square()); + for (size_t i = 0; i < kCountSize; i++) { + int expected = static_cast((i + 1) % 1000); + expected = expected * expected; + PT_EXPECT_EQ(vectorA[i], expected); + expected = static_cast((i + 2) % 1000); + expected = expected * expected; + PT_EXPECT_EQ(vectorB[i], expected); + } +} + +void ZipIteratorTest::TestZipReduce() { + long sum = 0; + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i+2); + vectorB[i] = static_cast(i+2); + sum += static_cast((i + 2) * (i + 2)); + } + + embb::algorithms::ZipIterator + start_iterator = embb::algorithms::Zip( + std::vector::const_iterator(vectorA.begin()), + std::vector::const_iterator(vectorB.begin())); + embb::algorithms::ZipIterator + end_iterator = embb::algorithms::Zip( + std::vector::const_iterator(vectorA.end()), + std::vector::const_iterator(vectorB.end())); + + PT_EXPECT_EQ(embb::algorithms::Reduce(start_iterator, end_iterator, 0, + std::plus(), DotProductFunctor()), sum); +} + +void ZipIteratorTest::TestZipScan() { + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + std::vector vectorOut(kCountSize); + + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i+1); + vectorB[i] = static_cast(i+2); + } + + Scan(embb::algorithms::Zip(vectorA.begin(), vectorB.begin()), + embb::algorithms::Zip(vectorA.end(), vectorB.end()), + vectorOut.begin(), 0, std::plus(), DotProductFunctor(), + embb::tasks::ExecutionPolicy(), 0); + + long sum = 0; + for (size_t i = 0; i < kCountSize; i++) { + sum += vectorA[i] * vectorB[i]; + PT_EXPECT_EQ(sum, vectorOut[i]); + } +} + +void ZipIteratorTest::TestIteratorTypes() { + long sum = 0; + + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + + int arrayA[kCountSize]; + int arrayB[kCountSize]; + + std::deque dequeA(kCountSize); + std::deque dequeB(kCountSize); + + const int constArrayA[] = {2, 3, 4, 5, 6, 7}; + const int constArrayB[] = {2, 3, 4, 5, 6, 7}; + + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i + 2); + vectorB[i] = static_cast(i + 2); + arrayA[i] = static_cast(i + 2); + arrayB[i] = static_cast(i + 2); + dequeA[i] = static_cast(i + 2); + dequeB[i] = static_cast(i + 2); + + sum += static_cast((i + 2) * (i + 2)); + } + + using embb::algorithms::Zip; + PT_EXPECT_EQ(Reduce(Zip(vectorA.begin(), vectorB.begin()), + Zip(vectorA.end(), vectorB.end()), 0, std::plus(), + DotProductFunctor()), sum); + PT_EXPECT_EQ(Reduce(Zip(dequeA.begin(), dequeB.begin()), + Zip(dequeA.end(), dequeB.end()), 0, std::plus(), + DotProductFunctor()), sum); + PT_EXPECT_EQ(Reduce(Zip(arrayA, arrayB), + Zip(arrayA + kCountSize, arrayB + kCountSize), 0, + std::plus(), DotProductFunctor()), sum); + PT_EXPECT_EQ(Reduce(Zip(constArrayA, constArrayB), + Zip(constArrayA + kCountSize, constArrayB + kCountSize), + 0, std::plus(), DotProductFunctor()), sum); +} + +struct MultiDotProductFunctor{ + mtapi_int64_t operator()( + embb::algorithms::ZipPair, + embb::algorithms::ZipPair > rhs) const { + return rhs.First().First() * rhs.First().Second() * + rhs.Second().First() * rhs.Second().Second(); + } +}; + +void ZipIteratorTest::TestDoubleZip() { + mtapi_int64_t sum = 0; + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + std::vector vectorC(kCountSize); + std::vector vectorD(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i + 1); + vectorB[i] = static_cast(i + 2); + vectorC[i] = static_cast(i + 3); + vectorD[i] = static_cast(i + 4); + sum += vectorA[i] * vectorB[i] * vectorC[i] * vectorD[i]; + } + + using embb::algorithms::Zip; + PT_EXPECT_EQ(Reduce( + Zip(Zip(vectorA.begin(), vectorB.begin()), + Zip(vectorC.begin(), vectorD.begin())), + Zip(Zip(vectorA.end(), vectorB.end()), + Zip(vectorC.end(), vectorD.end())), + mtapi_int64_t(0), std::plus(), + MultiDotProductFunctor()), sum); +} diff --git b/algorithms_cpp/test/zip_iterator_test.h a/algorithms_cpp/test/zip_iterator_test.h new file mode 100644 index 0000000..8764914 --- /dev/null +++ a/algorithms_cpp/test/zip_iterator_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALGORITHMS_CPP_TEST_ZIP_ITERATOR_TEST_H_ +#define ALGORITHMS_CPP_TEST_ZIP_ITERATOR_TEST_H_ + +#include + +/** + * Provides tests for the Zip Iterator class. + */ +class ZipIteratorTest : public partest::TestCase { + public: + ZipIteratorTest(); + + private: + /** + * Tests different iterator types. + */ + void TestIteratorTypes(); + + /** + * Tests the foreach with zipIterator. + */ + void TestZipForEach(); + + /** + * Tests the reduce with zipIterator. + */ + void TestZipReduce(); + + /** + * Tests the reduce with zipIterator. + */ + void TestZipScan(); + + /** + * Tests the double zipping scenario. + */ + void TestDoubleZip(); + + static const size_t kCountSize = 5; +}; + + +#endif // ALGORITHMS_CPP_TEST_ZIP_ITERATOR_TEST_H_ diff --git b/base_c/CMakeLists.txt a/base_c/CMakeLists.txt new file mode 100644 index 0000000..a05179d --- /dev/null +++ a/base_c/CMakeLists.txt @@ -0,0 +1,124 @@ +project (project_embb_base_c) + +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) + + +## CODE FILE DETECTION +# +# Fetch all header and source files for lib and test build separately +file(GLOB_RECURSE EMBB_BASE_SOURCES "src/*.c" "src/*.h" "src/*.cc") +file(GLOB_RECURSE EMBB_BASE_HEADERS "include/embb/base/c/*.h") +if (BUILD_TESTS STREQUAL ON) + file(GLOB_RECURSE EMBB_BASE_TEST_SOURCES "test/*.cc" "test/*.h") + IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) + FOREACH(src_tmp ${EMBB_BASE_TEST_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) + ENDFOREACH(src_tmp) + ENDIF() +endif() + +IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) +FOREACH(src_tmp ${EMBB_BASE_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +ENDIF() + +include(CheckTypeSize) +check_type_size(char EMBB_CHAR_TYPE_SIZE) +check_type_size(short EMBB_SHORT_TYPE_SIZE) +check_type_size("unsigned short" EMBB_UNSIGNED_SHORT_TYPE_SIZE) +check_type_size(int EMBB_INT_TYPE_SIZE) +check_type_size("unsigned int" EMBB_UNSIGNED_INT_TYPE_SIZE) +check_type_size(long EMBB_LONG_TYPE_SIZE) +check_type_size("unsigned long" EMBB_UNSIGNED_LONG_TYPE_SIZE) +check_type_size("long long" EMBB_LONG_LONG_TYPE_SIZE) +check_type_size("unsigned long long" EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE) +check_type_size(intptr_t EMBB_INTPTR_T_TYPE_SIZE) +check_type_size(uintptr_t EMBB_UINTPTR_T_TYPE_SIZE) +check_type_size(size_t EMBB_SIZE_T_TYPE_SIZE) +check_type_size(ptrdiff_t EMBB_PTRDIFF_T_TYPE_SIZE) +check_type_size(uintmax_t EMBB_UINTMAX_T_TYPE_SIZE) + +# Create header file from input file +configure_file("include/embb/base/c/internal/atomic/atomic_sizes.h.in" + "include/embb/base/c/internal/atomic/atomic_sizes.h") + +## Compiling and linking assembler code for atomic operations in MSVC +# +# The assembler implementation for atomic operations in MSVC is +# contained in external asm files (inline assembler not possible). +# The lines below are used for compiling those file to object files +# and linking them to the embb library. +# +if(MSVC) + if(EMBB_INTPTR_T_TYPE_SIZE EQUAL 8) + set(EMBB_BASE_ASM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/atomicfunc_64.asm") + set(EMBB_BASE_ASM_OBJS "${CMAKE_CURRENT_BINARY_DIR}/atomicfunc_64.obj") + set(MSVC_ML ml64) + set(MSVC_SAFESEH "") + else() + set(EMBB_BASE_ASM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/atomicfunc_32.asm") + set(EMBB_BASE_ASM_OBJS "${CMAKE_CURRENT_BINARY_DIR}/atomicfunc_32.obj") + set(MSVC_ML ml) + set(MSVC_SAFESEH "/safeseh") + endif() + add_custom_command( + DEPENDS ${EMBB_BASE_ASM_SOURCES} + OUTPUT ${EMBB_BASE_ASM_OBJS} + COMMAND ${MSVC_ML} /c /Zi ${MSVC_SAFESEH} + ${EMBB_BASE_ASM_SOURCES} + /Fo ${EMBB_BASE_ASM_OBJS} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +## CONFIGURATION +# +# Check headers and create configure file with preprocessor defines +include(CheckIncludeFiles) # Includes module to perform checks +include(CheckSymbolExists) # Includes module to perform symbol checks +check_include_files("sys/sysinfo.h" EMBB_PLATFORM_HAS_HEADER_SYSINFO) +check_include_files("sys/types.h;sys/sysctl.h" EMBB_PLATFORM_HAS_HEADER_SYSCTL) +check_include_files("sys/param.h;sys/cpuset.h" EMBB_PLATFORM_HAS_HEADER_CPUSET) +link_libraries(${link_libraries} ${gnu_libs}) +set(CMAKE_EXTRA_INCLUDE_FILES sched.h) + check_type_size(cpu_set_t EMBB_PLATFORM_HAS_GLIB_CPU) +set(CMAKE_EXTRA_INCLUDE_FILES) +if(DEFINED EMBB_PLATFORM_HAS_GLIB_CPU) + add_definitions(-D_GNU_SOURCE) # Needed to activate CPU_ macros +endif() + +# Create header file from input file +configure_file("include/embb/base/c/internal/cmake_config.h.in" + "include/embb/base/c/internal/cmake_config.h") + +# Execute the GroupSources macro +GroupSourcesMSVC(include/embb/base/c) +GroupSourcesMSVC(src) +if (BUILD_TESTS STREQUAL ON) + GroupSourcesMSVC(test) +endif() + +## BUILD TARGETS +# +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + ) + +add_library(embb_base_c ${EMBB_BASE_SOURCES} ${EMBB_BASE_HEADERS} + ${EMBB_BASE_ASM_SOURCES} ${EMBB_BASE_ASM_OBJS}) + +if (BUILD_TESTS STREQUAL ON) + include_directories(test/ ${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_base_c_test ${EMBB_BASE_TEST_SOURCES}) + target_link_libraries(embb_base_c_test partest embb_base_c + ${compiler_libs}) + CopyBin(BIN embb_base_c_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_base_c DESTINATION lib) diff --git b/base_c/include/embb/base/c/atomic.h a/base_c/include/embb/base/c/atomic.h new file mode 100644 index 0000000..b00b337 --- /dev/null +++ a/base_c/include/embb/base/c/atomic.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_ATOMIC_H_ +#define EMBB_BASE_C_ATOMIC_H_ + +/** + * \defgroup C_BASE_ATOMIC Atomic + * + * \ingroup C_BASE + * + * Atomic operations. + * + * \anchor general_desc_atomic_base + * Atomic operations are not directly applied to fundamental types. Instead, + * there is for each character and integer type an associated atomic type that + * has the same bit width (if the target CPU supports atomic operations on + * that type): + * + * Fundamental type | Atomic type + * :------------------| :---------------------------------- + * char | embb_atomic_char + * short | embb_atomic_short + * unsigned short | embb_atomic_unsigned_short + * int | embb_atomic_int + * unsigned int | embb_atomic_unsigned_int + * long | embb_atomic_long + * unsigned long | embb_atomic_unsigned_long + * long long | embb_atomic_long_long + * unsigned long long | embb_atomic_unsigned_long_long + * intptr_t | embb_atomic_intptr_t + * uintptr_t | embb_atomic_uintptr_t + * size_t | embb_atomic_size_t + * ptrdiff_t | embb_atomic_ptrdiff_t + * uintmax_t | embb_atomic_uintmax_t + * + * Each of the atomic operations described in the following can be applied to + * the types listed above. However, to avoid unnecessary redundancy, we document + * them only once in a generic way. The keyword TYPE serves as a placeholder + * which has to be replaced by the concrete type (e.g., int). If the fundamental + * type contains spaces (e.g., unsigned int), "_" is used for concatenation + * (e.g. unsigned_int). + * + * Usage example: + * -------------- + * + * Store the value \c 5 in an atomic \c "unsigned int" variable. + * + * Step 1 (declare atomic variable): + * \code + * embb_atomic_unsigned_int my_var; + * \endcode + + * Step 2 (store the value): + * \code + * embb_atomic_store_unsigned_int( &my_var, 5 ); + * \endcode + * + * The current implementation guarantees sequential consistency (full fences) + * for all atomic operations. Relaxed memory models may be added in the future. + */ + +#ifdef DOXYGEN +/** + * Computes the logical "and" of the value stored in \p variable and \c value. + * + * The result is stored in \p variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE void embb_atomic_and_assign_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable which serves as left-hand side for + the "and" operation and is used to store the result. */ + TYPE value + /** [IN] Right-hand side of "and" operation, passed by value */ + ); + +/** + * Compares \p variable with \p expected and, if equivalent, swaps its value + * with \p desired. + * + * Stores \p desired in \p variable if the value of \p variable is equivalent + * to the value of \p expected. Otherwise, stores the value of \p variable in + * \p expected. + * + * \return != 0 if the values of \p variable and \p expected were equivalent \n + * 0 otherwise + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE int embb_atomic_compare_and_swap_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable */ + TYPE* expected, + /**< [IN,OUT] Pointer to expected value */ + TYPE desired + /**< [IN] Value to be stored in \p variable */ + ); + +/** + * Adds \p value to \p variable and returns its old value. + * + * \return The value before the operation + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE TYPE embb_atomic_fetch_and_add_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable*/ + TYPE value + /**< [IN] The value to be added to \p variable (can be negative) */ + ); + +/** + * Loads the value of \p variable and returns it. + * + * \return The value of the atomic variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE TYPE embb_atomic_load_TYPE( + const embb_atomic_TYPE* variable + /**< [IN] Pointer to atomic variable */ + ); + +/** + * Enforces a memory barrier (full fence). + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE void embb_atomic_memory_barrier(); + +/** + * Computes the logical "or" of the value stored in \p variable and \c value. + * + * The result is stored in \p variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE void embb_atomic_or_assign_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable which serves as left-hand side for + the "or" operation and is used to store the result. */ + TYPE value + /** [IN] Right-hand side of "or" operation, passed by value */ + ); + +/** + * Stores \p value in \p variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE void embb_atomic_store_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable */ + int value + /**< [IN] Value to be stored */ + ); + +/** + * Swaps the current value of \p variable with \p value. + * + * \return The old value of \p variable + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_PLATFORM_INLINE TYPE embb_atomic_swap_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable whose value is swapped */ + TYPE value + /** [IN] Value which will be stored in the atomic variable */ + ); + +/** +* Computes the logical "xor" of the value stored in \p variable and \c value. +* +* The result is stored in \p variable. +* +* \see \ref general_desc_atomic_base "Detailed description" for general +* information and the meaning of \b TYPE. +* +* \ingroup C_BASE_ATOMIC +* \waitfree +*/ +EMBB_PLATFORM_INLINE void embb_atomic_xor_assign_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable which serves as left-hand side for + the "xor" operation and is used to store the result. */ + TYPE value + /** [IN] Right-hand side of "xor" operation, passed by value */ + ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif //EMBB_BASE_C_ATOMIC_H_ diff --git b/base_c/include/embb/base/c/base.h a/base_c/include/embb/base/c/base.h new file mode 100644 index 0000000..bcc178a --- /dev/null +++ a/base_c/include/embb/base/c/base.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_BASE_H_ +#define EMBB_BASE_C_BASE_H_ + +/** + * \defgroup C C Components + * Components written in C. + */ + +/** + * \defgroup C_BASE Base + * \ingroup C + * Platform-independent abstraction layer for multithreading and basic + * operations. + * + * This component provides basic functionalities, mainly for creating and + * synchronizing threads. Most of the functions are essentially wrappers for + * functions specific to the underlying operating system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* EMBB_BASE_C_BASE_H_ */ diff --git b/base_c/include/embb/base/c/condition_variable.h a/base_c/include/embb/base/c/condition_variable.h new file mode 100644 index 0000000..d707c2b --- /dev/null +++ a/base_c/include/embb/base/c/condition_variable.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_CONDITION_VARIABLE_H_ +#define EMBB_BASE_C_CONDITION_VARIABLE_H_ + +/** + * \defgroup C_BASE_CONDITION Condition Variable + * Condition variables for thread synchronization. + * + * Provides an abstraction from platform-specific condition variable + * implementations. Condition variables can be waited for with timeouts using + * relative durations and absolute time points. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing a condition variable. + */ +typedef opaque_type embb_condition_t; +#endif /* DOXYGEN */ + +/** + * Initializes a condition variable. + * + * \memory Potentially allocates dynamic memory + * \pre \c condition_var is not initialized + * \post If successful, \c condition_var is initialized + * \return EMBB_SUCCESS if successful \n + * EMBB_ERROR otherwise + * \notthreadsafe + * \see embb_condition_destroy() + */ +int embb_condition_init( + embb_condition_t* condition_var + /**< [OUT] Pointer to condition variable */ + ); + +/** + * Wakes up one thread waiting for \c condition_var. + * + * \pre \c condition_var is initialized + * \return EMBB_SUCCESS if signaling was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_all(), embb_condition_wait(), + * embb_condition_wait_until(), embb_condition_wait_for() + */ +int embb_condition_notify_one( + embb_condition_t* condition_var + /**< [IN/OUT] Pointer to condition variable */ + ); + +/** + * Wakes up all threads waiting for \c condition_var. + * + * \pre \c condition_var is initialized + * \return EMBB_SUCCESS if broadcast was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_wait(), + * embb_condition_wait_until(), embb_condition_wait_for() + */ +int embb_condition_notify_all( + embb_condition_t* condition_var + /**< [IN/OUT] Pointer to condition variable */ + ); + +/** + * Unlocks \c mutex and waits until the thread is woken up. + * + * \pre \c condition_var is initialized and \c mutex is locked by calling thread + * \post If successful, \c mutex is locked by the calling thread + * \return EMBB_SUCCESS if successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_notify_all(), + * embb_condition_wait_until(), embb_condition_wait_for() + */ +int embb_condition_wait( + embb_condition_t* condition_var, + /**< [IN/OUT] Pointer to condition variable */ + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Unlocks \c mutex and waits until the thread is woken up or \c time has + * passed. + * + * \pre \c condition_var is initialized and \c mutex is locked by calling thread + * \post If successful, \c mutex is locked by the calling thread + * \return EMBB_SUCCESS if successful \n + * EMBB_TIMEDOUT if mutex could not be locked until the specified point + * of time \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_notify_all(), + * embb_condition_wait(), embb_condition_wait_for() + */ +int embb_condition_wait_until( + embb_condition_t* condition_var, + /**< [IN/OUT] Pointer to condition variable */ + embb_mutex_t* mutex, + /**< [IN/OUT] Pointer to mutex */ + const embb_time_t* time + /**< [IN] Point of time until the thread waits */ + ); + +/** + * Unlocks \c mutex and waits until the thread is woken up or \c duration + * has passed. + * + * \pre \c condition_var is initialized and \c mutex is locked by calling thread + * \post If successful, \c mutex is locked by the calling thread + * \return EMBB_SUCCESS if successful \n + * EMBB_TIMEDOUT if mutex could not be locked within the specified time + * span \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_notify_all(), + * embb_condition_wait(), embb_condition_wait_until() + */ +int embb_condition_wait_for( + embb_condition_t* condition_var, + /**< [IN/OUT] Pointer to condition variable */ + embb_mutex_t* mutex, + /**< [IN/OUT] Pointer to mutex */ + const embb_duration_t* duration + /**< [IN] Duration in microseconds the thread waits */ + ); + +/** + * Destroys \c condition_var and frees used memory. + * + * \pre \c condition_var is initialized and no thread is waiting for it using + * embb_condition_wait(), embb_condition_wait_for(), or + * embb_condition_wait_until(). + * \post \c condition_var is uninitialized + * \return EMBB_SUCCESS if destruction of condition variable was successful \n + * EMBB_ERROR otherwise + * \notthreadsafe + * \see embb_condition_init() + */ +int embb_condition_destroy( + embb_condition_t* condition_var + /**< [IN] Pointer to condition variable */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_CONDITION_VARIABLE_H_ */ diff --git b/base_c/include/embb/base/c/core_set.h a/base_c/include/embb/base/c/core_set.h new file mode 100644 index 0000000..2f06615 --- /dev/null +++ a/base_c/include/embb/base/c/core_set.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_CORE_SET_H_ +#define EMBB_BASE_C_CORE_SET_H_ + +#include + +/** + * \defgroup C_BASE_CORESET Core Set + * + * Core sets for thread-to-core affinities + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Opaque type representing a set of processor cores. + * + * An instance of this type represents a subset of processor cores. Core sets + * can be used to set thread-to-core affinities. A core in a core set might + * just represent a logical core (hyper-thread), depending on the underlying + * hardware. Each core is identified by a unique integer starting with 0. + * For example, the cores of a quad-core system are represented by the set + * {0,1,2,3}. + * + * \see embb_core_count_available() + */ +#ifdef DOXYGEN +typedef opaque_type embb_core_set_t; +#else +typedef struct embb_core_set_t { + uint64_t rep; +} embb_core_set_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Returns the number of available processor cores. + * + * If the processor supports hyper-threading, each hyper-thread is treated as a + * separate processor core. + * + * \return Number of cores including hyper-threads + * + * \notthreadsafe + */ +unsigned int embb_core_count_available(); + +/** + * Initializes the specified core set. + * + * The second parameter specifies whether the set is initially empty or contains + * all cores. + * + * \pre \c core_set is not NULL. + * + * \notthreadsafe + */ +void embb_core_set_init( + embb_core_set_t* core_set, + /**< [OUT] Core set to initialize */ + int initializer + /**< [IN] The set is initially empty if initializer == 0, otherwise it + contains all available processor cores. */ + ); + +/** + * Adds a core to the specified set. + * + * If the core is already contained in the set, the operation has no effect. + * + * \pre \c core_set is not NULL and \c core_number is smaller than + * embb_core_count_available(). + * + * \notthreadsafe + * \see embb_core_set_remove() + */ +void embb_core_set_add( + embb_core_set_t* core_set, + /**< [IN/OUT] Core set to be manipulated */ + unsigned int core_number + /**< [IN] Number of core to be added. */ + ); + +/** + * Removes a core from the specified set. + * + * If the core is not in the set, the operation has no effect. + * + * \pre \c core_set is not NULL and \c core_number is smaller than + * embb_core_count_available(). + * + * \notthreadsafe + * \see embb_core_set_add() + */ +void embb_core_set_remove( + embb_core_set_t* core_set, + /**< [IN/OUT] Core set to be manipulated */ + unsigned int core_number + /**< [IN] Number of core to be removed */ + ); + +/** + * Determines whether a core is contained in the specified set. + * + * \pre \c core_set is not NULL and \c core_number is smaller than + * embb_core_count_available(). + * + * \return 0 if the core is not contained in the set, otherwise a number + * greater than zero. + * \notthreadsafe + */ +int embb_core_set_contains( + const embb_core_set_t* core_set, + /**< [IN] Core set */ + unsigned int core_number + /**< [IN] Number of core */ + ); + +/** + * Computes the intersection of core \c set1 and \c set2. + * + * The result is stored in \c set1. + * + * \pre \c set1 and \c set2 are not NULL. + * + * \notthreadsafe + * \see embb_core_set_union() + */ +void embb_core_set_intersection( + embb_core_set_t* set1, + /**< [IN/OUT] First set, gets overwritten by the result */ + const embb_core_set_t* set2 + /**< [IN] Second set */ + ); + +/** +* Computes the union of core \c set1 and \c set2. +* +* The result is stored in \c set1. +* +* \pre \c set1 and \c set2 are not NULL. +* +* \notthreadsafe +* \see embb_core_set_intersection() +*/ +void embb_core_set_union( + embb_core_set_t* set1, + /**< [IN/OUT] First set */ + const embb_core_set_t* set2 + /**< [IN] Second set */ + ); + +/** + * Returns the number of cores contained in the specified set. + * + * \pre \c core_set is not NULL. + * + * \notthreadsafe + * \return Number of cores in \c core_set + */ +unsigned int embb_core_set_count( + const embb_core_set_t* core_set + /**< [IN] Core set whose elements are counted */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_CORE_SET_H_ */ diff --git b/base_c/include/embb/base/c/counter.h a/base_c/include/embb/base/c/counter.h new file mode 100644 index 0000000..aa66eec --- /dev/null +++ a/base_c/include/embb/base/c/counter.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_COUNTER_H_ +#define EMBB_BASE_C_COUNTER_H_ + +/** + * \defgroup C_BASE_COUNTER Counter + * Thread-safe counter + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Opaque type representing a thread-safe counter. + */ +#ifdef DOXYGEN +typedef opaque_type embb_counter_t; +#else +typedef struct embb_counter_t { + embb_atomic_unsigned_int value; +} embb_counter_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Initializes \c counter and sets it to zero. + * + * \return EMBB_SUCCESS if counter could be initialized \n + * EMBB_ERROR otherwise + * + * \waitfree + */ +int embb_counter_init( + embb_counter_t* counter + /**< [OUT] Pointer to counter */ + ); + +/** + * Returns the current value of \c counter. + * + * \pre \c counter is not NULL. + * + * \return Current value + * + * \waitfree + */ +unsigned int embb_counter_get( + embb_counter_t* counter + /**< [IN] Pointer to counter */ + ); + +/** + * Increments \c counter and returns the old value. + * + * \pre \c counter is not NULL. + * + * \return Old, non-incremented value + * \waitfree + */ +unsigned int embb_counter_increment( + embb_counter_t* counter + /**< [IN,OUT] Pointer to counter */ + ); + +/** + * Decrements \c counter and returns the old value. + * + * \pre \c counter is not NULL. + * + * \return Old, non-decremented value + * \waitfree + */ +unsigned int embb_counter_decrement( + embb_counter_t* counter + /**< [IN,OUT] Pointer to counter */ + ); + +/** + * Destroys an initialized counter. + * + * \pre \c counter is initialized and not NULL. + * \post \c counter is invalid and cannot be used anymore + * \waitfree + */ +void embb_counter_destroy( + embb_counter_t* counter + /**< [OUT] Pointer to counter */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_COUNTER_H_ */ diff --git b/base_c/include/embb/base/c/duration.h a/base_c/include/embb/base/c/duration.h new file mode 100644 index 0000000..7f943fb --- /dev/null +++ a/base_c/include/embb/base/c/duration.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_DURATION_H_ +#define EMBB_BASE_C_DURATION_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup C_BASE_DURATIONTIME Duration and Time + * + * Relative time durations and absolute time points + * + * \ingroup C_BASE + * \{ + * + * \name Duration + * \{ + */ + +/** + * Opaque type representing a relative time duration. + */ +#ifdef DOXYGEN +typedef opaque_type embb_duration_t; +#else +typedef struct embb_duration_t { + /** + * Seconds part of duration + */ + unsigned long long seconds; + /** + * Nanoseconds part of duration, smaller than a second + */ + unsigned long nanoseconds; +} embb_duration_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Macro for initializing a duration with zero length at definition. + */ +#ifdef DOXYGEN +#define EMBB_DURATION_INIT +#else +#define EMBB_DURATION_INIT {0, 0} +#endif /* defined(DOXYGEN) */ + +/** + * Returns duration with maximum ticks representable by implementation. + * + * \return Pointer to duration with maximum value + * \notthreadsafe + * \see embb_duration_min() + */ +const embb_duration_t* embb_duration_max(); + +/** + * Returns duration with minimum ticks representable by implementation. + * + * \return Pointer to duration with minimum value + * \notthreadsafe + * \see embb_duration_max() + */ +const embb_duration_t* embb_duration_min(); + +/** + * Returns duration of length zero. + * + * \return Pointer to duration of length zero + * \notthreadsafe + */ +const embb_duration_t* embb_duration_zero(); + +/** + * Set duration from nanosecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given nanosecond interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given nanosecond interval is too large to be + * represented by implementation + * \notthreadsafe + */ +int embb_duration_set_nanoseconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long nanoseconds + /**< [IN] Nanosecond ticks */ + ); + +/** + * Sets duration from microsecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given microsecond interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given microsecond interval is too large to be + * represented by implementation + * \notthreadsafe + */ +int embb_duration_set_microseconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long microseconds + /**< [IN] Microsecond ticks */ + ); + +/** + * Sets duration from millisecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given millisecond interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given millisecond interval is too large to be + * represented by implementation \n + * \notthreadsafe + */ +int embb_duration_set_milliseconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long milliseconds + /**< [IN] Millisecond ticks */ + ); + +/** + * Sets duration from second ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given second interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given second interval is too large to be + * represented by implementation + * \notthreadsafe + */ +int embb_duration_set_seconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long seconds + /**< [IN] Second ticks */ + ); + +/** + * Adds two durations. + * + * Computest the sum of \c rhs and \c lhs and stores the result in \c lhs. + * + * \return EMBB_SUCCESS \n + * EMBB_OVERFLOW if sum is greater than embb_duration_max() + * \notthreadsafe + */ +int embb_duration_add( + embb_duration_t* lhs, + /**< [IN/OUT] Left-hand side operand, overwritten by result of addition */ + const embb_duration_t* rhs + /**< [IN] Right-hand side operand of addition */ + ); + +/** + * Converts duration to nanosecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * nanosecond \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_nanoseconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* nanoseconds + /**< [OUT] Pointer to nanosecond ticks of duration */ + ); + +/** + * Converts duration to microsecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * microsecond \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_microseconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* microseconds + /**< [OUT] Pointer to microsecond ticks of duration */ + ); + +/** + * Converts duration to millisecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * millisecond \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_milliseconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* milliseconds + /**< [OUT] Pointer to millisecond ticks of duration */ + ); + +/** + * Converts duration to second ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * second \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_seconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* seconds + /**< [OUT] Pointer to second ticks of duration */ + ); + +/** + * Compares two durations. + * + * \pre \c lhs and \c rhs are not NULL and properly initialized. + * + * \return -1 if \c lhs < \c rhs \n + * 0 if \c lhs == \c rhs \n + * 1 if \c lhs > \c rhs + * \notthreadsafe + */ +int embb_duration_compare( + const embb_duration_t* lhs, + /**< [IN] Pointer to left-hand side operand */ + const embb_duration_t* rhs + /**< [IN] Pointer to right-hand side operand */ + ); + +/** + * \} + * \} + */ + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +#endif /* EMBB_BASE_C_DURATION_H_ */ diff --git b/base_c/include/embb/base/c/errors.h a/base_c/include/embb/base/c/errors.h new file mode 100644 index 0000000..fef908b --- /dev/null +++ a/base_c/include/embb/base/c/errors.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_ERRORS_H_ +#define EMBB_BASE_C_ERRORS_H_ + +/** + * \defgroup C_BASE_ERROR Error + * + * Error codes for function return values + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Return value codes for functions. + */ +typedef enum { + EMBB_SUCCESS, /**< Successful */ + EMBB_NOMEM, /**< Error, not enough memory */ + EMBB_TIMEDOUT, /**< Error, timed out */ + EMBB_BUSY, /**< Resource busy */ + EMBB_OVERFLOW, /**< Error, numeric overflow */ + EMBB_UNDERFLOW, /**< Error, numeric underflow */ + EMBB_ERROR /**< Error, not further specified */ +} embb_errors_t; + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_ERRORS_H_ */ diff --git b/base_c/include/embb/base/c/internal/atomic/and_assign.h a/base_c/include/embb/base/c/internal/atomic/and_assign.h new file mode 100644 index 0000000..48871ca --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/and_assign.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_AND_ASSIGN_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_AND_ASSIGN_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* + * The EMBB_DEFINE_[ATOMIC METHOD] macro defines, given the size in bytes, + * the assembler prefix for that size (e.g., "b" for 1 byte), the assembler + * implementation for [ATOMIC METHOD]. For GCC, inline assembler is used. + * For MSVC, an external assembler implementation is called, as Microsoft + * doesn't allow inline assembler with 64-bit code. In that case, the + * assembly code is linked from an external object file. + * + * The generated function has the following signature (in the and_assign.h file, + * analogous for other atomic methods): + * + * static inline void embb_internal__atomic_and_assign_[BYTE_SIZE] + * (EMBB_BASE_BASIC_TYPE_SIZE_[BYTE_SIZE] volatile* pointer_to_value, + * EMBB_BASE_BASIC_TYPE_SIZE_[BYTE_SIZE] value) + * + * BYTE_SIZE is the number of bytes passed to the macro. + * + */ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_AND_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value); \ + EMBB_PLATFORM_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_AND_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX)\ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_internal__atomic_and_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + __asm__ __volatile__("lock and" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (value) \ + : \ + : "memory"); \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* + * The three or four macro calls below generate the methods for 1, 2, 4, and + * bytes, as stated in the macro definition. + */ +EMBB_DEFINE_AND_ASSIGN(1, "b") +EMBB_DEFINE_AND_ASSIGN(2, "w") +EMBB_DEFINE_AND_ASSIGN(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_AND_ASSIGN(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_AND_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + void EMBB_CAT2(embb_internal__atomic_and_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + tmp, result; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, [%2]\n\t" \ + "and %0, %0, %3\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, %0, [%2]\n\t" \ + "teq %1, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "=&r" (result), "=&r" (tmp) \ + : "r" (pointer_to_value), "Ir" (value) \ + : "memory", "cc" ); \ + } +/* __sync_fetch_and_and(pointer_to_value, value); \ + }*/ +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_AND_ASSIGN(1, "b") +EMBB_DEFINE_AND_ASSIGN(2, "h") +EMBB_DEFINE_AND_ASSIGN(4, "") + +#else +#error "Unknown architecture" +#endif + +/* + * Now, using the basic functions above, we generate the respective functions + * for all basic data types, like "unsigned short". For that purpose, the + * following generator macro is used. This macro is called by the macros in the + * generator header, defining the implementation for the basic data types. + * + * For unsigned short and for and_assign.h, the following method would be + * generated (analogous for other atomic methods): + * + * static inline void embb_atomic_and_assign_unsigned_short ( + * embb_atomic_unsigned_short* variable, unsigned short value) { + * embb_internal__atomic_and_assign_2((EMBB_BASE_BASIC_TYPE_SIZE_2 volatile *) + * (&(variable->internal_variable)), *((EMBB_BASE_BASIC_TYPE_SIZE_2*) + * (&value))); } + * + * This generated function is supposed to be called by the user of the lib. + */ +#define EMBB_ATOMIC_INTERNAL_DEFINE_AND_ASSIGN_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_atomic_and_assign_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun;\ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *) \ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE AND_ASSIGN_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_AND_ASSIGN_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in a/base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in new file mode 100644 index 0000000..26a7528 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_SIZES_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_SIZES_H_ + +#include +#include + +typedef uint8_t EMBB_BASE_BASIC_TYPE_SIZE_1; +typedef uint16_t EMBB_BASE_BASIC_TYPE_SIZE_2; +typedef uint32_t EMBB_BASE_BASIC_TYPE_SIZE_4; +typedef uint64_t EMBB_BASE_BASIC_TYPE_SIZE_8; + +/** + * CMake is used to set the correct size for types used in the atomic + * library. + * + */ +#define EMBB_CHAR_TYPE_SIZE ${EMBB_CHAR_TYPE_SIZE} +#define EMBB_SHORT_TYPE_SIZE ${EMBB_SHORT_TYPE_SIZE} +#define EMBB_UNSIGNED_SHORT_TYPE_SIZE ${EMBB_UNSIGNED_SHORT_TYPE_SIZE} +#define EMBB_INT_TYPE_SIZE ${EMBB_INT_TYPE_SIZE} +#define EMBB_UNSIGNED_INT_TYPE_SIZE ${EMBB_UNSIGNED_INT_TYPE_SIZE} +#define EMBB_LONG_TYPE_SIZE ${EMBB_LONG_TYPE_SIZE} +#define EMBB_UNSIGNED_LONG_TYPE_SIZE ${EMBB_UNSIGNED_LONG_TYPE_SIZE} +#define EMBB_LONG_LONG_TYPE_SIZE ${EMBB_LONG_LONG_TYPE_SIZE} +#define EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE ${EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE} +#define EMBB_INTPTR_T_TYPE_SIZE ${EMBB_INTPTR_T_TYPE_SIZE} +#define EMBB_UINTPTR_T_TYPE_SIZE ${EMBB_UINTPTR_T_TYPE_SIZE} +#define EMBB_SIZE_T_TYPE_SIZE ${EMBB_SIZE_T_TYPE_SIZE} +#define EMBB_PTRDIFF_T_TYPE_SIZE ${EMBB_PTRDIFF_T_TYPE_SIZE} +#define EMBB_UINTMAX_T_TYPE_SIZE ${EMBB_UINTMAX_T_TYPE_SIZE} + + +#ifdef EMBB_PLATFORM_ARCH_X86_64 +# define EMBB_64_BIT_ATOMIC_AVAILABLE_VAL 1 +# define EMBB_64_BIT_ATOMIC_AVAILABLE +#else +# define EMBB_64_BIT_ATOMIC_AVAILABLE_VAL 0 +#endif + +#if( (EMBB_CHAR_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_CHAR_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_SHORT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_SHORT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_SHORT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_SHORT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_INT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_INT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_INT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_INT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_LONG_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_LONG_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_LONG_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_INTPTR_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_INTPTR_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UINTPTR_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UINTPTR_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_SIZE_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_SIZE_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_PTRDIFF_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_PTRDIFF_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UINTMAX_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UINTMAX_T_TYPE_IS_ATOMIC +#endif + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_SIZES_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/atomic_variables.h a/base_c/include/embb/base/c/internal/atomic/atomic_variables.h new file mode 100644 index 0000000..4c35b1e --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/atomic_variables.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_ATOMIC_VARIABLES_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_ATOMIC_VARIABLES_H_ + +#include +#include + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#include +#endif + +#define EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE( \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, \ + EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX) \ + typedef struct \ +{ \ + volatile EMBB_ATOMIC_PARAMETER_TYPE_NATIVE internal_variable; \ +} EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX); + +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(char, char) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(short, short) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned short, unsigned_short) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(int, int) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned int, unsigned_int) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(long, long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned long, unsigned_long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(long long, long_long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned long long, unsigned_long_long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(intptr_t, intptr_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(uintptr_t, uintptr_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(size_t, size_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(ptrdiff_t, ptrdiff_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(uintmax_t, uintmax_t) + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_ATOMIC_VARIABLES_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/compare_and_swap.h a/base_c/include/embb/base/c/internal/atomic/compare_and_swap.h new file mode 100644 index 0000000..8de4e09 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/compare_and_swap.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_COMPARE_AND_SWAP_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_COMPARE_AND_SWAP_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_COMPARE_AND_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern int __fastcall EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* expected, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired); \ + EMBB_PLATFORM_INLINE int __fastcall EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_PARAMETER_SIZE_BYTE)( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* expected, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired) { \ + int result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, expected, desired); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_COMPARE_AND_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE int EMBB_CAT2(embb_internal__atomic_compare_and_swap_, \ + EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* expected, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired) { \ + char result; \ + __asm__ __volatile__ ("lock cmpxchg" EMBB_ATOMIC_X86_SIZE_SUFFIX\ + " %3, %0 \n\t" \ + "setz %2 \n\t" \ + : "+m" (*pointer_to_value), "+a" (*expected), "=q" (result) \ + : "q" (desired) \ + : "memory", "cc" ); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_COMPARE_AND_SWAP(1, "b") +EMBB_DEFINE_COMPARE_AND_SWAP(2, "w") +EMBB_DEFINE_COMPARE_AND_SWAP(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_COMPARE_AND_SWAP(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_COMPARE_AND_SWAP(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + int EMBB_CAT2(embb_internal__atomic_compare_and_swap_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* expected, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + oldval, res; \ + __asm__ __volatile__ ("dmb" : : : "memory"); \ + do { \ + __asm__ __volatile__ ( \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, [%2]\n\t" \ + "mov %0, #0\n\t" \ + "teq %1, %3\n\t" \ + "it eq\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX "eq %0, %4, [%2]\n\t" \ + : "=&r" (res), "=&r" (oldval) \ + : "r" (pointer_to_value), "Ir" (*expected), "r" (desired) \ + : "cc" ); \ + } while (res); \ + __asm__ __volatile__ ("isb" : : : "memory"); \ + if (oldval == *expected) { \ + return 1; \ + } else { \ + *expected = oldval; \ + return 0; \ + } \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_COMPARE_AND_SWAP(1, "b") +EMBB_DEFINE_COMPARE_AND_SWAP(2, "h") +EMBB_DEFINE_COMPARE_AND_SWAP(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_COMPARE_AND_SWAP_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE int EMBB_CAT2(embb_atomic_compare_and_swap_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE* expected, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE desired) {\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) desired_pun;\ + memcpy(&desired_pun, &desired, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + return EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *) \ + (&(variable->internal_variable)), (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *) expected, desired_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE COMPARE_AND_SWAP_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_COMPARE_AND_SWAP_H_ + diff --git b/base_c/include/embb/base/c/internal/atomic/fetch_and_add.h a/base_c/include/embb/base/c/internal/atomic/fetch_and_add.h new file mode 100644 index 0000000..a165744 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/fetch_and_add.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_FETCH_AND_ADD_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_FETCH_AND_ADD_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_FETCH_AND_ADD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value); \ + EMBB_PLATFORM_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_PARAMETER_SIZE_BYTE) (\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) {\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_fetch_and_add_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, new_value); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_FETCH_AND_ADD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_PARAMETER_SIZE_BYTE) \ + (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) { \ + __asm__ __volatile__ ("lock xadd" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (new_value) \ + : \ + : "memory", "cc" ); \ + return new_value; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_FETCH_AND_ADD(1, "b") +EMBB_DEFINE_FETCH_AND_ADD(2, "w") +EMBB_DEFINE_FETCH_AND_ADD(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_FETCH_AND_ADD(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_FETCH_AND_ADD(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + EMBB_CAT2(embb_internal__atomic_fetch_and_add_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + new_value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + tmp1, tmp2, result; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, [%3]\n\t" \ + "add %1, %0, %4\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %1, [%3]\n\t" \ + "teq %2, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "=&r" (result), "=&r" (tmp1), "=&r" (tmp2) \ + : "r" (pointer_to_value), "r" (new_value) \ + : "memory", "cc" ); \ + return result; \ + } +/* return __sync_fetch_and_add( \ + pointer_to_value, new_value); \ + }*/ +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_FETCH_AND_ADD(1, "b") +EMBB_DEFINE_FETCH_AND_ADD(2, "h") +EMBB_DEFINE_FETCH_AND_ADD(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_FETCH_AND_ADD_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE EMBB_ATOMIC_PARAMETER_TYPE_NATIVE EMBB_CAT2(embb_atomic_fetch_and_add_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun; \ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) return_val = EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)(\ + (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE return_val_pun; \ + memcpy(&return_val_pun, &return_val, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + return return_val_pun; \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE FETCH_AND_ADD_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_FETCH_AND_ADD_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h a/base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h new file mode 100644 index 0000000..318b835 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +// This file is used as a template, it is not a normal header file! +// Do not include it as a header! It is used for generating methods +// for all atomic types. + +#define EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_PARAMETER_POSTFIX, \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, \ + EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, \ + EMBB_ATOMIC_PARAMETER_TYPE_SIZE \ + ) \ + EMBB_CAT2(EMBB_ATOMIC_INTERNAL_DEFINE_, EMBB_ATOMIC_PARAMETER_POSTFIX)\ + (EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, \ + EMBB_ATOMIC_PARAMETER_TYPE_SIZE) + +#ifdef EMBB_CHAR_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC(EMBB_ATOMIC_METHOD_TO_GENERATE, \ + char, char, EMBB_CHAR_TYPE_SIZE) +#endif +#ifdef EMBB_SHORT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + short, short, EMBB_SHORT_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_SHORT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned short, unsigned_short, EMBB_UNSIGNED_SHORT_TYPE_SIZE) +#endif +#ifdef EMBB_INT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + int, int, EMBB_INT_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_INT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned int, unsigned_int, EMBB_UNSIGNED_INT_TYPE_SIZE) +#endif +#ifdef EMBB_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + long, long, EMBB_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned long, unsigned_long, EMBB_UNSIGNED_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_LONG_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + long long, long_long, EMBB_LONG_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_LONG_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned long long, unsigned_long_long, EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_INTPTR_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + intptr_t, intptr_t, EMBB_INTPTR_T_TYPE_SIZE) +#endif +#ifdef EMBB_UINTPTR_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + uintptr_t, uintptr_t, EMBB_UINTPTR_T_TYPE_SIZE) +#endif +#ifdef EMBB_SIZE_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + size_t, size_t, EMBB_SIZE_T_TYPE_SIZE) +#endif +#ifdef EMBB_PTRDIFF_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + ptrdiff_t, ptrdiff_t, EMBB_PTRDIFF_T_TYPE_SIZE) +#endif +#ifdef EMBB_UINTMAX_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + uintmax_t, uintmax_t, EMBB_UINTMAX_T_TYPE_SIZE) +#endif diff --git b/base_c/include/embb/base/c/internal/atomic/load.h a/base_c/include/embb/base/c/internal/atomic/load.h new file mode 100644 index 0000000..20094b5 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/load.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_LOAD_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_LOAD_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_LOAD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value); \ + EMBB_PLATFORM_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value) { \ + register EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_load_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_LOAD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value) { \ + /* no fence required for loads */ \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + __asm__ __volatile__("mov" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "=q" (result) \ + : "m" (*pointer_to_value) \ + : "memory"); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_LOAD(1, "b") +EMBB_DEFINE_LOAD(2, "w") +EMBB_DEFINE_LOAD(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_LOAD(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_LOAD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + result; \ + __asm__ __volatile__(\ + "dmb\n\t" \ + "ldr" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, [%1]\n\t" \ + "dmb" \ + : "=&r" (result) \ + : "r" (pointer_to_value) \ + : "memory"); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_LOAD(1, "b") +EMBB_DEFINE_LOAD(2, "h") +EMBB_DEFINE_LOAD(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_LOAD_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE EMBB_ATOMIC_PARAMETER_TYPE_NATIVE EMBB_CAT2(embb_atomic_load_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + const EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + return_val = (EMBB_CAT2(embb_internal__atomic_load_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)(\ + (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)(&(variable->internal_variable)))); \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE return_val_pun; \ + memcpy(&return_val_pun, &return_val, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + return return_val_pun; \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE LOAD_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_LOAD_H_ + diff --git b/base_c/include/embb/base/c/internal/atomic/memory_barrier.h a/base_c/include/embb/base/c/internal/atomic/memory_barrier.h new file mode 100644 index 0000000..392366f --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/memory_barrier.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_MEMORY_BARRIER_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_MEMORY_BARRIER_H_ + +#include + +#ifndef DOXYGEN + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#include +#endif + +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +extern void __fastcall embb_internal__atomic_memory_barrier_asm(); +// Read/write barrier +EMBB_PLATFORM_INLINE void __fastcall embb_atomic_memory_barrier() { + _ReadWriteBarrier(); + embb_internal__atomic_memory_barrier_asm(); + _ReadWriteBarrier(); +} +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +// Read/write barrier +EMBB_PLATFORM_INLINE void embb_atomic_memory_barrier() { + __asm__ __volatile__ ("mfence" : : : "memory"); +} +#else +#error "No atomic fetch and store implementation found" +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +EMBB_PLATFORM_INLINE void embb_atomic_memory_barrier() { + __asm__ __volatile__ ("dmb" : : : "memory"); +} + +#endif + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_MEMORY_BARRIER_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/or_assign.h a/base_c/include/embb/base/c/internal/atomic/or_assign.h new file mode 100644 index 0000000..9c76cf8 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/or_assign.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_OR_ASSIGN_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_OR_ASSIGN_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_OR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value); \ + EMBB_PLATFORM_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_OR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + __asm__ __volatile__("lock or" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (value) \ + : \ + : "memory"); \ + } + +#else +#error "No atomic or assign implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_OR_ASSIGN(1, "b") +EMBB_DEFINE_OR_ASSIGN(2, "w") +EMBB_DEFINE_OR_ASSIGN(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_OR_ASSIGN(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_OR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + void EMBB_CAT2(embb_internal__atomic_or_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + result, tmp; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, [%2]\n\t" \ + "orr %0, %0, %3\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, %0, [%2]\n\t" \ + "teq %1, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "=&r" (result), "=&r" (tmp) \ + : "r" (pointer_to_value), "Ir" (value) \ + : "memory", "cc" ); \ + } +#else +#error "No atomic or assign implementation found" +#endif + +EMBB_DEFINE_OR_ASSIGN(1, "b") +EMBB_DEFINE_OR_ASSIGN(2, "h") +EMBB_DEFINE_OR_ASSIGN(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_OR_ASSIGN_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_atomic_or_assign_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun;\ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE OR_ASSIGN_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_OR_ASSIGN_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/store.h a/base_c/include/embb/base/c/internal/atomic/store.h new file mode 100644 index 0000000..fdb6a40 --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/store.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_STORE_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_STORE_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_STORE(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX)\ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value); \ + EMBB_PLATFORM_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) \ + { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_store_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, new_value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_STORE(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX)\ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) {\ + /*the lock prefix is implicit for xchg*/ \ + __asm__ __volatile__("xchg" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (new_value) \ + : \ + : "memory"); \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_STORE(1, "b") +EMBB_DEFINE_STORE(2, "w") +EMBB_DEFINE_STORE(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_STORE(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_STORE(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_ARM_SIZE_SUFFIX)\ + EMBB_PLATFORM_INLINE \ + void EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* \ + pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) {\ + __asm__ __volatile__( \ + "dmb\n\t" \ + "str" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, [%0]" \ + : \ + : "r" (pointer_to_value), "r" (new_value) \ + : "memory"); \ + } +/* __sync_synchronize(); \ + *pointer_to_value = new_value; \ + __sync_synchronize(); \ + }*/ +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_STORE(1, "b") +EMBB_DEFINE_STORE(2, "h") +EMBB_DEFINE_STORE(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_STORE_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_atomic_store_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun; \ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + EMBB_CAT2(embb_internal__atomic_store_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE STORE_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_STORE_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/swap.h a/base_c/include/embb/base/c/internal/atomic/swap.h new file mode 100644 index 0000000..70c397c --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/swap.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_SWAP_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_SWAP_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2 (embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value); \ + EMBB_PLATFORM_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2 (embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, new_value); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) EMBB_CAT2(embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value)\ + { \ + /*the lock prefix is implicit for xchg*/ \ + __asm__ __volatile__("xchg" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (new_value) \ + : \ + : "memory"); \ + return new_value; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_SWAP(1, "b") +EMBB_DEFINE_SWAP(2, "w") +EMBB_DEFINE_SWAP(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_SWAP(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) EMBB_CAT2 \ + (embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value)\ + { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + tmp, result; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, [%3]\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, %2, [%3]\n\t" \ + "teq %1, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "=&r" (result), "=&r" (tmp) \ + : "r" (new_value), "r" (pointer_to_value) \ + : "memory", "cc" ); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_SWAP(1, "b") +EMBB_DEFINE_SWAP(2, "h") +EMBB_DEFINE_SWAP(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_SWAP_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE EMBB_ATOMIC_PARAMETER_TYPE_NATIVE EMBB_CAT2(embb_atomic_swap_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun; \ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + return_val = EMBB_CAT2(embb_internal__atomic_swap_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)(&(variable->internal_variable)), value_pun); \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE return_val_pun; \ + memcpy(&return_val_pun, &return_val, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + return return_val_pun; \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE SWAP_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_SWAP_H_ diff --git b/base_c/include/embb/base/c/internal/atomic/xor_assign.h a/base_c/include/embb/base/c/internal/atomic/xor_assign.h new file mode 100644 index 0000000..8de71fe --- /dev/null +++ a/base_c/include/embb/base/c/internal/atomic/xor_assign.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_ATOMIC_XOR_ASSIGN_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_XOR_ASSIGN_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_PLATFORM_ARCH_X86 + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#define EMBB_DEFINE_XOR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value); \ + EMBB_PLATFORM_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_xor_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_XOR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + __asm__ __volatile__("lock xor" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (value) \ + : \ + : "memory"); \ + } + +#else +#error "No atomic fetch xor store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_XOR_ASSIGN(1, "b") +EMBB_DEFINE_XOR_ASSIGN(2, "w") +EMBB_DEFINE_XOR_ASSIGN(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_XOR_ASSIGN(8, "q") +#endif + +#elif defined(EMBB_PLATFORM_ARCH_ARM) + +#if defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_DEFINE_XOR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_PLATFORM_INLINE \ + void EMBB_CAT2(embb_internal__atomic_xor_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + result, tmp; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, [%2]\n\t" \ + "eor %0, %0, %3\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, %0, [%2]\n\t" \ + "teq %1, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "=&r" (result), "=&r" (tmp) \ + : "r" (pointer_to_value), "Ir" (value) \ + : "memory", "cc" ); \ + } +#else +#error "No atomic or assign implementation found" +#endif + +EMBB_DEFINE_XOR_ASSIGN(1, "b") +EMBB_DEFINE_XOR_ASSIGN(2, "h") +EMBB_DEFINE_XOR_ASSIGN(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_XOR_ASSIGN_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_PLATFORM_INLINE void EMBB_CAT2(embb_atomic_xor_assign_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun;\ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE XOR_ASSIGN_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_XOR_ASSIGN_H_ diff --git b/base_c/include/embb/base/c/internal/bitset.h a/base_c/include/embb/base/c/internal/bitset.h new file mode 100644 index 0000000..0dd7216 --- /dev/null +++ a/base_c/include/embb/base/c/internal/bitset.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_BITSET_H_ +#define EMBB_BASE_C_INTERNAL_BITSET_H_ + +#include +#include +#include + +#include + +EMBB_PLATFORM_INLINE void embb_bitset_set( + uint64_t * that, + unsigned int bit + ) { + assert(NULL != that); + assert(64 > bit); + *that |= (1ull << bit); +} + +EMBB_PLATFORM_INLINE void embb_bitset_set_n( + uint64_t * that, + unsigned int count) { + assert(NULL != that); + assert(0 < count); + assert(64 >= count); + if (64 == count) { + *that = ~0ull; + } else { + *that = (1ull << count) - 1ull; + } +} + +EMBB_PLATFORM_INLINE void embb_bitset_clear( + uint64_t * that, + unsigned int bit + ) { + assert(NULL != that); + assert(64 > bit); + *that &= ~(1ull << bit); +} + +EMBB_PLATFORM_INLINE void embb_bitset_clear_all( + uint64_t * that + ) { + assert(NULL != that); + *that = 0ull; +} + +EMBB_PLATFORM_INLINE unsigned int embb_bitset_is_set( + uint64_t const * that, + unsigned int bit + ) { + return (unsigned int)((*that & (1ull << bit)) ? 1 : 0); +} + +EMBB_PLATFORM_INLINE void embb_bitset_intersect( + uint64_t * that, + uint64_t mask + ) { + assert(NULL != that); + *that &= mask; +} + +EMBB_PLATFORM_INLINE void embb_bitset_union( + uint64_t * that, + uint64_t mask + ) { + assert(NULL != that); + *that |= mask; +} + +EMBB_PLATFORM_INLINE unsigned int embb_bitset_count( + uint64_t const * that + ) { + unsigned int count = 0; + uint64_t mask = 1ull; + for (unsigned int ii = 0; ii < 64; ii++) { + if ((*that & mask) > 0) { + count++; + } + mask <<= 1; + } + return count; +} + +#endif // EMBB_BASE_C_INTERNAL_BITSET_H_ diff --git b/base_c/include/embb/base/c/internal/cmake_config.h.in a/base_c/include/embb/base/c/internal/cmake_config.h.in new file mode 100644 index 0000000..f86d3e6 --- /dev/null +++ a/base_c/include/embb/base/c/internal/cmake_config.h.in @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_CMAKE_CONFIG_H_ +#define EMBB_BASE_C_INTERNAL_CMAKE_CONFIG_H_ + +/* This file is used as input for CMake. CMake creates a file cmake_config.h in + its current build directory under the path builddir/embb/base/internal/. From + there, the cmake_config.h can be included as usual using + #include + */ + +/** + * Is used to get the number of cores on certain systems. + */ +#cmakedefine EMBB_PLATFORM_HAS_HEADER_SYSINFO + +/** + * Is used to get the number of cores on certain systems. + */ +#cmakedefine EMBB_PLATFORM_HAS_HEADER_SYSCTL + +/** + * Is used to set thread affinities on certain systems. + */ +#cmakedefine EMBB_PLATFORM_HAS_HEADER_CPUSET + +/** + * Is used for Linux thread affinities. + */ +#cmakedefine EMBB_PLATFORM_HAS_GLIB_CPU + + +#define EMBB_BASE_VERSION_MAJOR ${EMBB_BASE_VERSION_MAJOR} +#define EMBB_BASE_VERSION_MINOR ${EMBB_BASE_VERSION_MINOR} +#define EMBB_BASE_VERSION_PATCH ${EMBB_BASE_VERSION_PATCH} + + +#endif /* EMBB_BASE_INTERNAL_CMAKE_CONFIG_H_ */ diff --git b/base_c/include/embb/base/c/internal/config.h a/base_c/include/embb/base/c/internal/config.h new file mode 100644 index 0000000..8a08174 --- /dev/null +++ a/base_c/include/embb/base/c/internal/config.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_CONFIG_H_ +#define EMBB_BASE_C_INTERNAL_CONFIG_H_ + +#include + +/* Define names: + * - threading platforms: EMBB_THREADING_ + * - compilers: EMBB_COMPILER_ + * - operating systems: EMBB_OS_ + */ + +#ifdef DOXYGEN +/* For Doxygen, simulate GNU compiler on 64 bit */ +#define __GNUC__ +#define __x86_64__ +#endif + +/* Most processors have cache lines with up to 64 Bytes, except for Itanium + * which has 128 bytes. + */ +#if defined(_M_IA64) || defined(_IA64) +#define EMBB_PLATFORM_CACHE_LINE_SIZE 128 +#else +#define EMBB_PLATFORM_CACHE_LINE_SIZE 64 +#endif + +/* For MSVC, if _DEBUG is set, set also EMBB_DEBUG. + * There is no such flag for GCC. Instead, cmake sets + * the EMBB_DEBUG flag... + */ +#ifdef _DEBUG +#define EMBB_DEBUG +#endif + +#ifdef __GNUC__ +#define EMBB_PLATFORM_ALIGN(size) __attribute__ ((aligned(size))) +#elif defined _MSC_VER || defined __INTEL_COMPILER +#define EMBB_PLATFORM_ALIGN(size) __declspec(align(size)) +#else +#error "Unsupported compiler" +#endif + +#if __GNUC__ +#define EMBB_PLATFORM_INLINE static inline +#define EMBB_PLATFORM_COMPILER_GNUC +#elif _MSC_VER +#define EMBB_PLATFORM_INLINE __inline +#define EMBB_PLATFORM_COMPILER_MSVC +#else +#define EMBB_PLATFORM_INLINE inline +#define EMBB_PLATFORM_COMPILER_UNKNOWN +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#define EMBB_PLATFORM_ARCH_X86_64 +#define EMBB_PLATFORM_ARCH_X86 +#define EMBB_PLATFORM_HAS_CAS_64 +#elif defined(__i386) || defined(_M_IX86) +#define EMBB_PLATFORM_ARCH_X86_32 +#define EMBB_PLATFORM_ARCH_X86 +#elif defined(__arm__) +#define EMBB_PLATFORM_ARCH_ARM +#else +#define EMBB_PLATFORM_ARCH_UNKNOWN +#endif + +#if defined(EMBB_PLATFORM_COMPILER_MSVC) +#define EMBB_PLATFORM_THREADING_WINTHREADS +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) +#define EMBB_PLATFORM_THREADING_POSIXTHREADS +#else +#error "No thread implementation could be determined" +#endif + +#endif /* EMBB_BASE_C_INTERNAL_CONFIG_H_ */ diff --git b/base_c/include/embb/base/c/internal/macro_helper.h a/base_c/include/embb/base/c/internal/macro_helper.h new file mode 100644 index 0000000..8d43d8c --- /dev/null +++ a/base_c/include/embb/base/c/internal/macro_helper.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_MACRO_HELPER_H_ +#define EMBB_BASE_C_INTERNAL_MACRO_HELPER_H_ + +#define EMBB_CAT2(_1, _2) _1 ## _2 +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +#endif /* EMBB_BASE_C_INTERNAL_MACRO_HELPER_H_ */ diff --git b/base_c/include/embb/base/c/internal/platform.h a/base_c/include/embb/base/c/internal/platform.h new file mode 100644 index 0000000..5e21de6 --- /dev/null +++ a/base_c/include/embb/base/c/internal/platform.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_PLATFORM_H_ +#define EMBB_BASE_C_INTERNAL_PLATFORM_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define EMBB_TIME_MAX_SECONDS ULLONG_MAX +#define EMBB_DURATION_MAX_SECONDS 60 * 60 * 24 * 7 + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +// Suppress virtual functions but non-virtual constructor warning +// in windows headers +#pragma warning(push) +#pragma warning(disable : 4265) +#endif + +#define NOMINMAX +#include + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) // Reset warning 4640 +#endif + +struct embb_internal_thread_arg_t; + +/** + * Opaque handle for a thread. + */ +typedef struct embb_thread_t { + HANDLE embb_internal_handle; + struct embb_internal_thread_arg_t* embb_internal_arg; +} embb_thread_t; + +typedef DWORD embb_thread_id_t; +typedef CRITICAL_SECTION embb_mutex_t; +typedef CONDITION_VARIABLE embb_condition_t; + +#define EMBB_DURATION_MIN_NANOSECONDS 1000 + +#define EMBB_THREAD_SPECIFIC static __declspec(thread) + +#elif defined EMBB_PLATFORM_THREADING_POSIXTHREADS + +#include +#include +#include + +struct embb_internal_thread_arg_t; + +/** + * Opaque handle for a thread. + */ +typedef struct embb_thread_t { + pthread_t embb_internal_handle; + struct embb_internal_thread_arg_t* embb_internal_arg; +} embb_thread_t; + +typedef pthread_t embb_thread_id_t; +typedef pthread_mutex_t embb_mutex_t; +typedef pthread_cond_t embb_condition_t; + +#define EMBB_DURATION_MIN_NANOSECONDS 1 + +#define EMBB_THREAD_SPECIFIC __thread + +#else /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ + +#error "No threading platform defined!" + +#endif /* else */ + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +#endif // EMBB_BASE_C_INTERNAL_PLATFORM_H_ diff --git b/base_c/include/embb/base/c/internal/thread_index.h a/base_c/include/embb/base/c/internal/thread_index.h new file mode 100644 index 0000000..239b6e3 --- /dev/null +++ a/base_c/include/embb/base/c/internal/thread_index.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_THREAD_INDEX_H_ +#define EMBB_BASE_C_INTERNAL_THREAD_INDEX_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Tries to return the current thread's (internal) index. + * + * A new index has to be obtained only on first call of the function. Later + * calls always succeed, since they just return the index obtained in the + * first call. + * + * \pre embb_internal_thread_index_create() has been called. + * \return EMBB_SUCCESS, if an index could be obtained. + * EMBB_ERROR, if no more indices were available. + * \lockfree + */ +int embb_internal_thread_index( + unsigned int* index + /**< [OUT] Pointer to memory location to write thread index to. */ + ); + +/** + * Returns the maximum number of available thread indices. + * + * \lockfree + */ +int embb_internal_thread_index_max(); + +/** + * Sets the maximum number of thread indices. + * + * Data structures rely on the maximal number of indices. Thus, it should only + * be set at the very beginning of a program using EMBB, or in the beginning + * of a test case. + * + * \notthreadsafe + */ +void embb_internal_thread_index_set_max( + unsigned int max + /**< [IN] Maximum number of thread indices */ + ); + +/** + * Sets the thread index to zero. + * + * Should only be called in tests, in order to avoid a constantly growing + * index set. + * + * \waitfree + */ +void embb_internal_thread_index_reset(); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +#endif /* EMBB_BASE_C_INTERNAL_THREAD_INDEX_H_ */ + + + diff --git b/base_c/include/embb/base/c/internal/unused.h a/base_c/include/embb/base/c/internal/unused.h new file mode 100644 index 0000000..d68ff3a --- /dev/null +++ a/base_c/include/embb/base/c/internal/unused.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_INTERNAL_UNUSED_H_ +#define EMBB_BASE_C_INTERNAL_UNUSED_H_ + +#define EMBB_UNUSED(x) (void)(x) + +#ifdef NDEBUG +#define EMBB_UNUSED_IN_RELEASE(x) (void)(x) +#else +#define EMBB_UNUSED_IN_RELEASE(x) +#endif + +#endif /* EMBB_BASE_C_INTERNAL_UNUSED_H_ */ diff --git b/base_c/include/embb/base/c/log.h a/base_c/include/embb/base/c/log.h new file mode 100644 index 0000000..bf73dc5 --- /dev/null +++ a/base_c/include/embb/base/c/log.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_LOG_H_ +#define EMBB_BASE_C_LOG_H_ + +#include +#include + +/** + * \defgroup C_LOG Logging + * \ingroup C_BASE + * Simple logging facilities. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Log levels available for filtering the log. + * \ingroup C_LOG + */ +typedef enum { + EMBB_LOG_LEVEL_NONE, /**< show no log messages */ + EMBB_LOG_LEVEL_ERROR, /**< show errors only */ + EMBB_LOG_LEVEL_WARNING, /**< show warnings and errors */ + EMBB_LOG_LEVEL_INFO, /**< show info, warnings, and errors */ + EMBB_LOG_LEVEL_TRACE /**< show everything */ +} embb_log_level_t; + +/** + * Logging function type. + * This function is used by embb_log_write() to transfer a log message to its + * desired destination. The user may specify a pointer to a context that + * contains additional data (filter rules, file handles etc.) needed to put the + * message where it should go. This pointer might be NULL if no additional data + * is needed. + * \ingroup C_LOG + * \threadsafe + */ +typedef void(*embb_log_function_t)(void * context, char const * message); + +/** + * Default logging function. + * Writes to the given file (context needs to be a FILE*). + * \pre \c context is not NULL. + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_write_file( + void * context, /**< [in] User data, in this case a FILE* + file handle. */ + char const * message /**< [in] The message to write */ + ); + +/** + * Sets the global log level. + * This determines what messages will be shown, messages with a more detailed + * log level will be filtered out. The default log level is EMBB_LOG_LEVEL_NONE. + * \ingroup C_LOG + * \notthreadsafe + */ +void embb_log_set_log_level( + embb_log_level_t log_level /**< [in] Log level to use for + filtering */ + ); + +/** + * Sets the global logging function. + * The logging function implements the mechanism for transferring log messages + * to their destination. \c context is a pointer to data the user needs in the + * function to determine where the messages should go (may be NULL if no + * additional data is needed). The default logging function is + * embb_log_write_file() with context set to \c stdout. + * \see embb_log_function_t + * \ingroup C_LOG + * \notthreadsafe + */ +void embb_log_set_log_function( + void * context, /**< [in] User context to supply as the + first parameter of the logging + function*/ + embb_log_function_t func /**< [in] The logging function */ + ); + +/** + * Logs a message to the given channel with the specified log level. + * If the log level is greater than the configured log level for the channel, + * the message will be ignored. + * \see embb_log_set_log_level, embb_log_set_log_function + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_write( + char const * channel, /**< [in] User specified channel id + for filtering the log later on. + Might be NULL, channel identifier + will be "global" in that case */ + embb_log_level_t log_level, /**< [in] Log level to use */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +#if defined(EMBB_DEBUG) || defined(DOXYGEN) +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_TRACE using + * embb_log_write(). + * In non-debug builds, this function does nothing. + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_trace( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_INFO using + * embb_log_write(). + * In non-debug builds, this function does nothing. + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_info( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); +#else +#define embb_log_trace(...) +#define embb_log_info(...) +#endif + +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_WARNING using + * embb_log_write(). + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_warning( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_ERROR using + * embb_log_write(). + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_error( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +/* function for internal use only */ +void embb_log_write_internal( + char const * channel, + embb_log_level_t log_level, + char const * message, + va_list argp); + +#ifdef __cplusplus +} +#endif + +#endif /* EMBB_BASE_C_LOG_H_ */ diff --git b/base_c/include/embb/base/c/memory_allocation.h a/base_c/include/embb/base/c/memory_allocation.h new file mode 100644 index 0000000..0bcc9c5 --- /dev/null +++ a/base_c/include/embb/base/c/memory_allocation.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_MEMORY_ALLOCATION_H_ +#define EMBB_BASE_C_MEMORY_ALLOCATION_H_ + +/** +* \defgroup C_BASE_ALLOC Memory Allocation +* +* \ingroup C_BASE +* +* Functions for dynamic memory allocation +* +* There are functions for aligned and unaligned memory allocation. In debug +* mode, memory usage is tracked to detect memory leaks. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /** + * Allocates \p size bytes of memory. + * + * Keeps track of allocated memory in debug mode. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \see embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void* embb_alloc( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by embb_alloc() for some pointer + * \p ptr. + * + * Keeps track of freed memory in debug mode. + * + * \pre \c ptr is not NULL. + * + * \threadsafe + * + * \see embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void embb_free( + void* ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Allocates \p size bytes of memory with alignment \p alignment. + * + * This function can be used to align objects to certain boundaries such as + * cache lines, memory pages, etc. + * + * Keeps track of allocated memory in debug mode. + * + * It is not required that \p size is a multiple of \p alignment as, e.g., + * for the \c aligned\_alloc function of the C11 standard. + * + * \pre The alignment has to be power of 2 and a multiple of + * size(void*). + * \post The returned pointer is a multiple of \p alignment. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \memory Debug mode: Let \c n be the number of aligned cells necessary to + * fit the payload. Then, (n+1)*alignment+3*size_of(size_t)-1 + * bytes are allocated.
Release mode: \c size bytes are requested + * using the functions provided by the operating systems. + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * embb_free_aligned(). + * + * \see embb_alloc_cache_aligned(), embb_free_aligned(), + * embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void* embb_alloc_aligned( + size_t alignment, + /**< [IN] Alignment in bytes */ + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Allocates \p size bytes of cache-aligned memory. + * + * Specialized version of embb_alloc_aligned(). The alignment is chosen + * automatically (usually 64 bytes). + * + * Keeps track of allocated memory in debug mode. + * + * \post The returned pointer is a multiple of the cache line size. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \memory See embb_alloc_aligned() + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * embb_free_aligned(). + * + * \see embb_alloc_aligned(), embb_free_aligned(), embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void* embb_alloc_cache_aligned( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by an aligned method for \c ptr. + * + * The available aligned methods are embb_alloc_aligned() or + * embb_alloc_cache_aligned(). + * + * Keeps track of freed memory in debug mode. + * + * \pre \c ptr is not NULL and was allocated by an aligned method. + * + * \threadsafe + * + * \see embb_alloc_aligned(), embb_alloc_cache_aligned(), + * embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void embb_free_aligned( + void* ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Returns the total number of bytes currently allocated. + * + * Only the bytes allocated by embb_alloc(), embb_alloc_aligned(), and + * embb_alloc_cache_aligned() in debug mode are counted. + * + * \return Number of currently allocated bytes in debug mode, otherwise 0. + * + * \waitfree + * + * \see embb_alloc(), embb_alloc_aligned(), embb_alloc_cache_aligned() + * + * \ingroup C_BASE_ALLOC + */ + size_t embb_get_bytes_allocated(); +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // EMBB_BASE_C_MEMORY_ALLOCATION_H_ diff --git b/base_c/include/embb/base/c/mutex.h a/base_c/include/embb/base/c/mutex.h new file mode 100644 index 0000000..ee61c13 --- /dev/null +++ a/base_c/include/embb/base/c/mutex.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_MUTEX_H_ +#define EMBB_BASE_C_MUTEX_H_ + +/** + * \defgroup C_BASE_MUTEX Mutex + * + * Mutexes for thread synchronization + * + * Provides an abstraction from platform-specific mutex implementations. + * Plain and recursive mutexes are available, where the plain version can + * only be locked once by the same thread. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing a mutex. + */ +typedef opaque_type embb_mutex_t; + +/** + * Opaque type representing a spinlock. + */ +typedef opaque_type embb_spinlock_t; +#else +/** + * Spinlock type, treat as opaque. + */ +typedef struct { + embb_atomic_int atomic_spin_variable_; +} embb_spinlock_t; +#endif /* DOXYGEN */ + +/** + * Types of mutexes to be used in embb_mutex_init() + */ +enum { + EMBB_MUTEX_PLAIN, + /**< Mutex can be locked only once by the same thread. */ + EMBB_MUTEX_RECURSIVE + /**< Mutex can be locked recursively by the same thread. */ +}; + +/** + * Initializes a mutex + * + * \post \c mutex is initialized + * \return EMBB_SUCCESS if mutex could be initialized \n + * EMBB_ERROR otherwise + * \memory (Potentially) allocates dynamic memory + * \notthreadsafe + * \see embb_mutex_destroy() + */ +int embb_mutex_init( + embb_mutex_t* mutex, + /**< [OUT] Pointer to mutex */ + int type + /**< [IN] EMBB_MUTEX_PLAIN or EMBB_MUTEX_RECURSIVE. There is no guarantee + that a mutex is non-recursive if the plain type is given. */ + ); + +/** + * Waits until the mutex can be locked and locks it. + * + * \pre \c mutex is initialized \n + * If the mutex type is plain, \c mutex must not be locked by the current + * thread. + * \post If successful, \c mutex is locked. + * \return EMBB_SUCCESS if mutex could be locked \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_mutex_try_lock(), embb_mutex_unlock() + */ +int embb_mutex_lock( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Tries to lock the mutex and returns immediately. + * + * \pre \c mutex is initialized + * \post If successful, \c mutex is locked + * + * \return EMBB_SUCCESS if mutex could be locked \n + * EMBB_BUSY if mutex could not be locked \n + * EMBB_ERROR if an error occurred + * \threadsafe + * \see embb_mutex_lock(), embb_mutex_unlock() + */ +int embb_mutex_try_lock( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Unlocks a locked mutex. + * + * \pre \c mutex has been locked by the current thread. + * \post If successful and when the given mutex type is plain, \c mutex is + * unlocked. If its type is recursive, \c mutex is only unlocked if the + * number of successful unlocks has reached the number of successful locks + * done by the current thread. + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_mutex_lock(), embb_mutex_try_lock() + */ +int embb_mutex_unlock( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Destroys a mutex and frees its resources. + * + * \pre \c mutex is initialized and is not NULL. + * \post \c mutex is uninitialized + * \notthreadsafe + * \see embb_mutex_init() + */ +void embb_mutex_destroy( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Initializes a spinlock + * + * \pre \c spinlock is uninitialized + * \post \c spinlock is initialized + * \return EMBB_SUCCESS if spinlock could be initialized \n + * EMBB_ERROR otherwise + * \memory (Potentially) allocates dynamic memory + * \notthreadsafe + * \see embb_spinlock_destroy() + */ +int embb_spin_init( + embb_spinlock_t* spinlock + /**< [OUT] Pointer to spinlock */ + ); + +/** + * Spins until the spinlock can be locked and locks it. + * + * \note This method yields the current thread in regular, + * implementation-defined intervals. + * + * \pre \c spinlock is initialized \n + * \post If successful, \c spinlock is locked. + * \return EMBB_SUCCESS if spinlock could be locked. \n + * EMBB_ERROR if an error occurred. + * \threadsafe + * \see embb_spinlock_try_lock(), embb_mutex_unlock() + */ +int embb_spin_lock( + embb_spinlock_t* spinlock + /**< [IN/OUT] Pointer to spinlock */ + ); + +/** + * Tries to lock the spinlock and returns if not successful. + * + * \pre \c spinlock is initialized + * \post If successful, \c spinlock is locked + * + * \return EMBB_SUCCESS if spinlock could be locked \n + * EMBB_BUSY if spinlock could not be locked \n + * EMBB_ERROR if an error occurred + * \threadsafe + * \see embb_spin_lock(), embb_spin_unlock() + */ +int embb_spin_try_lock( + embb_spinlock_t* spinlock, + /**< [IN/OUT] Pointer to spinlock */ + unsigned int max_number_spins + /**< [IN] Maximal count of spins to perform, trying to acquire lock. Note that + * passing 0 here results in not trying to obtain the lock at all. + */ + ); + +/** + * Unlocks a locked spinlock. + * + * \pre \c spinlock has been locked by the current thread. + * \post If successful, \c spinlock is unlocked. + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_spin_lock(), embb_spin_try_lock() + */ +int embb_spin_unlock( + embb_spinlock_t* spinlock + /**< [IN/OUT] Pointer to spinlock */ + ); + +/** + * Destroys a spinlock and frees its resources. + * + * \pre \c spinlock is initialized and is not NULL. + * \post \c spinlock is uninitialized + * \notthreadsafe + * \see embb_spin_init() + */ +void embb_spin_destroy( + embb_spinlock_t* spinlock + /**< [IN/OUT] Pointer to spinlock */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_MUTEX_H_ */ diff --git b/base_c/include/embb/base/c/thread.h a/base_c/include/embb/base/c/thread.h new file mode 100644 index 0000000..1d7ea11 --- /dev/null +++ a/base_c/include/embb/base/c/thread.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_THREAD_H_ +#define EMBB_BASE_C_THREAD_H_ + +/** + * \defgroup C_BASE_THREADS Thread + * + * Threads supporting thread-to-core affinities. + * + * Provides an abstraction from platform-specific threading implementations to + * create, manage, and join threads of execution. Support for thread-to-core + * affinities is given on thread creation by using the core set functionality. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing a thread of execution. + */ +typedef opaque_type embb_thread_t; +#endif /* DOXYGEN */ + +/** + * Thread start function pointer type. + * + * The return value can be used to return a user-defined exit code when the + * thread is joined. + */ +typedef int(*embb_thread_start_t)(void*); + +/** + * Returns the maximum number of threads handled by EMB2. + * + * The maximum thread number concerns all threads in a program using + * EMB2 functionalities or data structures, regardless of whether + * a thread is started by EMB2 or other threading libraries. + * Each thread that makes use of EMB2 at least once consumes one + * entry in the internal tables. The entry is permanently consumed during a + * program run, even if the thread does not exist any longer. If more threads + * than the maximum thread count access EMB2, undefined behavior or + * abortion of program execution can occur. + * + * \return Maximum number of threads + * + * \lockfree + * + * \see embb_thread_set_max_count() + */ +unsigned int embb_thread_get_max_count(); + +/** + * Sets maximum number of threads handled by EMBB. + * + * It needs to be set before any EMB2 functionalities are used or + * data structures are defined, unless the default value is sufficient. + * + * \notthreadsafe + * \see embb_thread_get_max_count() + */ +void embb_thread_set_max_count( + unsigned int max + /**< [IN] Maximum number of threads */ + ); + +/** + * Returns the calling thread (that is, this thread). + * + * The returned handle is only valid for the thread calling the function. + * + * \return Calling thread + * + * \threadsafe + */ +embb_thread_t embb_thread_current(); + +/** + * Reschedule the current thread for later execution. + * + * This is only a request, the realization depends on the implementation and the + * scheduler employed by the operating system. + * + * \threadsafe + */ +void embb_thread_yield(); + +/** + * Creates and runs a thread. + * + * \pre The given thread is not running and has not yet been successfully + joined. + * \post On success, the given thread has started to run. + * \return EMBB_SUCCESS if the thread could be created. \n + * EMBB_NOMEM if there was insufficient amount of memory \n + * EMBB_ERROR otherwise. + * \memory Dynamically allocates a small constant amount of memory to store the + * function and argument pointers. This memory is freed when the thread + * is joined. + * \notthreadsafe + * \see embb_thread_join() + */ +int embb_thread_create( + embb_thread_t* thread, + /**< [OUT] Thread to be run */ + const embb_core_set_t* core_set, + /**< [IN] Set of cores on which the thread shall be executed. Can be NULL to + indicate automatic thread scheduling by the OS. */ + embb_thread_start_t function, + /**< [IN] Function which is executed by the thread when started. Has to be of + type embb_thread_start_t. */ + void* arg + /**< [IN/OUT] Argument to thread start function. Can be NULL. */ + ); + +/** + * Waits until the given thread has finished execution. + * + * \pre The given thread has been successfully created using + embb_thread_create(). + * \post If successful, the thread has finished execution and all memory + * associated to the thread has been freed. + * \return EMBB_SUCCESS if thread was joined \n + * EMBB_ERROR otherwise + * \notthreadsafe + * \see embb_thread_create() + */ +int embb_thread_join( + embb_thread_t* thread, + /**< [IN/OUT] Thread to be joined */ + int* result_code + /**< [OUT] Memory location (or NULL) for thread result code */ + ); + +/** + * Compares two threads represented by their handles for equality. + * + * \return Non-zero, if equal \n + * 0, otherwise + * \notthreadsafe + */ +int embb_thread_equal( + const embb_thread_t* lhs, + /**< [IN] First thread (left-hand side of equality sign) */ + const embb_thread_t* rhs + /**< [IN] Second thread (right-hand side of equality sign) */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_THREAD_H_ */ diff --git b/base_c/include/embb/base/c/thread_specific_storage.h a/base_c/include/embb/base/c/thread_specific_storage.h new file mode 100644 index 0000000..66bf072 --- /dev/null +++ a/base_c/include/embb/base/c/thread_specific_storage.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_THREAD_SPECIFIC_STORAGE_H_ +#define EMBB_BASE_C_THREAD_SPECIFIC_STORAGE_H_ + +/** + * \defgroup C_BASE_THREADSPECIFIC Thread-Specific Storage + * Thread-specific storage. + * + * Implements thread-specific storage (TSS), that is, memory locations that are + * individual for each thread. Each thread has its own slot for a memory + * address that can point to a (thread-specific) value. The value pointed to + * has to be managed, i.e., created and deleted, by the user. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Opaque type representing a TSS. + */ +#ifdef DOXYGEN +typedef opaque_type embb_tss_t; +#else +typedef struct embb_tss_t { + void** values; + unsigned int size; +} embb_tss_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Creates thread-specific storage (TSS) pointer slots. + * + * \pre The given TSS has not yet been created or has already been deleted. + * \return EMBB_SUCCESS if storage could be created \n + * EMBB_NOMEM if not enough memory was available + * \memory embb_thread_get_max_count() pointers + * \notthreadsafe + * \see embb_tss_delete(), embb_thread_get_max_count() + */ +int embb_tss_create( + embb_tss_t* tss + /**< [OUT] Pointer to TSS */ + ); + +/** + * Sets thread-specific slot value of the current thread. + * + * The value pointed to needs to be managed (created, deleted) by the user. + * + * \pre The given TSS has been created + * \return EMBB_SUCCESS if value could be set \n + * EMBB_ERROR if no thread index could be obtained, that is, the + * maximum number of threads has been exceeded. + * \lockfree + * \see embb_tss_get(), embb_thread_get_max_count() + */ +int embb_tss_set( + embb_tss_t* tss, + /**< [IN/OUT] Pointer to TSS */ + void* value + /**< [IN] Pointer to be stored in TSS */ + ); + +/** + * Gets thread-specific TSS slot value of the current thread. + * + * \pre The given TSS has been created + * \return Thread-specific value if embb_tss_set() has previously been called + * with a valid address. NULL, if no value was set or the calling thread + * could not obtain a thread-specific index. + * \lockfree + * \see embb_tss_set() + */ +void* embb_tss_get( + const embb_tss_t* tss + /**< [IN] Pointer to TSS */ + ); + +/** + * Deletes all slots of the given TSS. + * + * Does not delete the values pointed to. + * + * \pre \c tss has been created successfully and is not NULL. + * \post All slots are deleted + * \notthreadsafe + * \see embb_tss_create() + */ +void embb_tss_delete( + embb_tss_t* tss + /**< [IN/OUT] Pointer to TSS */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_THREAD_SPECIFIC_STORAGE_H_ */ diff --git b/base_c/include/embb/base/c/time.h a/base_c/include/embb/base/c/time.h new file mode 100644 index 0000000..e4f1d18 --- /dev/null +++ a/base_c/include/embb/base/c/time.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_C_TIME_H_ +#define EMBB_BASE_C_TIME_H_ + +/** + * \addtogroup C_BASE_DURATIONTIME + * \{ + * + * \name Time + * \{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Opaque type representing an absolute time point + */ +#ifdef DOXYGEN +typedef opaque_type embb_time_t; +#else +typedef struct embb_time_t { + /** + * Seconds part of time point + */ + unsigned long long seconds; + /** + * Nanoseconds part of time point, smaller than one second + */ + unsigned long nanoseconds; +} embb_time_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Sets time point to now. + * + * \notthreadsafe + * \see embb_time_in() + */ +void embb_time_now( + embb_time_t* time + /**< [OUT] Pointer to time point */ + ); + +/** + * Sets time point to now plus the given duration. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration is smaller than implementation allows \n + * EMBB_OVERFLOW if time + duration is larger than implementation allows + * \notthreadsafe + * \see embb_time_now() + */ +int embb_time_in( + embb_time_t* time, + /**< [OUT] Pointer to time point */ + const embb_duration_t* duration + /**< [IN] Pointer to duration */ + ); + +/** + * Compares two time points. + * + * \pre \c lhs and \c rhs are not NULL and properly initialized. + * + * \return -1 if \c lhs < \c rhs \n + * 0 if \c lhs == \c rhs \n + * 1 if \c lhs > \c rhs + * \notthreadsafe + */ +int embb_time_compare( + const embb_time_t* lhs, + /**< [IN] Pointer to left-hand side operand */ + const embb_time_t* rhs + /**< [IN] Pointer to right-hand side operand */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + * \} + */ + +#endif /* EMBB_BASE_C_TIME_H_ */ diff --git b/base_c/src/atomic.c a/base_c/src/atomic.c new file mode 100644 index 0000000..e8ea812 --- /dev/null +++ a/base_c/src/atomic.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +// Break compiling, if we assumed wrong sizes for types... +// Function should never be called! +void __embb_atomic_internal_compile_time_checks() { + BUILD_BUG_ON(sizeof(char) != EMBB_CHAR_TYPE_SIZE); + BUILD_BUG_ON(sizeof(short) != EMBB_SHORT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned short) != EMBB_UNSIGNED_SHORT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(int) != EMBB_INT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned int) != EMBB_UNSIGNED_INT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(long) != EMBB_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned long) != EMBB_UNSIGNED_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(long long) != EMBB_LONG_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned long long) != EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(intptr_t) != EMBB_INTPTR_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(uintptr_t) != EMBB_UINTPTR_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(size_t) != EMBB_SIZE_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(ptrdiff_t) != EMBB_PTRDIFF_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(uintmax_t) != EMBB_UINTMAX_T_TYPE_SIZE); +} diff --git b/base_c/src/atomicfunc_32.asm a/base_c/src/atomicfunc_32.asm new file mode 100644 index 0000000..3174b40 --- /dev/null +++ a/base_c/src/atomicfunc_32.asm @@ -0,0 +1,160 @@ + .686p + .xmm + .model flat + +_TEXT SEGMENT + +_abort proto + +define_not_implemented macro name + public name + name proc + call _abort + ret + name endp +endm + +; ---- and_assign ------------------------------------------------------------- + +define_and_assign macro name, size, value + public name + name proc + lock and size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_and_assign_8_asm@8 +define_and_assign @embb_internal__atomic_and_assign_4_asm@8, dword, edx +define_and_assign @embb_internal__atomic_and_assign_2_asm@8, word, dx +define_and_assign @embb_internal__atomic_and_assign_1_asm@8, byte, dl + +; ---- compare_and_swap ------------------------------------------------------- + +define_compare_and_swap macro name, size, value, desired + public name + name proc + push edx + mov desired, size ptr [edx] + mov value, size ptr [esp+8] + lock cmpxchg size ptr [ecx], value + pop edx + mov size ptr [edx], desired + setz al + movzx eax, al + ret 4 + name endp +endm + +define_not_implemented @embb_internal__atomic_compare_and_swap_8_asm@12 +define_compare_and_swap @embb_internal__atomic_compare_and_swap_4_asm@12, dword, edx, eax +define_compare_and_swap @embb_internal__atomic_compare_and_swap_2_asm@12, word, dx, ax +define_compare_and_swap @embb_internal__atomic_compare_and_swap_1_asm@12, byte, dl, al + +; ---- fetch_and_add ---------------------------------------------------------- + +define_fetch_and_add macro name, size, value, return + public name + name proc + lock xadd size ptr [ecx], value + mov return, value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_fetch_and_add_8_asm@8 +define_fetch_and_add @embb_internal__atomic_fetch_and_add_4_asm@8, dword, edx, eax +define_fetch_and_add @embb_internal__atomic_fetch_and_add_2_asm@8, word, dx, ax +define_fetch_and_add @embb_internal__atomic_fetch_and_add_1_asm@8, byte, dl, al + +; ---- load ------------------------------------------------------------------- + +define_load macro name, size, return + public name + name proc + mov return, size ptr [ecx] + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_load_8_asm@4 +define_load @embb_internal__atomic_load_4_asm@4, dword, eax +define_load @embb_internal__atomic_load_2_asm@4, word, ax +define_load @embb_internal__atomic_load_1_asm@4, byte, al + +; ---- memory_barrier --------------------------------------------------------- + +define_memory_barrier macro name + public name + name proc + mfence + ret + name endp +endm + +define_memory_barrier @embb_internal__atomic_memory_barrier_asm@0 + +; ---- or_assign -------------------------------------------------------------- + +define_or_assign macro name, size, value + public name + name proc + lock or size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_or_assign_8_asm@8 +define_or_assign @embb_internal__atomic_or_assign_4_asm@8, dword, edx +define_or_assign @embb_internal__atomic_or_assign_2_asm@8, word, dx +define_or_assign @embb_internal__atomic_or_assign_1_asm@8, byte, dl + +; ---- store ------------------------------------------------------------------ + +define_store macro name, size, value + public name + name proc + xchg size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_store_8_asm@8 +define_store @embb_internal__atomic_store_4_asm@8, dword, edx +define_store @embb_internal__atomic_store_2_asm@8, word, dx +define_store @embb_internal__atomic_store_1_asm@8, byte, dl + +; ---- swap ------------------------------------------------------------------- + +define_swap macro name, size, value, return + public name + name proc + lock xchg size ptr [ecx], value + mov return, value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_swap_8_asm@8 +define_swap @embb_internal__atomic_swap_4_asm@8, dword, edx, eax +define_swap @embb_internal__atomic_swap_2_asm@8, word, dx, ax +define_swap @embb_internal__atomic_swap_1_asm@8, byte, dl, al + +; ---- xor_assign ------------------------------------------------------------- + +define_xor_assign macro name, size, value + public name + name proc + lock xor size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_xor_assign_8_asm@8 +define_xor_assign @embb_internal__atomic_xor_assign_4_asm@8, dword, edx +define_xor_assign @embb_internal__atomic_xor_assign_2_asm@8, word, dx +define_xor_assign @embb_internal__atomic_xor_assign_1_asm@8, byte, dl + +_TEXT ENDS + +END diff --git b/base_c/src/atomicfunc_64.asm a/base_c/src/atomicfunc_64.asm new file mode 100644 index 0000000..8e5e658 --- /dev/null +++ a/base_c/src/atomicfunc_64.asm @@ -0,0 +1,153 @@ +_TEXT SEGMENT + +_abort proto + +define_not_implemented macro name + public name + name proc + call _abort + ret + name endp +endm + +; ---- and_assign ------------------------------------------------------------- + +define_and_assign macro name, size, value + public name + name proc + lock and size ptr [rcx], value + ret + name endp +endm + +define_and_assign embb_internal__atomic_and_assign_8_asm, qword, rdx +define_and_assign embb_internal__atomic_and_assign_4_asm, dword, edx +define_and_assign embb_internal__atomic_and_assign_2_asm, word, dx +define_and_assign embb_internal__atomic_and_assign_1_asm, byte, dl + +; ---- compare_and_swap ------------------------------------------------------- + +define_compare_and_swap macro name, size, value, desired + public name + name proc + mov desired, size ptr [rdx] + lock cmpxchg size ptr [rcx], value + mov size ptr [rdx], desired + setz al + movzx rax, al + ret + name endp +endm + +define_compare_and_swap embb_internal__atomic_compare_and_swap_8_asm, qword, r8, rax +define_compare_and_swap embb_internal__atomic_compare_and_swap_4_asm, dword, r8d, eax +define_compare_and_swap embb_internal__atomic_compare_and_swap_2_asm, word, r8w, ax +define_compare_and_swap embb_internal__atomic_compare_and_swap_1_asm, byte, r8b, al + +; ---- fetch_and_add ---------------------------------------------------------- + +define_fetch_and_add macro name, size, value, return + public name + name proc + lock xadd size ptr [rcx], value + mov return, value + ret + name endp +endm + +define_fetch_and_add embb_internal__atomic_fetch_and_add_8_asm, qword, rdx, rax +define_fetch_and_add embb_internal__atomic_fetch_and_add_4_asm, dword, edx, eax +define_fetch_and_add embb_internal__atomic_fetch_and_add_2_asm, word, dx, ax +define_fetch_and_add embb_internal__atomic_fetch_and_add_1_asm, byte, dl, al + +; ---- load ------------------------------------------------------------------- + +define_load macro name, size, return + public name + name proc + mov return, size ptr [rcx] + ret + name endp +endm + +define_load embb_internal__atomic_load_8_asm, qword, rax +define_load embb_internal__atomic_load_4_asm, dword, eax +define_load embb_internal__atomic_load_2_asm, word, ax +define_load embb_internal__atomic_load_1_asm, byte, al + +; ---- memory_barrier --------------------------------------------------------- + +define_memory_barrier macro name + public name + name proc + mfence + ret + name endp +endm + +define_memory_barrier embb_internal__atomic_memory_barrier_asm + +; ---- or_assign -------------------------------------------------------------- + +define_or_assign macro name, size, value + public name + name proc + lock or size ptr [rcx], value + ret + name endp +endm + +define_or_assign embb_internal__atomic_or_assign_8_asm, qword, rdx +define_or_assign embb_internal__atomic_or_assign_4_asm, dword, edx +define_or_assign embb_internal__atomic_or_assign_2_asm, word, dx +define_or_assign embb_internal__atomic_or_assign_1_asm, byte, dl + +; ---- store ------------------------------------------------------------------ + +define_store macro name, size, value + public name + name proc + xchg size ptr [rcx], value + ret + name endp +endm + +define_store embb_internal__atomic_store_8_asm, qword, rdx +define_store embb_internal__atomic_store_4_asm, dword, edx +define_store embb_internal__atomic_store_2_asm, word, dx +define_store embb_internal__atomic_store_1_asm, byte, dl + +; ---- swap ------------------------------------------------------------------- + +define_swap macro name, size, value, return + public name + name proc + lock xchg size ptr [rcx], value + mov return, value + ret + name endp +endm + +define_swap embb_internal__atomic_swap_8_asm, qword, rdx, rax +define_swap embb_internal__atomic_swap_4_asm, dword, edx, eax +define_swap embb_internal__atomic_swap_2_asm, word, dx, ax +define_swap embb_internal__atomic_swap_1_asm, byte, dl, al + +; ---- xor_assign ------------------------------------------------------------- + +define_xor_assign macro name, size, value + public name + name proc + lock xor size ptr [rcx], value + ret + name endp +endm + +define_xor_assign embb_internal__atomic_xor_assign_8_asm, qword, rdx +define_xor_assign embb_internal__atomic_xor_assign_4_asm, dword, edx +define_xor_assign embb_internal__atomic_xor_assign_2_asm, word, dx +define_xor_assign embb_internal__atomic_xor_assign_1_asm, byte, dl + +_TEXT ENDS + +END diff --git b/base_c/src/condition_variable.c a/base_c/src/condition_variable.c new file mode 100644 index 0000000..9a513a2 --- /dev/null +++ a/base_c/src/condition_variable.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +int embb_condition_wait_for(embb_condition_t* condition_var, + embb_mutex_t* mutex, + const embb_duration_t* duration) { + if (condition_var == NULL || mutex == NULL) { + return EMBB_ERROR; + } + embb_time_t time; + int status = embb_time_in(&time, duration); + if (status != EMBB_SUCCESS) { + return status; + } + return embb_condition_wait_until(condition_var, mutex, &time); +} + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +int embb_condition_init(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + InitializeConditionVariable(condition_var); + return EMBB_SUCCESS; +} + +int embb_condition_notify_one(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + WakeConditionVariable(condition_var); + return EMBB_SUCCESS; +} + +int embb_condition_notify_all(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + WakeAllConditionVariable(condition_var); + return EMBB_SUCCESS; +} + +int embb_condition_wait(embb_condition_t* condition_var, + embb_mutex_t* mutex) { + if (condition_var == NULL || mutex == NULL) { + return EMBB_ERROR; + } + if (SleepConditionVariableCS(condition_var, mutex, INFINITE)) { + return EMBB_SUCCESS; + } + return EMBB_ERROR; +} + +int embb_condition_wait_until(embb_condition_t* condition_var, + embb_mutex_t* mutex, const embb_time_t* time) { + if (condition_var == NULL || mutex == NULL || time == NULL) { + return EMBB_ERROR; + } + /* The Windows API needs a time duration, so we need to convert the given time + by using the time now. */ + embb_time_t now; + embb_time_now(&now); + /* Check if absolute timepoint (in milliseconds) still is in the future */ + if ((time->seconds * 1000 + time->nanoseconds / 1000000) + > (now.seconds * 1000 + now.nanoseconds / 1000000)) { + /* Convert to (unsigned type) milliseconds and round up */ + DWORD time_diff = (DWORD) ( + time->seconds * 1000 + time->nanoseconds / 1000000 + - now.seconds * 1000 - now.nanoseconds / 1000000); + if (SleepConditionVariableCS(condition_var, mutex, time_diff) == 0) { + if (GetLastError() == ERROR_TIMEOUT) { + return EMBB_TIMEDOUT; + } else { + return EMBB_ERROR; + } + } + } else { + return EMBB_TIMEDOUT; + } + return EMBB_SUCCESS; +} + +int embb_condition_destroy(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + EMBB_UNUSED_IN_RELEASE(condition_var); + return EMBB_SUCCESS; +} + +#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS + +int embb_condition_init(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + int result = pthread_cond_init(condition_var, NULL); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_notify_one(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + int result = pthread_cond_signal(condition_var); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_notify_all(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + int result = pthread_cond_broadcast(condition_var); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_wait(embb_condition_t* condition_var, embb_mutex_t* mutex) { + if (condition_var == NULL || mutex == NULL) { + return EMBB_ERROR; + } + int result = pthread_cond_wait(condition_var, mutex); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_wait_until(embb_condition_t* condition_var, + embb_mutex_t* mutex, const embb_time_t* time) { + if (condition_var == NULL || mutex == NULL || time == NULL) { + return EMBB_ERROR; + } + /* Convert EMBB time to Unix time format */ + struct timespec unix_time; + unix_time.tv_sec = time->seconds; + unix_time.tv_nsec = time->nanoseconds; + int result = pthread_cond_timedwait(condition_var, mutex, &unix_time); + if (result == ETIMEDOUT) { + return EMBB_TIMEDOUT; + } + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_condition_destroy(embb_condition_t* condition_var) { + if (condition_var == NULL) { + return EMBB_ERROR; + } + int status = pthread_cond_destroy(condition_var); + if (status != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ diff --git b/base_c/src/core_set.c a/base_c/src/core_set.c new file mode 100644 index 0000000..96ecdc0 --- /dev/null +++ a/base_c/src/core_set.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +/** + * For handling of more than 64 logical processors on Windows. + */ +typedef struct processor_info_t { + unsigned short group_count; + unsigned short processor_counts[64]; +} processor_info_t; + +/** + * Holds information about Windows processor groups. + * + * Is initialized on the first call of embb_core_set_init. + * + * !!! Not used at the moment. Results in a limitation of 64 logical processors. + */ +processor_info_t processor_info; + +unsigned int embb_core_count_available() { + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +} + +void embb_core_set_init(embb_core_set_t* core_set, int initializer) { + assert(core_set != NULL); + assert(embb_core_count_available() < 64 && + "Core sets are only supported up to 64 processors!"); + + /* Cache windows processor grouping information */ + if (processor_info.group_count == 0) { + /* Set relation group */ + LOGICAL_PROCESSOR_RELATIONSHIP rel = (LOGICAL_PROCESSOR_RELATIONSHIP)4; + /* Assume only one element of SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX is + returned to the buffer. */ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer; + /* The length is that of the buffer */ + DWORD length = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX); + BOOL status = GetLogicalProcessorInformationEx(rel, &buffer, &length); + assert(status == TRUE); + EMBB_UNUSED_IN_RELEASE(status); + processor_info.group_count = buffer.Group.ActiveGroupCount; + for (unsigned short i = 0; i < processor_info.group_count; i++) { + processor_info.processor_counts[i] = + (unsigned short)(buffer.Group.GroupInfo[i].ActiveProcessorCount); + } + } + + if (initializer == 0) { + embb_bitset_clear_all(&core_set->rep); + } else { + embb_bitset_set_n(&core_set->rep, embb_core_count_available()); + } +} + +#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS + +#ifdef EMBB_PLATFORM_HAS_HEADER_SYSINFO +#include +#elif defined EMBB_PLATFORM_HAS_HEADER_SYSCTL +#include +#include +#endif + +unsigned int embb_core_count_available() { +#ifdef EMBB_PLATFORM_HAS_HEADER_SYSINFO + return get_nprocs(); +#elif defined EMBB_PLATFORM_HAS_HEADER_SYSCTL + const size_t kBufferSize = sizeof(unsigned int); + char buf[kBufferSize]; + size_t len = kBufferSize; + sysctlbyname("hw.ncpu", buf, &len, NULL, 0); + return *(unsigned int*)&buf; +#else +#error "No implementation for embb_core_count_available()!" +#endif +} + +void embb_core_set_init(embb_core_set_t* core_set, int initializer) { + assert(core_set != NULL); + assert(embb_core_count_available() < 64 && + "Core sets are only supported up to 64 processors!"); + if (initializer == 0) { + embb_bitset_clear_all(&core_set->rep); + } else { + embb_bitset_set_n(&core_set->rep, embb_core_count_available()); + } +} + +#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ + +void embb_core_set_add(embb_core_set_t* core_set, unsigned int core_number) { + assert(core_set != NULL); + assert(core_number < embb_core_count_available()); + embb_bitset_set(&core_set->rep, core_number); +} + +void embb_core_set_remove(embb_core_set_t* core_set, unsigned int core_number) { + assert(core_set != NULL); + assert(core_number < embb_core_count_available()); + embb_bitset_clear(&core_set->rep, core_number); +} + +int embb_core_set_contains(const embb_core_set_t* core_set, + unsigned int core_number) { + assert(core_set != NULL); + assert(core_number < embb_core_count_available()); + return (int)(embb_bitset_is_set(&core_set->rep, core_number)); +} + +void embb_core_set_intersection(embb_core_set_t* set1, + const embb_core_set_t* set2) { + assert(set1 != NULL); + assert(set2 != NULL); + embb_bitset_intersect(&set1->rep, set2->rep); +} + +void embb_core_set_union(embb_core_set_t* set1, const embb_core_set_t* set2) { + assert(set1 != NULL); + assert(set2 != NULL); + embb_bitset_union(&set1->rep, set2->rep); +} + +unsigned int embb_core_set_count(const embb_core_set_t* core_set) { + assert(core_set != NULL); + return embb_bitset_count(&core_set->rep); +} diff --git b/base_c/src/counter.c a/base_c/src/counter.c new file mode 100644 index 0000000..7b96fc9 --- /dev/null +++ a/base_c/src/counter.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +int embb_counter_init(embb_counter_t* counter) { + if (counter == NULL) { + return EMBB_ERROR; + } + embb_atomic_store_unsigned_int(&(counter->value), 0); + return EMBB_SUCCESS; +} + +unsigned int embb_counter_get(embb_counter_t* counter) { + assert(counter != NULL); + return embb_atomic_load_unsigned_int(&(counter->value)); +} + +unsigned int embb_counter_increment(embb_counter_t* counter) { + assert(counter != NULL); + return embb_atomic_fetch_and_add_unsigned_int(&(counter->value), 1); +} + +unsigned int embb_counter_decrement(embb_counter_t* counter) { + assert(counter != NULL); + return embb_atomic_fetch_and_add_unsigned_int(&(counter->value), + (unsigned int)-1); +} + +void embb_counter_destroy(embb_counter_t* counter) { + assert(counter != NULL); + EMBB_UNUSED_IN_RELEASE(counter); +} + + diff --git b/base_c/src/duration.c a/base_c/src/duration.c new file mode 100644 index 0000000..84fe9fb --- /dev/null +++ a/base_c/src/duration.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +const embb_duration_t* embb_duration_max() { + static embb_duration_t max = { EMBB_DURATION_MAX_SECONDS, 0 }; + return &max; +} + +const embb_duration_t* embb_duration_min() { + static embb_duration_t min = { 0, EMBB_DURATION_MIN_NANOSECONDS }; + return &min; +} + +const embb_duration_t* embb_duration_zero() { + static embb_duration_t zero = { 0, 0 }; + return &zero; +} + +int embb_duration_set_nanoseconds(embb_duration_t* duration, + unsigned long long nanoseconds) { + if (duration == NULL) { + return EMBB_ERROR; + } + if (nanoseconds > 0) { + if (embb_duration_min()->nanoseconds > nanoseconds) { + duration->seconds = 0; + duration->nanoseconds = 0; + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds * 1000000000 + max->nanoseconds < nanoseconds) { + duration->seconds = max->seconds; + duration->nanoseconds = max->nanoseconds; + return EMBB_OVERFLOW; + } + } + duration->seconds = nanoseconds / 1000000000; + duration->nanoseconds = (nanoseconds % 1000000000); + return EMBB_SUCCESS; +} + +int embb_duration_set_microseconds(embb_duration_t* duration, + unsigned long long microseconds) { + if (duration == NULL) { + return EMBB_ERROR; + } + if (microseconds > 0) { + if (embb_duration_min()->nanoseconds > microseconds*1000) { + duration->seconds = 0; + duration->nanoseconds = 0; + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds * 1000000 + max->nanoseconds / 1000 < microseconds) { + duration->seconds = max->seconds; + duration->nanoseconds = max->nanoseconds; + return EMBB_OVERFLOW; + } + } + duration->seconds = microseconds / 1000000; + duration->nanoseconds = (microseconds % 1000000) * 1000; + return EMBB_SUCCESS; +} + +int embb_duration_set_milliseconds(embb_duration_t* duration, + unsigned long long milliseconds) { + if (duration == NULL) { + return EMBB_ERROR; + } + if (milliseconds > 0) { + if (embb_duration_min()->nanoseconds > milliseconds*1000000) { + duration->seconds = 0; + duration->nanoseconds = 0; + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds * 1000 + max->nanoseconds / 1000000 < milliseconds) { + duration->seconds = max->seconds; + duration->nanoseconds = max->nanoseconds; + return EMBB_OVERFLOW; + } + } + duration->seconds = milliseconds / 1000; + duration->nanoseconds = (milliseconds % 1000) * 1000000; + return EMBB_SUCCESS; +} + +int embb_duration_set_seconds(embb_duration_t* duration, + unsigned long long seconds) { + if (duration == NULL) { + return EMBB_ERROR; + } + if (seconds > 0) { + if (embb_duration_min()->nanoseconds > seconds*1000000000) { + duration->seconds = 0; + duration->nanoseconds = 0; + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds + max->nanoseconds / 1000000000 < seconds) { + duration->seconds = max->seconds; + duration->nanoseconds = max->nanoseconds; + return EMBB_OVERFLOW; + } + } + duration->seconds = seconds; + duration->nanoseconds = 0; + return EMBB_SUCCESS; +} + +int embb_duration_add(embb_duration_t* lhs, const embb_duration_t* rhs) { + if (lhs == NULL || rhs == NULL) { + return EMBB_ERROR; + } + int carry = (int)((lhs->nanoseconds + rhs->nanoseconds) / 1000000000); + if (lhs->seconds + rhs->seconds + carry > EMBB_DURATION_MAX_SECONDS) { + lhs->seconds = 0; + lhs->nanoseconds = 0; + return EMBB_OVERFLOW; + } + lhs->nanoseconds = (lhs->nanoseconds + rhs->nanoseconds) % 1000000000; + lhs->seconds = lhs->seconds + rhs->seconds + carry; + return EMBB_SUCCESS; +} + +int embb_duration_as_nanoseconds(const embb_duration_t* duration, + unsigned long long* nanoseconds) { + if (duration == NULL || nanoseconds == NULL) { + return EMBB_ERROR; + } + if (duration->seconds*1000000000 + duration->nanoseconds > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *nanoseconds = duration->seconds*1000000000 + duration->nanoseconds; + return EMBB_SUCCESS; +} + +int embb_duration_as_microseconds(const embb_duration_t* duration, + unsigned long long* microseconds) { + if (duration == NULL || microseconds == NULL) { + return EMBB_ERROR; + } + if (duration->nanoseconds % 1000 > 0) { + return EMBB_UNDERFLOW; + } + if (duration->seconds*1000000 + duration->nanoseconds/1000 > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *microseconds = duration->seconds*1000000 + duration->nanoseconds / 1000; + return EMBB_SUCCESS; +} + +int embb_duration_as_milliseconds(const embb_duration_t* duration, + unsigned long long* milliseconds) { + if (duration == NULL || milliseconds == NULL) { + return EMBB_ERROR; + } + if (duration->nanoseconds % 1000000 > 0) { + return EMBB_UNDERFLOW; + } + if (duration->seconds*1000 + duration->nanoseconds/1000000 > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *milliseconds = duration->seconds*1000 + duration->nanoseconds / 1000000; + return EMBB_SUCCESS; +} + +int embb_duration_as_seconds(const embb_duration_t* duration, + unsigned long long* seconds) { + if (duration == NULL || seconds == NULL) { + return EMBB_ERROR; + } + if (duration->nanoseconds % 1000000000 > 0) { + return EMBB_UNDERFLOW; + } + if (duration->seconds > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *seconds = duration->seconds; + return EMBB_SUCCESS; +} + +int embb_duration_compare(const embb_duration_t* lhs, + const embb_duration_t* rhs) { + assert(lhs != NULL && rhs != NULL); + assert(lhs->nanoseconds < 1000000000 && rhs->nanoseconds < 1000000000); + if (lhs->seconds > rhs->seconds) { + return 1; + } else if (lhs->seconds < rhs->seconds) { + return -1; + } else { /* Seconds are equal */ + if (lhs->nanoseconds > rhs->nanoseconds) { + return 1; + } else if (lhs->nanoseconds < rhs->nanoseconds) { + return -1; + } + } + return 0; +} diff --git b/base_c/src/internal/thread_index.c a/base_c/src/internal/thread_index.c new file mode 100644 index 0000000..b4c1a75 --- /dev/null +++ a/base_c/src/internal/thread_index.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Provides global variable for maximal number of thread indices. + * + * This function has local scope. + */ +static int embb_max_number_thread_indices_max = 0; +static embb_atomic_int embb_max_number_thread_indices_flag = { 0 }; + +unsigned int* embb_max_number_thread_indices() { + int compare_to = 0; + if (embb_atomic_load_int(&embb_max_number_thread_indices_flag) != 2) { + if (embb_atomic_compare_and_swap_int( + &embb_max_number_thread_indices_flag, &compare_to, 1)) { + embb_max_number_thread_indices_max = + (int)(embb_core_count_available() * 2); + embb_atomic_store_int(&embb_max_number_thread_indices_flag, 2); + } + while (embb_atomic_load_int(&embb_max_number_thread_indices_flag) != 2) {} + } + return (unsigned int*) &embb_max_number_thread_indices_max; +} + +/** + * Provides global variable for current thread index counter. + * + * This function has local scope. + */ +static embb_atomic_int embb_thread_index_counter_flag = { 0 }; +static embb_counter_t embb_thread_index_counter_index; +embb_counter_t* embb_thread_index_counter() { + int compare_to = 0; + if (embb_atomic_load_int(&embb_thread_index_counter_flag) != 2) { + if (embb_atomic_compare_and_swap_int( + &embb_thread_index_counter_flag, &compare_to, 1)) { + embb_counter_init(&embb_thread_index_counter_index); + embb_atomic_store_int(&embb_thread_index_counter_flag, 2); + } + while (embb_atomic_load_int(&embb_thread_index_counter_flag) != 2) {} + } + return &embb_thread_index_counter_index; +} + +/** + * Tries to return the next free (internal) thread index. + * + * An index is only set, if there was still one available. + * + * This function has local scope. + * + * \return EMBB_SUCCESS, if a free index was available. EMBB_ERROR otherwise. + */ +int embb_try_get_next_thread_index(unsigned int* free_index) { + assert(free_index != NULL); + unsigned int index = embb_counter_increment(embb_thread_index_counter()); + if (index >= *embb_max_number_thread_indices()) { + embb_counter_decrement(embb_thread_index_counter()); + return EMBB_ERROR; + } + *free_index = index; + return EMBB_SUCCESS; +} + +/** + * Thread specific thread index. + * + * This variable has local scope. + * + * This index is only used in certain data structures that have to know the + * number of threads and their indices in advance. Only a limited number of + * indices is made available. + */ +EMBB_THREAD_SPECIFIC unsigned int embb_internal_thread_index_var = UINT_MAX; + +int embb_internal_thread_index(unsigned int* index) { + assert(index != NULL); + if (embb_internal_thread_index_var == UINT_MAX) { + int status = + embb_try_get_next_thread_index(&embb_internal_thread_index_var); + if (status == EMBB_ERROR) { + return EMBB_ERROR; + } + } + *index = embb_internal_thread_index_var; + return EMBB_SUCCESS; +} + +int embb_internal_thread_index_max() { + return (int)(*embb_max_number_thread_indices()); +} + +void embb_internal_thread_index_set_max(unsigned int max) { + *embb_max_number_thread_indices() = max; +} + +/** + * \pre the calling thread is the only active thread + * + * \post the thread indices count and calling thread index is reset + */ +void embb_internal_thread_index_reset() { + /** This function is only called in tests, usually when all other threads + * except the main thread have terminated. However, the main thread still has + * potentially stored its old index value in its thread local storage, + * which might be assigned additionally to another thread (as the counter is + * reset), which may lead to hard to detect bugs. Therefore, reset the thread + * local thread id here. + */ + embb_internal_thread_index_var = UINT_MAX; + + embb_counter_init(embb_thread_index_counter()); +} diff --git b/base_c/src/log.c a/base_c/src/log.c new file mode 100644 index 0000000..02e7bf4 --- /dev/null +++ a/base_c/src/log.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include + +#include + +void embb_log_write_file( + void * context, + char const * message) { + assert(context != NULL); + FILE * ff = (FILE*)context; + fprintf(ff, "%s", message); + fflush(ff); +} + +static embb_log_level_t embb_log_global_log_level = + EMBB_LOG_LEVEL_NONE; + +static void * embb_log_global_log_context = NULL; + +static embb_log_function_t embb_log_global_log_function = embb_log_write_file; + +void embb_log_set_log_level( + embb_log_level_t log_level) { + embb_log_global_log_level = log_level; +} + +void embb_log_set_log_function( + void * context, + embb_log_function_t func) { + embb_log_global_log_context = context; + embb_log_global_log_function = func; +} + +void embb_log_write_internal( + char const * channel, + embb_log_level_t log_level, + char const * message, + va_list argp) { + if (log_level <= embb_log_global_log_level) { + char * log_level_str = " "; + char const * channel_str = channel; + void * log_context = embb_log_global_log_context; + if (NULL == channel_str) { + channel_str = " global "; + } + if (NULL == log_context) { + log_context = (void*)stdout; + } + switch (log_level) { + case EMBB_LOG_LEVEL_ERROR: + log_level_str = "ERROR"; + break; + case EMBB_LOG_LEVEL_WARNING: + log_level_str = "WARN "; + break; + case EMBB_LOG_LEVEL_INFO: + log_level_str = "INFO "; + break; + case EMBB_LOG_LEVEL_TRACE: + log_level_str = "TRACE"; + break; + + case EMBB_LOG_LEVEL_NONE: + default: + break; + } +#if defined(EMBB_PLATFORM_COMPILER_MSVC) + char msg_buffer[400]; + char buffer[500]; + vsprintf_s(msg_buffer, sizeof(msg_buffer), message, argp); + sprintf_s(buffer, sizeof(buffer), "[%s] - [%s] %s", + channel_str, log_level_str, msg_buffer); + embb_log_global_log_function(log_context, buffer); +#elif defined(EMBB_PLATFORM_COMPILER_GNUC) + char msg_buffer[400]; + char buffer[500]; + vsnprintf(msg_buffer, sizeof(msg_buffer), message, argp); + snprintf(buffer, sizeof(buffer), "[%s] - [%s] %s", + channel_str, log_level_str, msg_buffer); + embb_log_global_log_function(log_context, buffer); +#else + embb_log_global_log_function(log_context, "["); + embb_log_global_log_function(log_context, channel_str); + embb_log_global_log_function(log_context, "] - ["); + embb_log_global_log_function(log_context, log_level_str); + embb_log_global_log_function(log_context, "] "); + /* no secure formatting possible, sorry */ + embb_log_global_log_function(log_context, message); +#endif + } +} + +void embb_log_write( + char const * channel, + embb_log_level_t log_level, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, log_level, message, argp); + va_end(argp); +} + +#ifdef EMBB_DEBUG +void embb_log_trace( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_TRACE, message, argp); + va_end(argp); +} + +void embb_log_info( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_INFO, message, argp); + va_end(argp); +} +#endif + +void embb_log_warning( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_WARNING, message, argp); + va_end(argp); +} + +void embb_log_error( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_ERROR, message, argp); + va_end(argp); +} diff --git b/base_c/src/memory_allocation.c a/base_c/src/memory_allocation.c new file mode 100644 index 0000000..46f2828 --- /dev/null +++ a/base_c/src/memory_allocation.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef EMBB_DEBUG + +static embb_atomic_long embb_bytes_allocated = { 0 }; + +enum { + // Make the marking unlikely to be something else + INVALID_ALLOCATION = 0x91919191, + ALIGNED_ALLOCATION = 0x99AABB11, + UNALIGNED_ALLOCATION = 0x11AABB99 +}; + +void* embb_alloc(size_t bytes) { + size_t bytes_to_allocate = 2 * sizeof(size_t)+bytes; + void* allocated = malloc(bytes_to_allocate); + + if (allocated == NULL) + return NULL; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)bytes_to_allocate); + + size_t* x_as_size_type = (size_t*)allocated; + + x_as_size_type[1] = UNALIGNED_ALLOCATION; + x_as_size_type[0] = (size_t)bytes_to_allocate; + + // Return the pointer to the payload + return (void*)((size_t*)allocated + 2); +} + +void embb_free(void * ptr) { + assert(ptr != NULL); + + size_t * alloc_type = (size_t*)ptr - 1; + size_t * bytes_allocated = (size_t*)ptr - 2; + + // Check whether this memory was allocated unaligned + assert((*alloc_type) == UNALIGNED_ALLOCATION); + + (*alloc_type) = (size_t)INVALID_ALLOCATION; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)(0 - (size_t)(*bytes_allocated))); + + free((size_t*)ptr - 2); +} + +void* embb_alloc_aligned(size_t alignment, size_t size) { + // In debug mode, we count the number of allocated bytes to be able to detect + // memory leaks. For that purpose, we allocate more memory than necessary for + // the payload. In the extra memory before the payload, we store a pointer to + // the original allocated block, a flag which indicates whether the allocation + // was aligned, and the number of allocated bytes. This requires additional + // 3*sizeof(size_t) bytes of memory (additional_bytes). + // To get at least n aligned sections and additional_bytes before, we need + // the following number of bytes: + // (n+1)*alignment + (additional_bytes-1) + + // n specifies the number of alignment blocks we need for the payload + size_t n = (size + (alignment - 1)) / alignment; + + // additional_bytes specifies the number of bytes we need to store in addition + // to the payload + size_t additional_bytes = sizeof(size_t) * 3; + + size_t bytes_to_allocate = (n + 1)*alignment + (additional_bytes - 1); + + char* allocated = (char *)malloc(bytes_to_allocate); + + if (allocated == NULL) + return NULL; + + // Get the next aligned pointer + char* x = (char*)(((uintptr_t)(allocated+alignment)) & ~(alignment - 1)); + + // If we do not have enough space before, get the next allocated position + while ((x-allocated) < (long)additional_bytes) + x += alignment; + + // Now check if our allocation algorithm worked correctly + // 1. Enough space for the payload + assert((size_t)((allocated + bytes_to_allocate) - x) >= size); + + // 2. x is aligned + assert(((size_t)x % alignment) == 0); + + // 3. Enough space for the additional information + assert((x - additional_bytes) >= allocated); + + // x is now the first aligned position (this is the return value) + size_t* x_as_size_type = (size_t*)x; + + x_as_size_type[-1] = (size_t)ALIGNED_ALLOCATION; + x_as_size_type[-2] = (size_t)allocated; + x_as_size_type[-3] = bytes_to_allocate; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)bytes_to_allocate); + + return x; +} + +void embb_free_aligned(void* ptr) { + assert(ptr != NULL); + + size_t* ptr_conv = (size_t*)ptr; + + // If embb_free_aligned is called, the memory block should have been allocated + // using embb_alloc_aligned. + assert(ptr_conv[-1] == ALIGNED_ALLOCATION); + + ptr_conv[-1] = (size_t)INVALID_ALLOCATION; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)((long)0 - ptr_conv[-3])); + + free((void*)ptr_conv[-2]); +} + +size_t embb_get_bytes_allocated() { + return (size_t)(embb_atomic_load_long(&embb_bytes_allocated)); +} + +#else // EMBB_DEBUG + +void * embb_alloc(size_t bytes) { + return malloc(bytes); +} + +void embb_free(void * ptr) { + assert(ptr != NULL); + free(ptr); +} + +void *embb_alloc_aligned(size_t alignment, size_t size) { + void* malloc_addr = NULL; +#ifdef EMBB_PLATFORM_COMPILER_MSVC + /* + * From the Documentation: + * Allocates memory on a specified alignment boundary. + * + * Return: + * A pointer to the memory block that was allocated or NULL if the operation + * failed. The pointer is a multiple of alignment. + */ + malloc_addr = _aligned_malloc(size, alignment); +#elif defined EMBB_PLATFORM_COMPILER_GNUC + /* + * From the Documentation: + * The posix_memalign() function shall allocate size bytes aligned on a + * boundary specified by alignment, and shall return a pointer to the + * allocated memory in memptr. The value of alignment shall be a multiple + * of sizeof(void *), that is also a power of two. Upon successful + * completion, the value pointed to by memptr shall be a multiple of + * alignment. + */ + int status = posix_memalign(&malloc_addr, alignment, size); + EMBB_UNUSED(status); +#endif + return malloc_addr; +} + +void embb_free_aligned(void* ptr) { + assert(ptr != NULL); + +#ifdef EMBB_PLATFORM_COMPILER_MSVC + _aligned_free(ptr); +#else +#ifdef EMBB_PLATFORM_COMPILER_GNUC + free(ptr); +#else +#error Unsupported compiler +#endif +#endif +} + +size_t embb_get_bytes_allocated() { + return 0; +} + +#endif + +void *embb_alloc_cache_aligned(size_t size) { + return embb_alloc_aligned(EMBB_PLATFORM_CACHE_LINE_SIZE, size); +} diff --git b/base_c/src/mutex.c a/base_c/src/mutex.c new file mode 100644 index 0000000..5ccb0ab --- /dev/null +++ a/base_c/src/mutex.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +int embb_mutex_init(embb_mutex_t* mutex, int type) { + if (NULL == mutex) { + return EMBB_ERROR; + } + /* Critical sections in Windows are always recursive */ + InitializeCriticalSection(mutex); + EMBB_UNUSED(type); + return EMBB_SUCCESS; +} + +int embb_mutex_lock(embb_mutex_t* mutex) { + if (NULL == mutex) { + return EMBB_ERROR; + } + EnterCriticalSection(mutex); + return EMBB_SUCCESS; +} + +int embb_mutex_try_lock(embb_mutex_t* mutex) { + if (NULL == mutex) { + return EMBB_ERROR; + } + BOOL success; + success = TryEnterCriticalSection(mutex); + if (success == FALSE) return EMBB_ERROR; + return EMBB_SUCCESS; +} + +int embb_mutex_unlock(embb_mutex_t* mutex) { + if (NULL == mutex) { + return EMBB_ERROR; + } + LeaveCriticalSection(mutex); + return EMBB_SUCCESS; +} + +void embb_mutex_destroy(embb_mutex_t* mutex) { + assert(NULL != mutex); + DeleteCriticalSection(mutex); +} + +#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS + +int embb_mutex_init(embb_mutex_t* mutex, int type) { + if (NULL == mutex) { + return EMBB_ERROR; + } + if (type == EMBB_MUTEX_PLAIN) { + if (pthread_mutex_init(mutex, NULL) != 0) return EMBB_ERROR; + } else { + assert(type == EMBB_MUTEX_RECURSIVE); + pthread_mutexattr_t attributes; + if (pthread_mutexattr_init(&attributes) != 0) return EMBB_ERROR; + if (pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) { + pthread_mutexattr_destroy(&attributes); + return EMBB_ERROR; + } + if (pthread_mutex_init(mutex, &attributes) != 0) { + pthread_mutexattr_destroy(&attributes); + return EMBB_ERROR; + } + if (pthread_mutexattr_destroy(&attributes) != 0) return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_mutex_lock(embb_mutex_t* mutex) { + if (NULL == mutex) { + return EMBB_ERROR; + } + int result = pthread_mutex_lock(mutex); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_mutex_try_lock(embb_mutex_t* mutex) { + if (NULL == mutex) { + return EMBB_ERROR; + } + int result = pthread_mutex_trylock(mutex); + if (result == 0) { + return EMBB_SUCCESS; + } + if (result == EBUSY) { + return EMBB_BUSY; + } + return EMBB_ERROR; +} + +int embb_mutex_unlock(embb_mutex_t* mutex) { + if (NULL == mutex) { + return EMBB_ERROR; + } + int result = pthread_mutex_unlock(mutex); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +void embb_mutex_destroy(embb_mutex_t* mutex) { + assert(NULL != mutex); + pthread_mutex_destroy(mutex); +} + +#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ + +int embb_spin_init(embb_spinlock_t* spinlock) { + if (NULL == spinlock) { + return EMBB_ERROR; + } + // For now, store the initial value. In the future will use atomic init + // function (as soon as available). + embb_atomic_store_int(&spinlock->atomic_spin_variable_, 0); + return EMBB_SUCCESS; +} + +int embb_spin_lock(embb_spinlock_t* spinlock) { + if (NULL == spinlock) { + return EMBB_ERROR; + } + int expected = 0; + int spins = 1; + + // try to swap the + while (0 == embb_atomic_compare_and_swap_int( + &spinlock->atomic_spin_variable_, &expected, 1)) { + if (0 == (spins & 1023)) { + embb_thread_yield(); + } + spins++; + // reset expected, as CAS might change it... + expected = 0; + } + return EMBB_SUCCESS; +} + +int embb_spin_try_lock(embb_spinlock_t* spinlock, + unsigned int max_number_spins) { + if (NULL == spinlock) { + return EMBB_ERROR; + } + if (max_number_spins == 0) + return EMBB_BUSY; + + int expected = 0; + while (0 == embb_atomic_compare_and_swap_int( + &spinlock->atomic_spin_variable_, + &expected, 1)) { + max_number_spins--; + if (0 == max_number_spins) { + return EMBB_BUSY; + } + expected = 0; + } + + return EMBB_SUCCESS; +} + +int embb_spin_unlock(embb_spinlock_t* spinlock) { + if (NULL == spinlock) { + return EMBB_ERROR; + } + int expected = 1; + return embb_atomic_compare_and_swap_int(&spinlock->atomic_spin_variable_, + &expected, 0) ? + EMBB_SUCCESS : EMBB_ERROR; +} + +void embb_spin_destroy(embb_spinlock_t* spinlock) { + assert(NULL != spinlock); + // for now, doing nothing here... in future, will call the respective + // destroy function for atomics... + EMBB_UNUSED(spinlock); +} diff --git b/base_c/src/thread.c a/base_c/src/thread.c new file mode 100644 index 0000000..f88b2ab --- /dev/null +++ a/base_c/src/thread.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +unsigned int embb_thread_get_max_count() { + return (unsigned int)(embb_internal_thread_index_max()); +} + +void embb_thread_set_max_count(unsigned int max) { + embb_internal_thread_index_set_max(max); +} + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +/** + * Used to wrap client thread start function and argument when calling internal + * thread start function embb_internal_thread_start. + */ +typedef struct embb_internal_thread_arg_t { + embb_thread_start_t func; + void* arg; +} embb_internal_thread_arg_t; + +/** + * Used to offer a consistent thread start function signature. Windows threads + * have a different signature than Pthreads and C11. This internal start + * function for Windows threads just calls the client start function with the + * given argument. + */ +DWORD WINAPI embb_internal_thread_start(LPVOID internalArg) { + int result = ((embb_internal_thread_arg_t*)internalArg)->func( + ((embb_internal_thread_arg_t*)internalArg)->arg); +#if !defined(__cplusplus) + ExitThread((DWORD)result); /* In C, returning the result code doesn't work */ +#else + return (DWORD)result; +#endif +} + +embb_thread_t embb_thread_current() { + embb_thread_t thread; + thread.embb_internal_handle = GetCurrentThread(); + thread.embb_internal_arg = NULL; + return thread; +} + +void embb_thread_yield() { + SwitchToThread(); +} + +int embb_thread_create(embb_thread_t* thread, const embb_core_set_t* core_set, + embb_thread_start_t func, void *arg) { + if (thread == NULL) { + return EMBB_ERROR; + } + thread->embb_internal_arg = (embb_internal_thread_arg_t*) + embb_alloc(sizeof(embb_internal_thread_arg_t)); + if (thread->embb_internal_arg == NULL) { + thread->embb_internal_handle = NULL; + return EMBB_NOMEM; + } + thread->embb_internal_arg->func = func; + thread->embb_internal_arg->arg = arg; + + thread->embb_internal_handle = CreateThread( + 0, /* no security */ + 0, /* default stack size */ + embb_internal_thread_start, /* entry function */ + (LPVOID)thread->embb_internal_arg, /* parameters */ + 0, /* no creation arguments */ + 0); /* no system thread ID */ + if (thread->embb_internal_handle == NULL) { + embb_free(thread->embb_internal_arg); + thread->embb_internal_arg = NULL; + return EMBB_ERROR; + } + + if (core_set != NULL) { /* Set thread affinity, if a core set is given */ + DWORD_PTR core_mask = 0; + DWORD bit_mask = 1; + assert(embb_core_count_available() < 64); + for (unsigned int i = 0; i < embb_core_count_available(); i++) { + if (embb_core_set_contains(core_set, i)) { + core_mask |= bit_mask; + } + bit_mask <<= 1; + } + if (SetThreadAffinityMask(thread->embb_internal_handle, core_mask) + == (DWORD_PTR)NULL) { + return EMBB_ERROR; + } + } + + return EMBB_SUCCESS; +} + +int embb_thread_join(embb_thread_t* thread, int* result_code) { + if (thread == NULL) { + return EMBB_ERROR; + } + BOOL success; + DWORD result; + result = WaitForSingleObject(thread->embb_internal_handle, INFINITE); + embb_free(thread->embb_internal_arg); + if (result != WAIT_OBJECT_0) { + /* WAIT_OBJECT_0 indicates successful waiting */ + return EMBB_ERROR; + } + if (result_code != NULL) { /* != NULL means the client wants a result code */ + if (GetExitCodeThread(thread->embb_internal_handle, &result) != 0) { + *result_code = (int)result; + } else { + *result_code = 0; /* Error on obtaining result code */ + return EMBB_ERROR; + } + } + success = CloseHandle(thread->embb_internal_handle); + if (success == FALSE) { + return EMBB_ERROR; + } + /*return embb_internal_thread_counter_try_decrement();*/ + return EMBB_SUCCESS; +} + +int embb_thread_equal(const embb_thread_t* lhs, const embb_thread_t* rhs) { + if (lhs == NULL || rhs == NULL) { + return 0; + } + embb_thread_id_t idLhs = GetThreadId(lhs->embb_internal_handle); + embb_thread_id_t idRhs = GetThreadId(rhs->embb_internal_handle); + if (idLhs == idRhs) { + return 1; + } + return 0; +} + +#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS + +#ifdef EMBB_PLATFORM_HAS_GLIB_CPU +#include +#elif defined EMBB_PLATFORM_HAS_HEADER_CPUSET +#include +#include +#include +#endif + +#ifdef EMBB_PLATFORM_HAS_HEADER_SYSINFO +#include /* Used to get number of processors */ +#endif /* EMBB_PLATFORM_HAS_HEADER_SYSINFO */ + +/** + * Used to wrap client thread start function and argument when calling internal + * thread start function embb_internal_thread_start. + */ +typedef struct embb_internal_thread_arg_t { + embb_thread_start_t func; + void* arg; + int result; +} embb_internal_thread_arg_t; + +/** + * Used to offer a consistent thread start function signature. POSIX threads + * have a different signature than C11 threads. This internal start function + * for POSIX threads just calls the client start function with the given + * argument. + */ +void* embb_internal_thread_start(void* internalArg) { + ((embb_internal_thread_arg_t*)internalArg)->result = + ((embb_internal_thread_arg_t*)internalArg)->func( + ((struct embb_internal_thread_arg_t*)internalArg)->arg); + return NULL; +} + +embb_thread_t embb_thread_current() { + embb_thread_t thread; + thread.embb_internal_handle = pthread_self(); + thread.embb_internal_arg = NULL; + return thread; +} + +void embb_thread_yield() { + pthread_yield(); +} + +int embb_thread_create(embb_thread_t* thread, const embb_core_set_t* core_set, + embb_thread_start_t func, void* arg) { + if (thread == NULL) { + return EMBB_ERROR; + } + pthread_attr_t attr; /* Used to set thread affinities */ + int status = pthread_attr_init(&attr); + if (status != 0) return EMBB_ERROR; + if (core_set != NULL) { +#if defined(EMBB_PLATFORM_HAS_GLIB_CPU) || \ + defined(EMBB_PLATFORM_HAS_HEADER_CPUSET) + assert(embb_core_count_available() < CPU_SETSIZE && + "Core sets are only supported up to CPU_SETSIZE processors!"); +#ifdef EMBB_PLATFORM_HAS_GLIB_CPU + cpu_set_t cpuset; +#else + cpuset_t cpuset; +#endif + CPU_ZERO(&cpuset); /* Disable all processors */ + for (unsigned int i = 0; i < embb_core_count_available(); i++) { + if (embb_core_set_contains(core_set, i)) { + CPU_SET(i, &cpuset); + } + } + status = pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset); + if (status != 0) { + thread->embb_internal_arg = NULL; + thread->embb_internal_handle = 0; + return EMBB_ERROR; + } +#else + embb_log_write("base_c", EMBB_LOG_LEVEL_WARNING, "Could not set thread " + "affinity, since no implementation available!\n"); +#endif + } + + /* Dynamic allocation of thread arguments. Freed on call of join. */ + thread->embb_internal_arg = (embb_internal_thread_arg_t*) + embb_alloc(sizeof(embb_internal_thread_arg_t)); + if (thread->embb_internal_arg == NULL) { + thread->embb_internal_handle = 0; + pthread_attr_destroy(&attr); + return EMBB_NOMEM; + } + thread->embb_internal_arg->func = func; + thread->embb_internal_arg->arg = arg; + + status = pthread_create( + &(thread->embb_internal_handle), /* pthread handle */ + &attr, /* additional attributes, + e.g., affinities */ + embb_internal_thread_start, /* thread start function */ + (void*)(thread->embb_internal_arg)); /* arguments to thread start func. */ + if (status != 0) return EMBB_ERROR; + + status = pthread_attr_destroy(&attr); + if (status != 0) return EMBB_ERROR; + return EMBB_SUCCESS; +} + +int embb_thread_join(embb_thread_t* thread, int *result_code) { + if (thread == NULL) { + return EMBB_ERROR; + } + int status = 0; + status = pthread_join(thread->embb_internal_handle, NULL); + if (thread->embb_internal_arg != NULL) { + if (result_code != NULL) { + *result_code = thread->embb_internal_arg->result; + } + embb_free(thread->embb_internal_arg); + } + if (status != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_thread_equal(const embb_thread_t* lhs, const embb_thread_t* rhs) { + if (lhs == NULL || rhs == NULL) { + return 0; + } + return pthread_equal(lhs->embb_internal_handle, rhs->embb_internal_handle); +} + +#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ diff --git b/base_c/src/thread_specific_storage.c a/base_c/src/thread_specific_storage.c new file mode 100644 index 0000000..40a0fc3 --- /dev/null +++ a/base_c/src/thread_specific_storage.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +int embb_tss_create(embb_tss_t* tss) { + if (tss == NULL) { + return EMBB_ERROR; + } + tss->size = embb_thread_get_max_count(); + tss->values = (void**) embb_alloc_cache_aligned(tss->size * sizeof(void*)); + if (tss->values == NULL) { + return EMBB_NOMEM; + } + for (unsigned int i = 0; i < tss->size; i++) { + tss->values[i] = NULL; + } + return EMBB_SUCCESS; +} + +int embb_tss_set(embb_tss_t* tss, void* value) { + if (tss == NULL) { + return EMBB_ERROR; + } + unsigned int index = 0; + int status = embb_internal_thread_index(&index); + if ((status != EMBB_SUCCESS) || (index >= tss->size)) { + return EMBB_ERROR; + } + tss->values[index] = value; + return EMBB_SUCCESS; +} + +void* embb_tss_get(const embb_tss_t* tss) { + if (tss == NULL) { + return NULL; + } + if (tss->values == NULL) { + return NULL; + } + unsigned int index = 0; + int status = embb_internal_thread_index(&index); + if ((status != EMBB_SUCCESS) || (index >= tss->size)) { + return NULL; + } + return tss->values[index]; +} + +void embb_tss_delete(embb_tss_t* tss) { + assert(tss != NULL); + if (tss->values != NULL) { + embb_free_aligned(tss->values); + } +} diff --git b/base_c/src/time.c a/base_c/src/time.c new file mode 100644 index 0000000..0b0d3fe --- /dev/null +++ a/base_c/src/time.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +void embb_time_now(embb_time_t* time) { + embb_time_in(time, embb_duration_zero()); +} + +int embb_time_compare(const embb_time_t* lhs, const embb_time_t* rhs) { + assert(lhs != NULL && rhs != NULL); + assert(lhs->nanoseconds < 1000000000 && rhs->nanoseconds < 1000000000); + if (lhs->seconds > rhs->seconds) { + return 1; + } else if (lhs->seconds < rhs->seconds) { + return -1; + } else { /* Seconds are equal */ + if (lhs->nanoseconds > rhs->nanoseconds) { + return 1; + } else if (lhs->nanoseconds < rhs->nanoseconds) { + return -1; + } + } + return 0; +} + + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +int embb_time_in(embb_time_t* time, const embb_duration_t* duration) { + if (time == NULL || duration == NULL) { + return EMBB_ERROR; + } + /* Get system time */ + SYSTEMTIME system_time; + GetLocalTime(&system_time); + /* Convert system time to file time */ + FILETIME file_time; + SystemTimeToFileTime(&system_time, &file_time); /* Has 100 nanosec. ticks */ + /* Convert file time to windows unsigned large integer union type */ + ULARGE_INTEGER win_time; + win_time.LowPart = file_time.dwLowDateTime; + win_time.HighPart = file_time.dwHighDateTime; + /* Convert windows time type to EMBB type */ + time->seconds = win_time.QuadPart / 10000000; + time->nanoseconds = win_time.QuadPart % 10000000; + /* Add duration to time */ + int carry = (int)((time->nanoseconds + duration->nanoseconds) / 1000000000); + if (time->seconds + duration->seconds + carry > EMBB_TIME_MAX_SECONDS) { + return EMBB_OVERFLOW; + } + time->seconds += duration->seconds + carry; + time->nanoseconds += duration->nanoseconds - carry * 1000000000; + return EMBB_SUCCESS; +} + +#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ + + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS + +int embb_time_in(embb_time_t* time, const embb_duration_t* duration) { + if (time == NULL || duration == NULL) { + return EMBB_ERROR; + } + struct timespec unix_time; + clock_gettime(CLOCK_REALTIME, &unix_time); + time->seconds = unix_time.tv_sec; + time->nanoseconds = unix_time.tv_nsec; + int carry = (time->nanoseconds + duration->nanoseconds) / 1000000000; + if (time->seconds + duration->seconds + carry > EMBB_TIME_MAX_SECONDS) { + return EMBB_OVERFLOW; + } + time->seconds += duration->seconds + carry; + time->nanoseconds += duration->nanoseconds - carry * 1000000000; + return EMBB_SUCCESS; +} + +#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ + diff --git b/base_c/test/README.txt a/base_c/test/README.txt new file mode 100644 index 0000000..14524d6 --- /dev/null +++ a/base_c/test/README.txt @@ -0,0 +1,4 @@ +There are no direct tests for the C implementation of atomic operations, since +these are extensively tested via the C++ wrappers. See files atomic_tests.h +and atomic_tests.cc in folder embb\base_cpp\test. + diff --git b/base_c/test/alloc_test.cc a/base_c/test/alloc_test.cc new file mode 100644 index 0000000..50c3b46 --- /dev/null +++ a/base_c/test/alloc_test.cc @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +AllocTest::AllocTest() + : allocated_(partest::TestSuite::GetDefaultNumThreads()) { + CreateUnit("Non-aligned") + .Add(&AllocTest::TestNonAlignedAlloc, this, + partest::TestSuite::GetDefaultNumThreads()) + .Post(&AllocTest::PostNonAligned, this); + + CreateUnit("Aligned") + .Add(&AllocTest::TestAlignedAlloc, this, + partest::TestSuite::GetDefaultNumThreads()) + .Post(&AllocTest::PostAligned, this); + + CreateUnit("Cache aligned") + .Add(&AllocTest::TestCacheAlignedAlloc, this, + partest::TestSuite::GetDefaultNumThreads()) + .Post(&AllocTest::PostCacheAligned, this); + + CreateUnit("AccessAllocatedMemory") + .Add(&AllocTest::TestAccessAllocatedMemory, this) + .Post(&AllocTest::PostAccessAllocatedMemory, this); + + CreateUnit("Mixed").Add(&AllocTest::TestMixedAllocs, this); +} + +void AllocTest::TestAccessAllocatedMemory() { + static const unsigned int memory_allocation_iterations = 1000; + + ::std::vector allocs_aligned; + ::std::vector allocs_unaligned; + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + allocs_aligned.push_back(embb_alloc_cache_aligned(8)); + allocs_unaligned.push_back(embb_alloc(8)); + } + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + memcpy(allocs_aligned[i], &i, sizeof(int)); + memcpy(allocs_unaligned[i], &i, sizeof(int)); + } + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + unsigned int alloc_aligned_test; + unsigned int alloc_unaligned_test; + memcpy(&alloc_aligned_test, allocs_aligned[i], sizeof(i)); + memcpy(&alloc_unaligned_test, allocs_unaligned[i], sizeof(i)); + + PT_EXPECT_EQ(alloc_aligned_test, i); + if (alloc_aligned_test != i) { + PT_ASSERT(false); + } + if (alloc_unaligned_test != i) { + PT_ASSERT(false); + } + } + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + embb_free(allocs_unaligned[i]); + embb_free_aligned(allocs_aligned[i]); + } +} + +void AllocTest::PostAccessAllocatedMemory() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestNonAlignedAlloc() { + size_t thread_num = + partest::TestSuite::GetCurrentThreadID(); + assert(thread_num < allocated_.size()); + allocated_[thread_num] = NULL; + allocated_[thread_num] = embb_alloc(1); + PT_EXPECT_NE(allocated_[thread_num], static_cast(NULL)); + embb_free(allocated_[thread_num]); +} + +void AllocTest::PostNonAligned() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestAlignedAlloc() { + size_t thread_num = + partest::TestSuite::GetCurrentThreadID(); + assert(thread_num < allocated_.size()); + allocated_[thread_num] = NULL; + allocated_[thread_num] = embb_alloc_aligned(2*sizeof(void*), 1); + PT_EXPECT_NE(allocated_[thread_num], static_cast(NULL)); + embb_free_aligned(allocated_[thread_num]); +} + +void AllocTest::PostAligned() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestCacheAlignedAlloc() { + size_t thread_num = + partest::TestSuite::GetCurrentThreadID(); + assert(thread_num < allocated_.size()); + allocated_[thread_num] = NULL; + allocated_[thread_num] = embb_alloc_cache_aligned(1); + PT_EXPECT_NE(allocated_[thread_num], static_cast(NULL)); + embb_free_aligned(allocated_[thread_num]); +} + +void AllocTest::PostCacheAligned() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestMixedAllocs() { + size_t expected = 0; + size_t allocated = 0; + + // Plain memory allocation + void* plain = NULL; + plain = embb_alloc(2); + PT_EXPECT_NE(plain, static_cast(NULL)); + if (NULL == plain) { + return; + } + allocated = embb_get_bytes_allocated(); +#ifdef EMBB_DEBUG + expected += 2 + 2*sizeof(size_t); +#endif // else EMBB_DEBUG + PT_EXPECT_EQ(allocated, expected); + + // Aligned memory allocation + void* aligned = NULL; + aligned = embb_alloc_aligned(2*sizeof(void*), 2); + PT_EXPECT_NE(aligned, static_cast(NULL)); + if (NULL == aligned) { + embb_free(plain); + return; + } + allocated = embb_get_bytes_allocated(); +#ifdef EMBB_DEBUG + expected += (1 + 1) * 2 * sizeof(void*) + 3 * sizeof(size_t) - 1; +#endif // else EMBB_DEBUG + PT_EXPECT_EQ(allocated, expected); + + // Cache-aligned memory allocation + void* cache_aligned = NULL; + cache_aligned = embb_alloc_cache_aligned(2); + PT_EXPECT_NE(cache_aligned, static_cast(NULL)); + if (NULL == cache_aligned) { + embb_free(plain); + embb_free_aligned(aligned); + return; + } + allocated = embb_get_bytes_allocated(); +#ifdef EMBB_DEBUG + expected += (1 + 1) * EMBB_PLATFORM_CACHE_LINE_SIZE + 3 * sizeof(size_t) - 1; +#endif // else EMBB_DEBUG + PT_EXPECT_EQ(allocated, expected); + + embb_free(plain); + embb_free_aligned(aligned); + embb_free_aligned(cache_aligned); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/alloc_test.h a/base_c/test/alloc_test.h new file mode 100644 index 0000000..62ad953 --- /dev/null +++ a/base_c/test/alloc_test.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_ALLOC_TEST_H_ +#define BASE_C_TEST_ALLOC_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class AllocTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + AllocTest(); + + private: + /** + * Tests non-aligned memory allocation and freeing with several threads. + */ + void TestNonAlignedAlloc(); + void PostNonAligned(); + + /** + * Tests aligned memory allocation and freeing with several threads. + */ + void TestAlignedAlloc(); + void PostAligned(); + + /** + * Tests cache aligned memory allocation and freeing with several threads. + */ + void TestCacheAlignedAlloc(); + void PostCacheAligned(); + + /** + * Tests mixed memory allocation in detail using a single thread. + */ + void TestMixedAllocs(); + + /** + * Tests access to allocated memory using a single thread. + */ + void TestAccessAllocatedMemory(); + void PostAccessAllocatedMemory(); + + /** + * Stores the pointer to the allocated memory for each thread. + */ + std::vector allocated_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_ALLOC_TEST_H_ diff --git b/base_c/test/condition_var_test.cc a/base_c/test/condition_var_test.cc new file mode 100644 index 0000000..8688f1b --- /dev/null +++ a/base_c/test/condition_var_test.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace embb { +namespace base { +namespace test { + +ConditionVarTest::ConditionVarTest() +:num_threads_(partest::TestSuite::GetDefaultNumThreads()) { + embb_counter_init(&counter_); + embb_condition_init(&cond_notify_); + embb_mutex_init(&mutex_cond_notify_, EMBB_MUTEX_PLAIN); + embb_condition_init(&cond_wait_); + embb_mutex_init(&mutex_cond_wait_, EMBB_MUTEX_PLAIN); + + CreateUnit("Timed wait timeouts") + .Add(&ConditionVarTest::TestTimedWaitTimeouts, this); + if (num_threads_ >= 2) { + CreateUnit("Condition Notify Test") + .Add(&ConditionVarTest::TestNotify, this, num_threads_); + } else { + std::cout << "Warning: Condition Notify Test needs a minimum of two threads" + << std::endl; + } +} + +void ConditionVarTest::TestNotify() { + size_t threadID = partest::TestSuite::GetCurrentThreadID(); + + if (threadID != 0) { + embb_mutex_lock(&mutex_cond_notify_); + embb_counter_increment(&counter_); + embb_condition_wait(&cond_notify_, &mutex_cond_notify_); + embb_counter_increment(&counter_); + embb_mutex_unlock(&mutex_cond_notify_); + } else { + embb_duration_t duration = EMBB_DURATION_INIT; + embb_duration_set_milliseconds(&duration, 1); + + while (embb_counter_get(&counter_) + < static_cast(num_threads_-1)) + {} // All threads entered critical section + embb_mutex_lock(&mutex_cond_notify_); + embb_mutex_unlock(&mutex_cond_notify_); + // All threads called wait on the condition (even last thread) + + embb_counter_init(&counter_); + + embb_condition_notify_one(&cond_notify_); + embb_mutex_lock(&mutex_cond_wait_); + embb_condition_wait_for(&cond_wait_, &mutex_cond_wait_, &duration); + while (embb_counter_get(&counter_) == 0) + {} // If test hangs here, signalling has not succeeded + PT_ASSERT_EQ_MSG(embb_counter_get(&counter_), static_cast(1), + "Only one thread notified"); + + embb_condition_notify_all(&cond_notify_); + + embb_condition_wait_for(&cond_wait_, &mutex_cond_wait_, &duration); + + while (embb_counter_get(&counter_) != + static_cast(num_threads_-1)) + {} // If test hangs here, not all threads were notified + + embb_mutex_unlock(&mutex_cond_wait_); + embb_mutex_destroy(&mutex_cond_wait_); + embb_mutex_destroy(&mutex_cond_notify_); + embb_condition_destroy(&cond_wait_); + embb_condition_destroy(&cond_notify_); + } +} + + +void ConditionVarTest::TestTimedWaitTimeouts() { + // Set up data structures + embb_condition_t cond; + embb_mutex_t mutex; + embb_condition_init(&cond); + embb_mutex_init(&mutex, EMBB_MUTEX_PLAIN); + embb_time_t time; + embb_duration_t duration = EMBB_DURATION_INIT; + + // Wait for "now" tests already passed time point + embb_time_now(&time); + embb_mutex_lock(&mutex); + int status = embb_condition_wait_until(&cond, &mutex, &time); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Wait for a future time point + status = embb_duration_set_milliseconds(&duration, 1); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_time_in(&time, &duration); // Time now + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_condition_wait_until(&cond, &mutex, &time); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Wait for a zero duration + status = embb_duration_set_milliseconds(&duration, 0); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_condition_wait_for(&cond, &mutex, &duration); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Wait for some duration + status = embb_duration_set_milliseconds(&duration, 1); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_condition_wait_for(&cond, &mutex, &duration); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Tear down data structures + embb_mutex_unlock(&mutex); + embb_mutex_destroy(&mutex); + embb_condition_destroy(&cond); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/condition_var_test.h a/base_c/test/condition_var_test.h new file mode 100644 index 0000000..4224074 --- /dev/null +++ a/base_c/test/condition_var_test.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_CONDITION_VAR_TEST_H_ +#define BASE_C_TEST_CONDITION_VAR_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class ConditionVarTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ConditionVarTest(); + + private: + /** + * Tests timeout of condition wait until and wait for methods. + */ + void TestTimedWaitTimeouts(); + + /** + * Tests if notify works correctly. + * Each thread waits on the same lock. + */ + void TestNotify(); + + size_t num_threads_; + embb_counter_t counter_; + embb_condition_t cond_notify_; + embb_mutex_t mutex_cond_notify_; + embb_condition_t cond_wait_; + embb_mutex_t mutex_cond_wait_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_CONDITION_VAR_TEST_H_ diff --git b/base_c/test/core_set_test.cc a/base_c/test/core_set_test.cc new file mode 100644 index 0000000..f8ab25b --- /dev/null +++ a/base_c/test/core_set_test.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +CoreSetTest::CoreSetTest() { + CreateUnit("Test all").Add(&CoreSetTest::Test, this); +} + +void CoreSetTest::Test() { + embb_core_set_t set; + unsigned int available_cores = embb_core_count_available(); + unsigned int zero_cores = 0; + // Test setting all cores + embb_core_set_init(&set, 0); + unsigned int cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, zero_cores); + embb_core_set_init(&set, 1); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, available_cores); + + // Test setting individual cores + embb_core_set_init(&set, 0); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, zero_cores); + for (unsigned int i = 0; i < available_cores; i++) { + embb_core_set_add(&set, i); + int is_set = embb_core_set_contains(&set, i); + PT_EXPECT_EQ(is_set, 1); + if (i < available_cores - 1) { + is_set = embb_core_set_contains(&set, i+1); + PT_EXPECT_EQ(is_set, 0); + } + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, i+1); + } + + // Test logical & and | operations + embb_core_set_t set2; + embb_core_set_init(&set, 0); + embb_core_set_init(&set2, 1); + embb_core_set_intersection(&set, &set2); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, zero_cores); + embb_core_set_union(&set, &set2); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, available_cores); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git b/base_c/test/core_set_test.h a/base_c/test/core_set_test.h new file mode 100644 index 0000000..33e5996 --- /dev/null +++ a/base_c/test/core_set_test.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_CORE_SET_TEST_H_ +#define BASE_C_TEST_CORE_SET_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class CoreSetTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + CoreSetTest(); + + private: + /** + * Tests all functionalities. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_CORE_SET_TEST_H_ diff --git b/base_c/test/counter_test.cc a/base_c/test/counter_test.cc new file mode 100644 index 0000000..2cba520 --- /dev/null +++ a/base_c/test/counter_test.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +CounterTest::CounterTest() { + CreateUnit("Single threaded API test").Add(&CounterTest::TestBase, this); + CreateUnit(); +} + +void CounterTest::TestBase() { + embb_counter_t counter; + embb_counter_init(&counter); + + unsigned int value = embb_counter_get(&counter); + PT_EXPECT_EQ(value, static_cast(0)); + value = embb_counter_increment(&counter); + PT_EXPECT_EQ(value, static_cast(0)); + value = embb_counter_get(&counter); + PT_EXPECT_EQ(value, static_cast(1)); + value = embb_counter_decrement(&counter); + PT_EXPECT_EQ(value, static_cast(1)); + value = embb_counter_get(&counter); + PT_EXPECT_EQ(value, static_cast(0)); + embb_counter_destroy(&counter); +} + +CounterTest::TestStress::TestStress() + : TestUnit("Stress test for incrementing and decrementing"), counter_() { + size_t num_threads = partest::TestSuite::GetDefaultNumThreads(); + size_t num_iterations = partest::TestSuite::GetDefaultNumIterations(); + Pre(&TestStress::Init, this); + Add(&TestStress::TestCounterIncrement, this, num_threads, num_iterations); + Add(&TestStress::TestCounterDecrement, this, num_threads, num_iterations); + Post(&TestStress::CheckAndDestroyCounter, this); +} + +void CounterTest::TestStress::CheckAndDestroyCounter() { + unsigned int value = embb_counter_get(&counter_); + PT_EXPECT_EQ(value, static_cast(0)); + embb_counter_destroy(&counter_); +} + +} // namespace test +} // namespace base +} // namespace embb + + diff --git b/base_c/test/counter_test.h a/base_c/test/counter_test.h new file mode 100644 index 0000000..9f14bfa --- /dev/null +++ a/base_c/test/counter_test.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_COUNTER_TEST_H_ +#define BASE_C_TEST_COUNTER_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for C counter functionality. + */ +class CounterTest : public partest::TestCase { + public: + /** + * Adds test units. + */ + CounterTest(); + + private: + /** + * Checks the correct return values of inc., dec., and get methods. + */ + void TestBase(); + + /** + * Test repeated incrementing and decrement by several threads. + */ + class TestStress : public partest::TestUnit { + public: + /** + * Adds test methods to unit. + */ + TestStress(); + + private: + /** + * Inits the counter. + */ + void Init() { + embb_counter_init(&counter_); + } + /** + * Increases the counter. + */ + void TestCounterIncrement() { + embb_counter_increment(&counter_); + } + + /** + * Decreases the counter. + */ + void TestCounterDecrement() { + embb_counter_decrement(&counter_); + } + + /** + * Checks the value of the counter and deletes the counter. + */ + void CheckAndDestroyCounter(); + + /** + * Counter used in tests. + */ + embb_counter_t counter_; + }; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_COUNTER_TEST_H_ diff --git b/base_c/test/duration_test.cc a/base_c/test/duration_test.cc new file mode 100644 index 0000000..675ad68 --- /dev/null +++ a/base_c/test/duration_test.cc @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +DurationTest::DurationTest() { + CreateUnit("Compare durations").Add(&DurationTest::TestCompare, this); + CreateUnit("Set zero durations").Add(&DurationTest::TestSetZero, this); + CreateUnit("Set and get").Add(&DurationTest::TestSetAndGet, this); + CreateUnit("Add").Add(&DurationTest::TestAdd, this); +} + +void DurationTest::TestCompare() { + embb_duration_t lhs = EMBB_DURATION_INIT; + embb_duration_t rhs = EMBB_DURATION_INIT; + + int result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 0); + + lhs.seconds = 1; + lhs.nanoseconds = 0; + rhs.seconds = 0; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 1); + + lhs.seconds = 0; + lhs.nanoseconds = 0; + rhs.seconds = 1; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, -1); + + lhs.seconds = 0; + lhs.nanoseconds = 1; + rhs.seconds = 0; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 1); + + lhs.seconds = 0; + lhs.nanoseconds = 0; + rhs.seconds = 0; + rhs.nanoseconds = 1; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, -1); + + lhs.seconds = 1; + lhs.nanoseconds = 0; + rhs.seconds = 1; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 0); + + lhs.seconds = 0; + lhs.nanoseconds = 1; + rhs.seconds = 0; + rhs.nanoseconds = 1; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 0); +} + +void DurationTest::TestSetZero() { + PT_EXPECT_EQ(embb_duration_zero()->seconds, + static_cast(0)); + PT_EXPECT_EQ(embb_duration_zero()->nanoseconds, + static_cast(0)); + + embb_duration_t duration; + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_seconds(&duration, 0); + int result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); + + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_milliseconds(&duration, 0); + result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); + + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_microseconds(&duration, 0); + result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); + + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_nanoseconds(&duration, 0); + result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); +} + +void DurationTest::TestSetAndGet() { + // No underflows are detected here, to be done + + embb_duration_t duration = EMBB_DURATION_INIT; + + unsigned long long ticks = 0; + unsigned long long min_ticks = 0; + embb_duration_as_nanoseconds(embb_duration_min(), &min_ticks); + int result = embb_duration_set_nanoseconds(&duration, min_ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks); + result = embb_duration_as_microseconds(&duration, &ticks); + if (min_ticks / 1000 == 0 || min_ticks % 1000 != 0) { + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + } else { + PT_EXPECT_EQ(ticks, min_ticks / 1000); + } + result = embb_duration_as_milliseconds(&duration, &ticks); + if (min_ticks / 1000000 == 0 || min_ticks % 1000000 != 0) { + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + } else { + PT_EXPECT_EQ(ticks, min_ticks / 1000000); + } + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + + ticks = 0; + embb_duration_as_microseconds(embb_duration_min(), &min_ticks); + result = embb_duration_set_microseconds(&duration, min_ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks*1000); + result = embb_duration_as_microseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks); + result = embb_duration_as_milliseconds(&duration, &ticks); + if (min_ticks / 1000 == 0 || min_ticks % 1000 != 0) { + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + } else { + PT_EXPECT_EQ(ticks, min_ticks / 1000); + } + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + + ticks = 0; + embb_duration_as_milliseconds(embb_duration_min(), &min_ticks); + result = embb_duration_set_milliseconds(&duration, min_ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks*1000000); + result = embb_duration_as_microseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks*1000); + result = embb_duration_as_milliseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks); + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + + ticks = 0; + result = embb_duration_set_seconds(&duration, 1); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1000000000)); + result = embb_duration_as_microseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1000000)); + result = embb_duration_as_milliseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1000)); + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1)); + + ticks = 0; + result = embb_duration_as_nanoseconds(embb_duration_min(), &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_GT(ticks, static_cast(0)); + + ticks = 0; + result = embb_duration_as_seconds(embb_duration_max(), &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_NE(ticks, static_cast(0)); + + embb_duration_as_seconds(embb_duration_max(), &ticks); + ticks++; + result = embb_duration_set_seconds(&duration, ticks); + PT_EXPECT_EQ(result, EMBB_OVERFLOW); +} + +void DurationTest::TestAdd() { + embb_duration_t lhs = EMBB_DURATION_INIT; + embb_duration_t rhs = EMBB_DURATION_INIT; + + int result = embb_duration_add(&lhs, &rhs); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(embb_duration_compare(&lhs, embb_duration_zero()), 0); + + result = embb_duration_set_milliseconds(&rhs, 1); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_add(&lhs, &rhs); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(embb_duration_compare(&lhs, &rhs), 0); + + embb_duration_set_seconds(&lhs, 0); + embb_duration_set_milliseconds(&rhs, 1100); + result = embb_duration_add(&lhs, &rhs); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(lhs.seconds, static_cast(1)); + PT_EXPECT_EQ(lhs.nanoseconds, static_cast(100000000)); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git b/base_c/test/duration_test.h a/base_c/test/duration_test.h new file mode 100644 index 0000000..73aca64 --- /dev/null +++ a/base_c/test/duration_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_DURATION_TEST_H_ +#define BASE_C_TEST_DURATION_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for Base C duration type and methods. + * + * These tests access the internal of embb_duration_t, which is necessary to + * get a starting point without dependencies on not-tested methods. It is + * discouraged in real usage, due to possible changes to the type. + */ +class DurationTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + DurationTest(); + + private: + /** + * Tests compare method. + */ + void TestCompare(); + + /** + * Tests the zero duration and setting a duration to zero with all set methods. + */ + void TestSetZero(); + + /** + * Tests setting and getting values. + */ + void TestSetAndGet(); + + /** + * Tests adding durations. + */ + void TestAdd(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_DURATION_TEST_H_ diff --git b/base_c/test/main.cc a/base_c/test/main.cc new file mode 100644 index 0000000..ad864c8 --- /dev/null +++ a/base_c/test/main.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using embb::base::test::AllocTest; +using embb::base::test::DurationTest; +using embb::base::test::TimeTest; +using embb::base::test::CounterTest; +using embb::base::test::MutexTest; +using embb::base::test::SpinLockTest; +using embb::base::test::ThreadIndexTest; +using embb::base::test::CoreSetTest; +using embb::base::test::ConditionVarTest; +using embb::base::test::ThreadTest; +using embb::base::test::ThreadSpecificStorageTest; + +PT_MAIN("Base C") { + embb_log_set_log_level(EMBB_LOG_LEVEL_WARNING); + unsigned int max_threads = + static_cast(2 * partest::TestSuite::GetDefaultNumThreads()); + embb_thread_set_max_count(max_threads); + + PT_RUN(AllocTest); + PT_RUN(DurationTest); + PT_RUN(TimeTest); + PT_RUN(CounterTest); + PT_RUN(MutexTest); + PT_RUN(SpinLockTest); + PT_RUN(ThreadIndexTest); + PT_RUN(CoreSetTest); + PT_RUN(ConditionVarTest); + PT_RUN(ThreadTest); + PT_RUN(ThreadSpecificStorageTest); + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/base_c/test/mutex_test.cc a/base_c/test/mutex_test.cc new file mode 100644 index 0000000..ed970df --- /dev/null +++ a/base_c/test/mutex_test.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +MutexTest::MutexTest() : counter_(0), + number_threads_(partest::TestSuite::GetDefaultNumThreads()), + number_iterations_(partest::TestSuite::GetDefaultNumIterations()) { + embb_mutex_init(&mutex_, EMBB_MUTEX_PLAIN); + //embb_thread_set_max_count(number_threads_); + CreateUnit("Protected counter") + .Pre(&MutexTest::PreMutexInc, this) + .Add(&MutexTest::TestMutexInc, this, number_threads_, number_iterations_) + .Post(&MutexTest::PostMutexInc, this); + CreateUnit("Recursive mutex") + .Add(&MutexTest::TestRecursiveMutex, this); +} + +void MutexTest::PreMutexInc() { + counter_ = 0; +} + +void MutexTest::TestMutexInc() { + embb_mutex_lock(&mutex_); + ++counter_; + embb_mutex_unlock(&mutex_); +} + +void MutexTest::PostMutexInc() { + PT_EXPECT_EQ(counter_, number_iterations_ * number_threads_); + embb_mutex_destroy(&mutex_); +} + +void MutexTest::TestRecursiveMutex() { + embb_mutex_t mutex; + int status = embb_mutex_init(&mutex, EMBB_MUTEX_RECURSIVE); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + int number = 5; + for (int i = 0; i < number; i++) { + status = embb_mutex_lock(&mutex); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + for (int i = 0; i < number; i++) { + status = embb_mutex_unlock(&mutex); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + embb_mutex_destroy(&mutex); +} + +SpinLockTest::SpinLockTest() : counter_(0), +number_threads_(partest::TestSuite::GetDefaultNumThreads()), + number_iterations_(partest::TestSuite::GetDefaultNumIterations()), + counter_iterations_(10000) { + CreateUnit("Protected counter using Lock") + .Pre(&SpinLockTest::PreSpinLockInc, this) + .Add(&SpinLockTest::TestSpinLockIncUseLock, this, + number_threads_, + number_iterations_) + .Post(&SpinLockTest::PostSpinLockInc, this); + + CreateUnit("Protected counter using TryLock") + .Pre(&SpinLockTest::PreSpinLockInc, this) + .Add(&SpinLockTest::TestSpinLockIncUseTryLock, this, + number_threads_, + number_iterations_) + .Post(&SpinLockTest::PostSpinLockInc, this); + + CreateUnit("Test spinning (too many spins), single thread") + .Add(&SpinLockTest::TestSpinLockTooManySpins, this, + // one thread + 1, + // one iteration + 1); +} + +void SpinLockTest::TestSpinLockTooManySpins() { + embb_spin_init(&spinlock_); + embb_spin_lock(&spinlock_); + + int return_code = embb_spin_try_lock(&spinlock_, 100); + PT_ASSERT(return_code == EMBB_BUSY); + + embb_spin_unlock(&spinlock_); + + return_code = embb_spin_try_lock(&spinlock_, 100); + PT_ASSERT(return_code == EMBB_SUCCESS); + + embb_spin_unlock(&spinlock_); + + embb_spin_destroy(&spinlock_); +} + +void SpinLockTest::PreSpinLockInc() { + embb_spin_init(&spinlock_); +} + +void SpinLockTest::TestSpinLockIncUseLock() { + for (unsigned int i = 0; i != counter_iterations_; ++i) { + embb_spin_lock(&spinlock_); + counter_++; + embb_spin_unlock(&spinlock_); + } +} + +void SpinLockTest::TestSpinLockIncUseTryLock() { + for (unsigned int i = 0; i != counter_iterations_; ++i) { + while (embb_spin_try_lock(&spinlock_, 100) != EMBB_SUCCESS) {} + counter_++; + embb_spin_unlock(&spinlock_); + } +} + +void SpinLockTest::PostSpinLockInc() { + embb_spin_destroy(&spinlock_); + PT_EXPECT_EQ(counter_, number_iterations_ * + number_threads_* + counter_iterations_); + counter_ = 0; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/mutex_test.h a/base_c/test/mutex_test.h new file mode 100644 index 0000000..b049933 --- /dev/null +++ a/base_c/test/mutex_test.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_MUTEX_TEST_H_ +#define BASE_C_TEST_MUTEX_TEST_H_ + +#include +#include +#include + + +namespace embb { +namespace base { +namespace test { +/** + * Provides tests for class Mutex. + */ +class MutexTest : public partest::TestCase { + public: + /** + * Constructs the test case and adds test units. + */ + MutexTest(); + + private: + /** + * Prepares TestMutexIncCpp. + */ + void PreMutexInc(); + /** + * Tests mutex locking and unlocking to protect shared counter. + */ + void TestMutexInc(); + /** + * Checks and tears down TestMutexIncCpp. + */ + void PostMutexInc(); + + /** + * Tests the multiple locking and unlocking of a recursive mutex. + */ + void TestRecursiveMutex(); + + /** + * Mutex for tests. + */ + embb_mutex_t mutex_; + + /** + * Shared counter to check effectiveness of mutex. + */ + size_t counter_; + + /** + * Number of threads used to run tests. + */ + size_t number_threads_; + + /** + * Number of times the test method is called by each thread. + */ + size_t number_iterations_; +}; + + +class SpinLockTest : public partest::TestCase { + public: + SpinLockTest(); + + private: + /** + * Check that the try lock fails, when lock is already set. + */ + void TestSpinLockTooManySpins(); + + /** + * Prepares TestMutexIncCpp. + */ + void PreSpinLockInc(); + + /** + * Tests mutex locking and unlocking to protect shared counter. + */ + void TestSpinLockIncUseLock(); + + /** + * Tests mutex locking and unlocking to protect shared counter using trylock. + */ + void TestSpinLockIncUseTryLock(); + + /** + * Checks and tears down TestMutexIncCpp. + */ + void PostSpinLockInc(); + + /** + * Shared counter to check effectiveness of mutex. + */ + size_t counter_; + + /** + * Number of threads used to run tests. + */ + size_t number_threads_; + + /** + * Number of times the test method is called by each thread. + */ + size_t number_iterations_; + + /** + * Number of internal iterations, for incrementing the counter. + */ + size_t counter_iterations_; + + /** + * The used spinlock + */ + embb_spinlock_t spinlock_; +}; + + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_MUTEX_TEST_H_ diff --git b/base_c/test/thread_index_test.cc a/base_c/test/thread_index_test.cc new file mode 100644 index 0000000..baf2f75 --- /dev/null +++ a/base_c/test/thread_index_test.cc @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace test { + +ThreadIndexTest::ThreadIndexTest() + : number_threads_(partest::TestSuite::GetDefaultNumThreads()) { + CreateUnit("Test 0 indices").Add(&ThreadIndexTest::Test0, this); + CreateUnit("Test 1 index").Add(&ThreadIndexTest::Test1, this); + CreateUnit("Test N indices").Add(&ThreadIndexTest::TestN, this, 1); +} + +void ThreadIndexTest::Test0() { + embb_internal_thread_index_reset(); + unsigned int old_max = embb_thread_get_max_count(); + embb_internal_thread_index_set_max(0); + embb_thread_t thread; + bool index_available = false; + int status = embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + embb_internal_thread_index_set_max(old_max); +} + +void ThreadIndexTest::Test1() { + embb_internal_thread_index_reset(); + unsigned int old_max = embb_thread_get_max_count(); + embb_internal_thread_index_set_max(1); + { + embb_thread_t thread; + bool index_available = true; + int status = + embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + } + { + embb_thread_t thread; + bool index_available = false; + int status = + embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + } + embb_internal_thread_index_set_max(old_max); +} + +embb_atomic_int flag = { 1 }; + +void ThreadIndexTest::TestN() { + embb_internal_thread_index_reset(); + unsigned int old_max = embb_thread_get_max_count(); + embb_internal_thread_index_set_max( + static_cast(number_threads_)); + embb_thread_t* threads = new embb_thread_t[number_threads_]; + + embb_atomic_store_int(&flag, 0); + for (size_t i = 0; i < number_threads_; i++) { + bool index_available = true; + int status = embb_thread_create(threads + i, NULL, ThreadStart, + &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + embb_atomic_store_int(&flag, 1); + for (size_t i = 0; i < number_threads_; i++) { + int status = embb_thread_join(threads + i, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + embb_thread_t thread; + bool index_available = false; + int status = embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + delete[] threads; + embb_internal_thread_index_set_max(old_max); +} + +int ThreadStart(void* arg) { + assert(arg != NULL); + unsigned int index = UINT_MAX; + while (embb_atomic_load_int(&flag) == 0) { embb_thread_yield(); } + int status = embb_internal_thread_index(&index); + if (*static_cast(arg) == true) { + PT_EXPECT_EQ(status, EMBB_SUCCESS); + PT_EXPECT_NE(index, UINT_MAX); + PT_EXPECT_LT(index, partest::TestSuite::GetDefaultNumThreads()); + } else { + PT_EXPECT_EQ(status, EMBB_ERROR); + PT_EXPECT_EQ(index, UINT_MAX); + } + // Do it a second time + status = embb_internal_thread_index(&index); + if (*static_cast(arg) == true) { + PT_EXPECT_EQ(status, EMBB_SUCCESS); + PT_EXPECT_NE(index, UINT_MAX); + PT_EXPECT_LT(index, partest::TestSuite::GetDefaultNumThreads()); + } else { + PT_EXPECT_EQ(status, EMBB_ERROR); + PT_EXPECT_EQ(index, UINT_MAX); + } + return 0; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/thread_index_test.h a/base_c/test/thread_index_test.h new file mode 100644 index 0000000..3452a41 --- /dev/null +++ a/base_c/test/thread_index_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_THREAD_INDEX_TEST_H_ +#define BASE_C_TEST_THREAD_INDEX_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class ThreadIndexTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ThreadIndexTest(); + + private: + /** + * Tests 0 available indices. + */ + void Test0(); + + /** + * Tests 1 available index. + */ + void Test1(); + + /** + * Tests number_thread_ available indices and starting threads at same time. + */ + void TestN(); + + private: + /** + * Configurable number of threads (and indices) used in TestN(). + */ + size_t number_threads_; +}; + +/** + * Thread start method, performs checks with retrieving thread index. + * + * An own thread start is used to ensure that the thread does not yet have an + * index. This is necessary, since partest assigns thread indices before the + * threads are calling the user test functions (when using EMBB for it). + */ +int ThreadStart(void* arg); + +} // namespace test +} // namespace base +} // namespace embb + + + +#endif // BASE_C_TEST_THREAD_INDEX_TEST_H_ diff --git b/base_c/test/thread_specific_storage_test.cc a/base_c/test/thread_specific_storage_test.cc new file mode 100644 index 0000000..b186c6a --- /dev/null +++ a/base_c/test/thread_specific_storage_test.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +ThreadSpecificStorageTest::ThreadSpecificStorageTest() + : tss_(), number_threads_(partest::TestSuite::GetDefaultNumThreads()) { + embb_tss_create(&tss_); + CreateUnit("API") + .Add(&ThreadSpecificStorageTest::Test, this, number_threads_, 1) + .Post(&ThreadSpecificStorageTest::Post, this); +} + +ThreadSpecificStorageTest::~ThreadSpecificStorageTest() { + embb_tss_delete(&tss_); +} + +void ThreadSpecificStorageTest::Test() { + size_t rank = partest::TestSuite::GetCurrentThreadID(); + void* value = embb_tss_get(&tss_); + if (value == NULL) { + size_t * prank = new size_t(rank); + int status = embb_tss_set(&tss_, prank); + if (EMBB_SUCCESS != status) { + delete prank; + } + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } else { + size_t stored_rank = *static_cast(value); + PT_EXPECT_EQ(rank, stored_rank); + } +} + +void ThreadSpecificStorageTest::Post() { + size_t sum_ranks = 0; + for (size_t i = 0; i < embb_thread_get_max_count(); i++) { + void* value = tss_.values[i]; + if (value != NULL) { + sum_ranks += *static_cast(value); + delete static_cast(value); + } + } + size_t expected_sum = 0; + for (size_t i = 0; i < number_threads_; i++) { + expected_sum += i; + } + PT_EXPECT_EQ(sum_ranks, expected_sum); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/thread_specific_storage_test.h a/base_c/test/thread_specific_storage_test.h new file mode 100644 index 0000000..ac2c7df --- /dev/null +++ a/base_c/test/thread_specific_storage_test.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ +#define BASE_C_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class ThreadSpecificStorageTest : public partest::TestCase { + public: + /** + * Adds test methods and sets up data structures. + */ + ThreadSpecificStorageTest(); + + ~ThreadSpecificStorageTest(); + + private: + /** + * Test. + */ + void Test(); + void Post(); + + embb_tss_t tss_; + + size_t number_threads_; +}; + +} // namespace test +} // namespace base +} // namespace embb + + + +#endif // BASE_C_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ diff --git b/base_c/test/thread_test.cc a/base_c/test/thread_test.cc new file mode 100644 index 0000000..f5af851 --- /dev/null +++ a/base_c/test/thread_test.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +ThreadTest::ThreadTest() { + CreateUnit("Starting and joining") + .Add(&ThreadTest::TestStartingAndJoining, this, + partest::TestSuite::GetDefaultNumThreads()); + CreateUnit("Affinities") + .Add(&ThreadTest::TestThreadAffinities, this, 1, + partest::TestSuite::GetDefaultNumIterations()); +} + +void ThreadTest::TestStartingAndJoining() { + { // Run thread with no argument and do not retrieve return value + embb_thread_t thread; + int status = embb_thread_create(&thread, NULL, ThreadStartFunction, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_thread_join(&thread, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + { // Run thread with no argument and do retrieve return value + embb_thread_t thread; + int status = embb_thread_create(&thread, NULL, ThreadStartFunction, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + int return_value = -1; + status = embb_thread_join(&thread, &return_value); + PT_EXPECT_EQ(return_value, 0); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + { // Run thread with argument and do retrieve return value + embb_thread_t thread; + int arg = 1; + int status = embb_thread_create(&thread, NULL, ThreadStartFunction, &arg); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + int return_value = -1; + status = embb_thread_join(&thread, &return_value); + PT_EXPECT_EQ(return_value, 1); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } +} + +void ThreadTest::TestThreadAffinities() { + embb_core_set_t core_set; + embb_core_set_init(&core_set, 1); + embb_thread_t thread; + int status = + embb_thread_create(&thread, &core_set, ThreadStartFunction, NULL); + embb_thread_join(&thread, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); +} + +int ThreadStartFunction(void* arg) { + int return_value = 0; + if (arg != NULL) { + return_value = *static_cast(arg); + } + return return_value; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/thread_test.h a/base_c/test/thread_test.h new file mode 100644 index 0000000..149a489 --- /dev/null +++ a/base_c/test/thread_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_THREAD_TEST_H_ +#define BASE_C_TEST_THREAD_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class ThreadTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ThreadTest(); + + private: + /** + * Tests starting and joining threads. + */ + void TestStartingAndJoining(); + + /** + * Tests setting thread affinities. + * + * Is executed number_iteration times, since a former bug occurred only + * every few times on Windows. + */ + void TestThreadAffinities(); +}; + +/** + * Thread start function used in tests. + * + * \return Zero, if arg is NULL, otherwise arg converted to int. + */ +int ThreadStartFunction(void* arg); + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_THREAD_TEST_H_ diff --git b/base_c/test/time_test.cc a/base_c/test/time_test.cc new file mode 100644 index 0000000..7b1b63e --- /dev/null +++ a/base_c/test/time_test.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + + +TimeTest::TimeTest() { + CreateUnit("Time in duration").Add(&TimeTest::TestTimeInDuration, this); + CreateUnit("Monotonicity").Add( + &TimeTest::TestMonotonicity, this, + 1, partest::TestSuite::GetDefaultNumIterations() * 10); +} + +void TimeTest::TestTimeInDuration() { + embb_time_t time; + + int status = embb_time_in(&time, embb_duration_min()); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + + status = embb_time_in(&time, embb_duration_max()); + PT_EXPECT_EQ(status, EMBB_SUCCESS); +} + +void TimeTest::TestMonotonicity() { + embb_time_t first; + embb_time_t second; + int status1 = embb_time_in(&first, embb_duration_zero()); + int status2 = embb_time_in(&second, embb_duration_zero()); + PT_EXPECT_EQ(status1, EMBB_SUCCESS); + PT_EXPECT_EQ(status2, EMBB_SUCCESS); + unsigned long long first_abs = first.seconds * 1000 + + first.nanoseconds / 1000000; + unsigned long long second_abs = second.seconds * 1000 + + second.nanoseconds / 1000000; + PT_EXPECT_GE(second_abs, first_abs); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_c/test/time_test.h a/base_c/test/time_test.h new file mode 100644 index 0000000..0cf3b84 --- /dev/null +++ a/base_c/test/time_test.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_C_TEST_TIME_TEST_H_ +#define BASE_C_TEST_TIME_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class TimeTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + TimeTest(); + + private: + /** + * Tests time-in-duration method. + */ + void TestTimeInDuration(); + + /** + * Tests that succeedingly taken times are monotonously increasing. + */ + void TestMonotonicity(); +}; + +} // namespace test +} // namespace base +} // namespace embb + + + +#endif // BASE_C_TEST_TIME_TEST_H_ diff --git b/base_cpp/CMakeLists.txt a/base_cpp/CMakeLists.txt new file mode 100644 index 0000000..e7cfd27 --- /dev/null +++ a/base_cpp/CMakeLists.txt @@ -0,0 +1,48 @@ +project (project_embb_base_cpp) + +## CODE FILE DETECTION +# +# Fetch all header and source files for C, C++, and test build separately +file(GLOB_RECURSE EMBB_BASE_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_BASE_CPP_HEADERS "include/embb/*.h") +if (BUILD_TESTS STREQUAL ON) + file(GLOB_RECURSE EMBB_BASE_TEST_SOURCES "test/*.cc" "test/*.h") +endif() + +# Create header file from input file +configure_file("include/embb/base/internal/cmake_config.h.in" + "include/embb/base/internal/cmake_config.h") + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include/embb/base) +GroupSourcesMSVC(src) +if (BUILD_TESTS STREQUAL ON) + GroupSourcesMSVC(test) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ) + +## BUILD TARGETS +# +add_library (embb_base_cpp ${EMBB_BASE_CPP_SOURCES} ${EMBB_BASE_CPP_HEADERS}) +target_link_libraries(embb_base_cpp embb_base_c) +if (BUILD_TESTS STREQUAL ON) + include_directories(test/ + ${CMAKE_CURRENT_BINARY_DIR}/../partest/include + ) + add_executable (embb_base_cpp_test ${EMBB_BASE_TEST_SOURCES}) + target_link_libraries(embb_base_cpp_test partest embb_base_cpp embb_base_c + ${compiler_libs}) + CopyBin(BIN embb_base_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_base_cpp DESTINATION lib) diff --git b/base_cpp/include/embb/base/atomic.h a/base_cpp/include/embb/base/atomic.h new file mode 100644 index 0000000..2d00b86 --- /dev/null +++ a/base_cpp/include/embb/base/atomic.h @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_ATOMIC_H_ +#define EMBB_BASE_ATOMIC_H_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace embb { +namespace base { +#ifdef DOXYGEN + +/** + * \defgroup CPP_BASE_ATOMIC Atomic + * %Atomic operations. + * + * \ingroup CPP_BASE + */ + +/** + * Class representing atomic variables. + * + * The current implementation guarantees sequential consistency (full fences) + * for all atomic operations. Relaxed memory models may be added in the future. + * + * \tparam BaseType Underlying type + * \ingroup CPP_BASE_ATOMIC + */ +template +class Atomic { + public: + /** + * Default constructor. + * + * Constructs an atomic variable holding zero. + * + * \waitfree + * + * \see Atomic(BaseType) + */ + Atomic(); + + /** + * Valued-based constructor. + * + * Constructs an atomic variable holding the passed value. + * + * \param val Initial value + * + * \waitfree + * + * \note There is intentionally no copy constructor, since two different + * memory locations cannot be manipulated atomically. + * + * \see Atomic() + */ + explicit Atomic(BaseType val); + + /** + * Assignment operator. + * + * Assigns the passed value to the object. + * + * \waitfree + * + * \param val The value to assign + * + * \return A shallow copy of this object + */ + BaseType operator=(BaseType val); + + /** + * Type conversion. + * + * Returns the value of the object. Equivalent to Load(). + * + * \waitfree + * + * \return Stored value + * + * \see Load() + */ + operator BaseType() const; + + /** + * Predicate representing support for arithmetic operations. + * + * Returns \c true if type \c BaseType supports arithmetic operations, + * otherwise \c false. Only integers and non-void pointers support + * arithmetic operations. + * + * \waitfree + * + * \return Boolean value indicating support for arithmetic operations + * + * \see IsInteger(), IsPointer() + */ + bool IsArithmetic() const; + + /** + * Predicate representing integers. + * + * Returns \c true if \c BaseType is an integer type, otherwise \c false. + * + * \waitfree + * + * \return Boolean value indicating whether \c BaseType is an integer + * + * \see IsArithmetic(), IsPointer() + */ + bool IsInteger() const; + + /** + * Predicate representing pointers. + * + * Returns \c true if \c BaseType is a non-void pointer type, otherwise + * \c false. + * + * \waitfree + * + * \return Boolean value indicating whether \c BaseType is a non-void pointer + * type + * + * \see IsArithmetic(), IsInteger() + */ + bool IsPointer() const; + + /** + * Store operation. + * + * Stores the passed value in the object. Equivalent to assignment operator, + * except that \c Store does not return anything. + * + * \waitfree + * + * \param val Value to be stored + * + * \see Load() + */ + void Store(BaseType val); + + /** + * Load operation. + * + * Loads and returns the stored value. Equivalent to type conversion. + * + * \waitfree + * + * \return Stored value + * + * \see Store() + */ + BaseType Load() const; + + /** + * Swap operation. + * + * Stores the given value in the object and returns the old value. + * + * \waitfree + * + * \param val New value + * + * \return Old value + * + * \see CompareAndSwap() + */ + BaseType Swap(BaseType val); + + /** + * Compare-and-Swap operation (CAS). + * + * Stores \c desired if the current value is equal to \c expected. + * Otherwise, stores the current value in \c expected. + * + * \waitfree + * + * \param expected Expected value + * \param desired Desired value + * + * \return \c true if CAS succeeded, otherwise \c false + * + * \see Swap() + */ + bool CompareAndSwap(BaseType& expected, BaseType desired); + + /** @name Arithmetic members + * + * The following members are only available if \c BaseType supports arithmetic + * operations (integer and non-void pointer types). + * + */ + + /**@{*/ + + /** + * Fetch-and-Add operation. + * + * Adds the passed value and returns the old value. + * + * \waitfree + * + * \param val Addend + * + * \return Old value + * + * \see FetchAndSub() + */ + BaseType FetchAndAdd(BaseType val); + + /** + * Fetch-and-Sub operation. + * + * Subtracts the passed value and returns the old value. + * + * \waitfree + * + * \param val Subtrahend + * + * \return Old value + * + * \see FetchAndAdd() + */ + BaseType FetchAndSub(BaseType val); + + /** + * Post-increment operation. + * + * Increments the value and returns the old value. + * + * \waitfree + * + * \return Old value + * + * \see operator++() + */ + BaseType operator++(int); + + /** + * Post-decrement operation. + * + * Decrements the value and returns the old value. + * + * \waitfree + * + * \return Old value + * + * \see operator--() + */ + BaseType operator--(int); + + /** + * Pre-increment operation. + * + * Increments the value and returns the new value. + * + * \waitfree + * + * \return New value + * + * \see operator++(int) + */ + BaseType operator++(); + + /** + * Pre-decrement operation. + * + * Decrements the value and returns the new value. + * + * \waitfree + * + * \return New value + * + * \see operator--(int) + */ + BaseType operator--(); + + /** + * Assignment by sum operation. + * + * Adds the passed value and returns the new value. + * + * \param val Addend + * \return New value + * + * \waitfree + * + * \see operator-=() + */ + BaseType operator+=(BaseType val); + + /** + * Assignment by difference operation. + * + * Subtracts the passed value and returns the new value. + * + * \param val Subtrahend + * \return New value + * + * \waitfree + * + * \see operator+=() + */ + BaseType operator-=(BaseType val); + + /**@}*/ + + /** @name Integer members + * + * The following members are only available if \c BaseType is an integer type. + * + */ + + /**@{*/ + + /** + * Assignment by bitwise AND. + * + * Stores the result of the bitwise AND in the current object. + * Does not return anything, since this cannot be implemented + * atomically on all architectures. + * + * \waitfree + * + * \param val Second operand of bitwise AND + * + * \see operator|=(), operator^=() + */ + void operator&=(BaseType val); + + /** + * Assignment by bitwise OR. + * + * Stores the result of the bitwise OR in the current object. + * Does not return anything, since this cannot be implemented + * atomically on all architectures. + * + * \waitfree + * + * \param val Second operand of bitwise OR + * + * \see operator&=(), operator^=() + */ + void operator|=(BaseType val); + + /** + * Assignment by bitwise XOR. + * + * Stores the result of the bitwise XOR in the current object. + * Does not return anything, since this cannot be implemented + * atomically on all architectures. + * + * \param val Second operand of bitwise XOR + * + * \waitfree + * + * \see operator&=(), operator|=() + */ + void operator^=(BaseType val); + + /**@}*/ + + /** @name Pointer members + * + * The following members are only available if \c BaseType is a non-void + * pointer type. + * + */ + + /**@{*/ + + /** + * Structure dereference operation. + * + * Used to access an element of an instance of a class or a structure + * pointed to by the stored pointer. + * + * \return Stored pointer + * + * \waitfree + * + * \see operator*() + */ + BaseType* operator->(); + + /** + * Dereference operation. + * + * Used to access the object pointed to by the stored pointer. + * + * \return Reference to the object + * + * \waitfree + * + * \see operator->() + */ + BaseType& operator*(); + + /**@}*/ +}; + +#else + +/** + * Generic implementation that provides basic functionality. + * See \c embb::base::AtomicBase for more information. + * + * \tparam BaseType Underlying type + */ +template +class Atomic : public embb::base::internal::atomic::AtomicBase < BaseType > { + public: + /** + * Constructs an atomic variable holding an uninitialized value. + */ + Atomic() : embb::base::internal::atomic::AtomicBase() {} + + /** + * Constructs an atomic variable holding the passed value. + * + * \param val The value to assign. + */ + explicit Atomic(BaseType val) : embb::base::internal::atomic:: + AtomicBase(val) {} + + /** + * Assignment operator. + * + * \param val The value to assign. + * + * \return A shallow copy of this object. + */ + BaseType operator=(BaseType val) { + return embb::base::internal::atomic::AtomicBase:: + operator=(val); + } +}; + +/** +* Specialization for non-void pointer types. +* See \c embb::base::internal::atomic::AtomicPointer for more information. +* +* \tparam BaseType Type of the objects pointed to +*/ +template +class Atomic : public embb::base::internal::atomic:: + AtomicPointer < BaseType, ptrdiff_t, sizeof(BaseType*) > { + public: + Atomic() : embb::base::internal::atomic:: + AtomicPointer() {} + explicit Atomic(BaseType* p) : embb::base::internal::atomic:: + AtomicPointer(p) {} + + BaseType* operator=(BaseType* p) { + return embb::base::internal::atomic:: + AtomicPointer::operator=(p); + } +}; + +/** +* Specialization for void pointer types. +* See \c embb::base::internal::atomic::AtomicBase for more information. +* Unlike the specialization for non-void pointer types, this class +* does not permit dereferencing, incrementation, etc. +*/ +template<> +class Atomic : public embb::base::internal::atomic::AtomicBase < void* > { + public: + Atomic() : embb::base::internal::atomic::AtomicBase() {} + explicit Atomic(void* p) : embb::base::internal::atomic::AtomicBase(p) + {} + + void* operator=(void* p) { + return embb::base::internal::atomic::AtomicBase::operator=(p); + } +}; + +/** +* Specializations for integers. +* See \c embb::base::internal::atomic::AtomicInteger for more information. +*/ +#define __EMBB_ATOMIC_INTEGER_SPECIALIZATION(T) \ +template<> \ +class Atomic: public embb::base::internal::atomic::AtomicInteger { \ + public: \ + \ + Atomic() : embb::base::internal::atomic::AtomicInteger() {} \ + explicit Atomic(T val) : embb::base::internal::atomic::AtomicInteger(val) \ + {} \ + \ + T operator=(T val) { return embb::base::internal::atomic::AtomicInteger::\ + operator=(val); } \ + \ +} + +// Specializations for integers +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(signed char); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(unsigned char); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(signed short); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(unsigned short); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(signed int); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(unsigned int); + +#ifdef EMBB_PLATFORM_ARCH_X86_64 +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(size_t); +#endif + +#endif +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_ATOMIC_H_ diff --git b/base_cpp/include/embb/base/base.h a/base_cpp/include/embb/base/base.h new file mode 100644 index 0000000..9bde979 --- /dev/null +++ a/base_cpp/include/embb/base/base.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_BASE_H_ +#define EMBB_BASE_BASE_H_ + +/** + * \defgroup CPP C++ Components + * Components written in C++. + */ + +/** + * \defgroup CPP_CONCEPT C++ Concepts + * Concepts for C++ components. + */ + +/** + * \defgroup CPP_BASE Base + * \ingroup CPP + * Platform-independent abstraction layer for multithreading and basic + * operations. + * + * Base C++ is mainly a C++ wrapper around the Base C abstractions. It adds + * additional convenience types and functions that leverage the capabilities of + * C++ such as templates, operator overloading, or RAII paradigms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_BASE_BASE_H_ diff --git b/base_cpp/include/embb/base/condition_variable.h a/base_cpp/include/embb/base/condition_variable.h new file mode 100644 index 0000000..50dd37a --- /dev/null +++ a/base_cpp/include/embb/base/condition_variable.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CONDITION_VARIABLE_H_ +#define EMBB_BASE_CONDITION_VARIABLE_H_ + +#include +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_CONDITION Condition Variable + * + * Condition variables for thread synchronization. + * + * \ingroup CPP_BASE + */ + +/** + * Represents a condition variable for thread synchronization. + * + * Provides an abstraction from platform-specific condition variable + * implementations. Condition variables can be waited for with timeouts using + * relative durations and absolute time points. + * + * This class is essentially a wrapper for the underlying C implementation. + * + * \ingroup CPP_BASE_CONDITION + */ +class ConditionVariable { + public: + /** + * Creates a condition variable. + * + * \throws embb::base::ErrorException if initialization failed + * + * \memory Potentially allocates dynamic memory + * + * \notthreadsafe + */ + ConditionVariable(); + + /** + * Wakes up one waiting thread. + * + * \throws embb::base::ErrorException if notification failed + * + * \threadsafe + * + * \see NotifyAll(), Wait() + */ + void NotifyOne(); + + /** + * Wakes up all waiting threads. + * + * \throws embb::base::ErrorException if notification failed + * + * \threadsafe + * + * \see NotifyOne(), Wait() + */ + void NotifyAll(); + + /** + * Releases the lock and waits until the thread is woken up. + * + * \pre The lock has been acquired by the calling thread. + * \post The lock has been re-acquired by the calling thread. + * + * \throws embb::base::ErrorException if waiting failed + * + * \threadsafe + * + * \see NotifyOne(), NotifyAll() + */ + void Wait( + UniqueLock& lock + /**< [IN,OUT] Lock to be released and re-acquired */ + ); + + /** + * Releases the lock and waits until the thread is woken up or the specified + * time point has passed. + * + * \pre The lock has been acquired by the calling thread. + * \post The lock has been re-acquired by the calling thread. + * + * \return \c true if the thread was woken up before the specified time point + * has passed, otherwise \c false. + * + * \throws embb::base::ErrorException if an error occurred + * + * \threadsafe + */ + bool WaitUntil( + UniqueLock& lock, + /**< [IN,OUT] Lock to be released and re-acquired */ + const Time& time + /**< [IN] Absolute time point until which the thread maximally waits */ + ); + + /** + * Releases the lock and waits until the thread is woken up or the specified + * duration has passed. + * + * \pre The lock has been acquired by the calling thread. + * \post The lock has been re-acquired by the calling thread. + * + * \return \c true if the thread was woken up before the specified duration + * has passed, otherwise \c false. + * + * \throws embb::base::ErrorException if an error occurred + * + * \threadsafe + * + * \tparam Tick Type of tick of the duration. See Duration. + */ + template + bool WaitFor( + UniqueLock& lock, + /**< [IN,OUT] Lock to be released and re-acquired */ + const Duration& duration + /**< [IN] Relative time duration the thread maximally waits */ + ); + + private: + /** + * Disables copying and assigment. + */ + ConditionVariable(const ConditionVariable&); + ConditionVariable& operator=(const ConditionVariable&); + + /** + * Holds actual condition variable. + */ + internal::ConditionVariableType condition_var_; +}; + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_CONDITION_VARIABLE_H_ diff --git b/base_cpp/include/embb/base/core_set.h a/base_cpp/include/embb/base/core_set.h new file mode 100644 index 0000000..82599ac --- /dev/null +++ a/base_cpp/include/embb/base/core_set.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CORE_SET_H_ +#define EMBB_BASE_CORE_SET_H_ + +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_CORESET Core Set + * + * Core sets for thread-to-core affinities + * + * \ingroup CPP_BASE + */ + +/** + * Represents a set of processor cores, used to set thread-to-core affinities. + * + * An instance of this type represents a subset of processor cores. Core sets + * can be used to set thread-to-core affinities. A core in a core set might + * just represent a logical core (hyper-thread), depending on the underlying + * hardware. Each core is identified by a unique integer starting with 0. + * For example, the cores of a quad-core system are represented by the set + * {0,1,2,3}. + * + * This class is essentially a wrapper for the underlying C implementation. + * + * \notthreadsafe + * \ingroup CPP_BASE_CORESET + */ +class CoreSet { + public: + /** + * Returns the number of available processor cores. + * + * If the processor supports hyper-threading, each hyper-thread is treated as + * a separate processor core. + * + * \return Number of cores including hyper-threads + */ + static unsigned int CountAvailable(); + + /** + * Constructs an empty core set. + */ + CoreSet(); + + /** + * Constructs a core set with all or no cores. + */ + explicit CoreSet( + bool value + /**< [IN] \c true includes all cores in the set, \c false excludes all */ + ); + + /** + * Constructs a copy of the specified core set. + */ + CoreSet( + const CoreSet& to_copy + /**< [IN] Core set to copy */ + ); + + /** + * Assigns an existing core set. + * + * \return Reference to \c *this + */ + CoreSet& operator=( + const CoreSet& to_assign + /**< [IN] Core set to assign */ + ); + + /** + * Resets the core set according to the specified value. + */ + void Reset( + bool value + /**< [IN] \c true includes all cores in the set, \c false excludes all */ + ); + + /** + * Adds one core to the core set. + */ + void Add( + unsigned int core + /**< [IN] Core to add (from 0 to number of cores - 1) */ + ); + + /** + * Removes one core from the core set. + */ + void Remove( + unsigned int core + /** [IN] Core to remove (from 0 to number of cores - 1) */ + ); + + /** + * Checks whether the specified core is included in the set. + * + * \return \c true if core is included, otherwise \c false + */ + bool IsContained( + unsigned int core + /**< [IN] Core to check (from 0 to number of cores - 1) */ + ) const; + + /** + * Counts the number of cores in the set. + * + * \return Number of cores in the set + */ + unsigned int Count() const; + + /** + * Intersects this core set with the specified one. + * + * This core set is not modified by the operation. + * + * \return Copy of the result + */ + CoreSet operator&( + const CoreSet& rhs + /** [IN] Core set on right-hand side of intersection operation */ + ) const; + + /** + * Unites this core set with the specified one. + * + * This core set is not modified by the operation. + * + * \return Copy of the result + */ + CoreSet operator|( + const CoreSet& rhs + /** [IN] Core set on right-hand side of union operation */ + ) const; + + /** + * Intersects this core set with the specified one and overwrites this core + * set. + * + * \return Reference to \c *this + */ + CoreSet& operator&=( + const CoreSet& rhs + /** [IN] Core set on right-hand side of intersection operation */ + ); + + /** + * Unites this core set with the specified one an overwrites this core set. + * + * \return Reference to \c *this + */ + CoreSet& operator|=( + const CoreSet& rhs + /** [IN] Core set on right-hand side of union operation */ + ); + + /** + * Provides access to internal representation to use it with C API. + * + * \return A reference to the internal embb_core_set_t structure. + */ + embb_core_set_t const & GetInternal() const { return rep_; } + + private: + /** + * Internal representation of core set. + */ + embb_core_set_t rep_; + + /** + * Needs access to internal representation to use it with C API. + */ + friend class Thread; +}; + +} // namespace base +} // namespace embb + + + +#endif // EMBB_BASE_CORE_SET_H_ diff --git b/base_cpp/include/embb/base/duration.h a/base_cpp/include/embb/base/duration.h new file mode 100644 index 0000000..dd66468 --- /dev/null +++ a/base_cpp/include/embb/base/duration.h @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_DURATION_H_ +#define EMBB_BASE_DURATION_H_ + +#include +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_TIMEDURATION Duration and Time + * + * Relative time durations and absolute time points + * + * \ingroup CPP_BASE + */ + +/** + * Represents a relative time duration for a given tick type. + * + * \notthreadsafe + * \note The typedefs DurationSeconds, DurationMilliseconds, + * DurationMicroseconds, and DurationNanoseconds provide directly usable + * duration types. + * \tparam Tick Possible tick types are Seconds, Milliseconds, Microseconds, + * Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +template +class Duration { + public: + /** + * Returns duration of length zero. + * \return Duration of length zero + */ + static const Duration& Zero(); + + /** + * Returns duration with maximum ticks representable by implementation. + * + * This value depends on the tick type and on the platform. + * + * \return Reference to duration with maximum value + */ + static const Duration& Max(); + + /** + * Returns duration with minimum ticks representable by implementation. + * + * This value depends on the tick type and on the platform. + * + * \return Reference to duration with minimum value + */ + static const Duration& Min(); + + /** + * Constructs a duration of length zero. + */ + Duration(); + + /** + * Constructs a duration with given number of ticks. + */ + explicit Duration( + unsigned long long ticks + /**< [IN] Number of ticks */ + ); + + /** + * Constructs a duration by copying from an existing duration. + */ + Duration( + const Duration& to_copy + /**< [IN] %Duration to copy */ + ); + + /** + * Assigns an existing duration. + * + * \return Reference to \c *this + */ + Duration& operator=( + const Duration& to_assign + /**< [IN] %Duration to assign */ + ); + + /** + * Returns the number of ticks of the duration. + * \return Number of ticks of the duration + */ + unsigned long long Count() const; + + /** + * Assignment by addition of another duration with same tick type. + * + * \return Reference to \c *this + */ + Duration& operator+=( + const Duration& rhs + /**< [IN] %Duration to add to this duration */ + ); + + private: + /** + * Constructs a duration from the internal representation. + * + * \pre \c duration needs to fit into the duration type + * \throws + */ + Duration( + const embb_duration_t& duration + /**< [IN] %Duration to copy from */ + ); + + /** + * Internal representation from Base C. + */ + embb_duration_t rep_; + + /** + * For accessing rep_ and using Base C functionality. + */ + friend class Time; + + /** + * For accessing rep_ and using Base C functionality. + */ + friend class ConditionVariable; +}; + +/** + * Compares two durations (equality). + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is equal to \c rhs, otherwise \c false + */ +template +bool operator==( + const Duration& lhs, + /**< [IN] Left-hand side of equality operator */ + const Duration& rhs + /**< [IN] Right-hand side of equality operator */ + ) { + return embb_duration_compare(&lhs, &rhs) == 0; +} + +/** + * Compares two durations (inequality). + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is not equal to \c rhs, otherwise \c false + */ +template +bool operator!=( + const Duration& lhs, + /**< [IN] Left-hand side of inequality operator */ + const Duration& rhs + /**< [IN] Right-hand side of inequality operator */ + ) { + return embb_duration_compare(&lhs, &rhs) != 0; +} + +/** + * Compares two durations (less than) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is shorter than \c rhs. + */ +template +bool operator<( + const Duration& lhs, + /**< [IN] Left-hand side of less than operator */ + const Duration& rhs + /**< [IN] Right-hand side of less than operator */ + ) { + return embb_duration_compare(&lhs, &rhs) == -1; +} + +/** + * Compares two durations (greater than) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is longer than \c rhs. + */ +template +bool operator>( + const Duration& lhs, + /**< [IN] Left-hand side of greater than operator */ + const Duration& rhs + /**< [IN] Right-hand side of greater than operator */ + ) { + return embb_duration_compare(&lhs, &rhs) == 1; +} + +/** + * Compares two durations (less than or equal to) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is shorter than or equal to \c rhs. + */ +template +bool operator<=( + const Duration& lhs, + /**< [IN] Left-hand side of less than or equal to operator */ + const Duration& rhs + /**< [IN] Right-hand side of less than or equal to operator */ + ) { + return embb_duration_compare(&lhs, &rhs) < 1; +} + +/** + * Compares two durations (greater than or equal to) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is longer than or equal to \c rhs. + */ +template +bool operator>=( + const Duration& lhs, + /**< [IN] Left-hand side of greater than or equal to operator */ + const Duration& rhs + /**< [IN] Right-hand side of greater than or equal to operator */ + ) { + return embb_duration_compare(&lhs, &rhs) > -1; +} + +/** + * Adds two durations + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return Sum of \c lhs and \c rhs. + */ +template +Duration operator+( + const Duration& lhs, + /**< [IN] Left-hand side of addition operator */ + const Duration& rhs + /**< [IN] Right-hand side of addition operator */ + ) { + return Duration(lhs.Count() + rhs.Count()); +} + +namespace internal { + +/** + * Base class for ticks. + */ +class Tick { + public: + /** + * Checks the status for under- and overflow and, in such a case, throws an + * exception. + */ + static void CheckExceptions( + int status, + /**< [IN] Status code to check */ + const char* msg + /**< [IN] Exception message if one is thrown */ + ); +}; + +/** + * %Seconds tick for Duration. + * + * \see Milliseconds, Microseconds, Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Seconds : public Tick { + public: + /** + * Sets the ticks as seconds and returns the status of the duration operation. + * + * \return Status code of embb_duration_set_seconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as seconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in seconds for the given duration. + * + * \return Number of second ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of second ticks for the available implementation. + * + * \return Minimum number of second ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of second ticks for the available implementation. + * + * \return Maximum number of second ticks. + */ + static unsigned long long Max(); +}; + +/** + * %Milliseconds tick for Duration. + * + * \see Seconds, Microseconds, Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Milliseconds : public Tick { + public: + /** + * Sets the ticks as milliseconds and returns the status of the duration + * operation. + * + * \return Status code of embb_duration_set_milliseconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as milliseconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in milliseconds for the given duration. + * + * \return Number of millisecond ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of millisecond ticks for the available + * implementation. + * + * \return Minimum number of microsecond ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of millisecond ticks for the available + * implementation. + * + * \return Maximum number of millisecond ticks. + */ + static unsigned long long Max(); +}; + +/** + * %Microseconds tick for Duration. + * + * \see Seconds, Milliseconds, Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Microseconds : public Tick { + public: + /** + * Sets the ticks as microseconds and returns the status of the duration + * operation. + * + * \return Status code of embb_duration_set_microseconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as microseconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in microseconds for the given duration. + * + * \return Number of microsecond ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of microsecond ticks for the available + * implementation. + * + * \return Minimum number of microsecond ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of microsecond ticks for the available + * implementation. + * + * \return Maximum number of microsecond ticks. + */ + static unsigned long long Max(); +}; + +/** + * %Nanoseconds tick for Duration. + * + * \see Seconds, Milliseconds, Microseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Nanoseconds : public Tick { + public: + /** + * Sets the ticks as nanoseconds and returns the status of the duration + * operation. + * + * \return Status code of embb_duration_set_nanoseconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as nanoseconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in nanoseconds for the given duration. + * + * \return Number of nanosecond ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of nanosecond ticks for the available + * implementation. + * + * \return Minimum number of nanosecond ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of nanosecond ticks for the available + * implementation. + * + * \return Maximum number of nanosecond ticks. + */ + static unsigned long long Max(); +}; + +} // namespace internal + +/** + * Duration with seconds tick. + * + * \ingroup CPP_BASE_TIMEDURATION + */ +typedef Duration DurationSeconds; +/** + * Duration with milliseconds tick. + * + * \ingroup CPP_BASE_TIMEDURATION + */ +typedef Duration DurationMilliseconds; +/** + * Duration with microseconds tick. + * + * \ingroup CPP_BASE_TIMEDURATION + */ +typedef Duration DurationMicroseconds; +/** + * Duration with nanoseconds tick. + * + * \ingroup CPP_BASE_TIMEDURATION + */ +typedef Duration DurationNanoseconds; + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_DURATION_H_ diff --git b/base_cpp/include/embb/base/exceptions.h a/base_cpp/include/embb/base/exceptions.h new file mode 100644 index 0000000..9beb684 --- /dev/null +++ a/base_cpp/include/embb/base/exceptions.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_EXCEPTIONS_H_ +#define EMBB_BASE_EXCEPTIONS_H_ + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +// Disable warning that exceptions are disabled but try/catch is used. +#pragma warning(disable : 4530) +#endif // EMBB_PLATFORM_COMPILER_MSVC + +#include +#include + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + +#include +#include + +/** + * \defgroup CPP_BASE_EXCEPTIONS Exception + * + * Exception types. + * + * If exceptions are disabled, i.e., if the library was built without support + * for exceptions, no exceptions will be thrown. Instead, an error message is + * printed to \c stderr and the program exits with the code representing the + * exception. + * + * \ingroup CPP_BASE + */ + +/** + * Macros to be used within EMBB when throwing and catching exceptions. + * + * Example: + * - Throwing an exception: + * EMBB_THROW(NoMemoryException, "Could not create thread."); + * --> If exceptions are disabled, this will write an error to stderr, + * containing the exception's message, and exit the program with the code + * of the exception. + * - Try/catch block: + * EMBB_TRY{ ... things to try ... } + * EMBB_CATCH(ExceptionXYZ& e){ ... things to catch ... } + * --> If exceptions are disabled, this will execute the try block without and + * replace the catch() statement by "if (false)", such that + * the catch block will be compiled but never executed. + */ +#ifdef EMBB_USE_EXCEPTIONS +#define EMBB_TRY try +#define EMBB_THROW(Type, Message) throw Type(Message); +#define EMBB_CATCH(Statement) catch(Statement) +#else /* EMBB_USE_EXCEPTIONS */ +#include +#include +#include +#define EMBB_TRY +/** + * Concatenates the inputs. + */ +#define EMBB_CATCH_VAR_CAT2(X, Y) X##Y +/** + * Is a necessary intermediate macro to create EMBB_CATCH_VAR. + */ +#define EMBB_CATCH_VAR_CAT1(X, Y) EMBB_CATCH_VAR_CAT2(X, Y) +/** + * Defines a unique variable name for each line of code. + * + * The line number is concatenated to a base name. + */ +#define EMBB_CATCH_VAR EMBB_CATCH_VAR_CAT1(embb_catch_var_, __LINE__) +/** + * Replaces catch(xyz) by an if-statement that is always false. + * + * To avoid a compiler warning that the condition is constant, a variable is + * used instead of "false". A unique name for every catch-statement is + * necessary, which is achieved by macro EMBB_CATCH_VAR and helper macros. To + * avoid the unused variable warning, in addition the EMBB_UNUSED macro is + * used. + */ +#define EMBB_CATCH(Statement) \ + int EMBB_CATCH_VAR = false; \ + EMBB_UNUSED(EMBB_CATCH_VAR); \ + if (EMBB_CATCH_VAR) + +/** + * Replaces a throw by an error message and program exit. + */ +#define EMBB_THROW(Type, Message) \ + { \ + Type e(Message); \ + fprintf(stderr, \ + "Exit program due to (not thrown) " #Type ": %s\n", e.what()); \ + exit(e.Code()); \ + } +#endif /* else EMBB_USE_EXCEPTIONS */ + +namespace embb { +namespace base { + +/** + * Abstract base class for exceptions. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class Exception : public std::exception { + public: + /** + * Constructs an exception with a custom message. + */ + explicit Exception( + const char* message + /**< [IN] Error message */ + ) : message_(message) {} + + /** + * Destructs the exception. + */ + virtual ~Exception() throw() {} + + /** + * Constructs an exception by copying from an existing one. + */ + Exception( + const Exception& e + /**< [IN] %Exception to be copied */ + ) : message_(e.message_) {} + + /** + * Assigns an existing exception. + * + * \return Reference to \c *this + */ + Exception& operator=( + const Exception& e + /**< [IN] %Exception to assign */ + ) { + message_ = e.message_; + return *this; + } + + /** + * Returns the error message. + * + * \return Pointer to error message + */ + virtual const char* What() const throw() { + return message_; + } + + /** + * Returns an integer code representing the exception. + * + * \return %Exception code + */ + virtual int Code() const = 0; + + private: + /** + * Holds error message + */ + const char* message_; +}; + +/** + * Indicates lack of memory necessary to allocate a resource. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class NoMemoryException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit NoMemoryException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_NOMEM; } +}; + +/** + * Indicates business (unavailability) of a required resource. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class ResourceBusyException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit ResourceBusyException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_BUSY; } +}; + +/** + * Indicates a numeric underflow. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class UnderflowException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit UnderflowException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_UNDERFLOW; } +}; + +/** + * Indicates a numeric overflow. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class OverflowException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit OverflowException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_OVERFLOW; } +}; + +/** + * Indicates a general error. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class ErrorException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit ErrorException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_ERROR; } +}; + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_EXCEPTIONS_H_ diff --git b/base_cpp/include/embb/base/function.h a/base_cpp/include/embb/base/function.h new file mode 100644 index 0000000..58aef0d --- /dev/null +++ a/base_cpp/include/embb/base/function.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_FUNCTION_H_ +#define EMBB_BASE_FUNCTION_H_ + +/** + * \defgroup CPP_BASE_FUNCTION Function + * %Function wrapper and binding of parameters. + * + * \ingroup CPP_BASE + */ + +namespace embb { +namespace base { + +/** + * Provides placeholders for Function arguments used in Bind() + * + * \ingroup CPP_BASE_FUNCTION + */ +class Placeholder { + public: + class Arg_1 {}; + class Arg_2 {}; + class Arg_3 {}; + class Arg_4 {}; + class Arg_5 {}; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_1 _1; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_2 _2; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_3 _3; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_4 _4; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_5 _5; +}; + +} // namespace base +} // namespace embb + +#ifdef DOXYGEN + +namespace embb { +namespace base { + +/** + * Wraps function pointers, member function pointers, and functors with up to + * five arguments. + * + * \ingroup CPP_BASE_FUNCTION + */ +template +class Function { + public: + /** + * Constructor from functor. Uses operator() with return type ReturnType + * and up to five arguments. Copies the functor. + * \memory Allocates memory for the copy of the functor. + */ + template + explicit Function( + ClassType const & obj /**< The functor to wrap. */ + ); + + /** + * Constructor from function pointer with return type ReturnType and up to + * five arguments. + */ + explicit Function( + ReturnType(*func)(...) /**< The function pointer. */ + ); + + /** + * Constructor from object and member function pointer with return type + * ReturnType and up to five arguments. + */ + template + Function( + ClassType & obj, /**< Reference to object. */ + ReturnType(ClassType::*func)(...) + /**< Member function pointer. */ + ); + + /** + * Copy constructor. + */ + Function( + Function const & func /**< The Function to copy. */ + ); + + /** + * Destructor. + */ + ~Function(); + + /** + * Assigns this object a new function pointer. + */ + void operator = ( + ReturnType(*func)(...) /**< The function pointer. */ + ); + + /** + * Assigns this object another Function. + */ + void operator = ( + Function & func /**< The Function. */ + ); + + /** + * Assigns this object a new functor. The functor is copied. + */ + template + void operator = ( + C const & obj /**< The functor. */ + ); + + /** + * Calls the wrapped function with the given parameters. + * \returns A value generated by the wrapped function. + */ + ReturnType operator () (...); +}; + +/** + * Wraps an object and a member function pointer into a Function + * + * \returns Function with same return value and argument syntax as \c func + * \see Function + * \tparam ClassType Class that contains the member function pointed to by \c + * func. + * \tparam ReturnType Return type of member function pointed to by \c func + * \tparam [Arg1,...,Arg5] (Optional) Types of up to five arguments of the + * member function + * \ingroup CPP_BASE_FUNCTION + */ +template +Function MakeFunction( + ClassType& obj, + /**< [IN] Reference to the object with corresponding member function */ + ReturnType(ClassType::*func)([Arg1, ..., Arg5]) + /**< [IN] Member function pointer with up to five optional arguments */ + ); + +/** + * Wraps a function pointer into a Function + * + * \returns Function with same return value and argument syntax as \c func + * \see Function + * \tparam ReturnType Return type of member function pointed to by \c func. + * \tparam [Arg1,...,Arg5] (Optional) Types of up to five arguments of the + * member function + * \ingroup CPP_BASE_FUNCTION + */ +template +Function MakeFunction( + ReturnType(*func)([Arg1, ..., Arg5]) + /**< [IN] Function pointer with up to five optional arguments */ + ); + +/** + * Binds given values as arguments of \c func into a new Function + * + * The new Function has no arguments or one, if Placeholder::_1 is + * given as one of the values. The position of Placeholder::_1 determines which + * argument of \c func is not bound. + * \memory Allocates dynamic memory to hold the parameters. + * \returns Function that uses given values as parameters + * \see Placeholder, Function + * \tparam ReturnType Return type of \c func and parameterless function returned + * \tparam [UnboundArgument] Type of not bound argument of \c func, only present + * when a placeholder is used as value in the bind. + * \tparam Arg1[,...,Arg5] Types of up to five arguments of the values to bind + * \ingroup CPP_BASE_FUNCTION + */ +template +Function Bind( + Function func, + /**< [IN] The Function to bind the values (\c value1, ...) to */ + Arg1 value1, + /**< [IN] At least one and up to five values to bind as arguments of \c func. + Placeholder::_1 can be used instead of one of the values to keep the + corresponding argument of \c func unbound. */ + ... + ); + +} // namespace base +} // namespace embb + +#else // DOXYGEN + +#include +#include +#include +#include +#include +#include + +#endif // DOXYGEN + +#endif // EMBB_BASE_FUNCTION_H_ diff --git b/base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h a/base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h new file mode 100644 index 0000000..f9cdd25 --- /dev/null +++ a/base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_ARITHMETIC_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_ARITHMETIC_H_ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * Subclass that implements arithmetic operations. + * This class provides arithmetic operations for integers and pointers. + * + * \tparam BaseType Underlying type (must be an integral type or a + * non-void pointer type) + * \tparam DifferenceType Difference type (\c ptrdiff_t for pointers, + * otherwise \c BaseType) + * \tparam Stride Stride (in case of pointers, size of the objects + * pointed to, otherwise 1) + */ +template +class AtomicArithmetic : public AtomicBase { + protected: + typedef typename AtomicBase::NativeType NativeType; + + public: + // See base class + AtomicArithmetic(); + // See base class + explicit AtomicArithmetic(BaseType val); + + // See base class + BaseType operator=(BaseType val); + + // See base class + bool IsArithmetic() const; + + // The methods below are documented in atomic.h + BaseType FetchAndAdd(DifferenceType val); + BaseType FetchAndSub(DifferenceType val); + BaseType operator++(int); + BaseType operator--(int); + BaseType operator++(); + BaseType operator--(); + BaseType operator+=(DifferenceType val); + BaseType operator-=(DifferenceType val); +}; + +template +inline AtomicArithmetic::AtomicArithmetic() +: AtomicBase() {} + +template +inline AtomicArithmetic:: +AtomicArithmetic(BaseType val) : AtomicBase(val) {} + +template +inline BaseType +AtomicArithmetic::operator=(BaseType val) { + return AtomicBase::operator=(val); +} + +template +inline bool AtomicArithmetic:: +IsArithmetic() const { + return true; +} + +template +inline BaseType AtomicArithmetic:: +FetchAndAdd(DifferenceType val) { + /* + Despite the (at first sight) complexity of different function variables + and memcpy calls, the compiler is smart enough to optimize this... + A call to a++ (invoking this function), where "a" is an atomic integer + variable, translates to (tested using MSVC): + 00CA3744 mov edx,1 + 00CA3749 lea ecx,[a] + 00CA374C call \embb_internal__atomic_fetch_and_add_4_asm\8 (0CA381Fh) + */ + BaseType return_value; + DifferenceType desired = static_cast(Stride)*val; + + NativeType native_desired; + memcpy(&native_desired, &desired, sizeof(desired)); + + NativeType storage_value = fetch_and_add_implementation:: + fetch_and_add(&this->AtomicValue, native_desired); + + memcpy(&return_value, &storage_value, sizeof(return_value)); + return return_value; +} + +template +inline BaseType AtomicArithmetic:: +FetchAndSub(DifferenceType val) { + return FetchAndAdd(-val); +} + +template +inline BaseType AtomicArithmetic:: +operator++(int) { + return FetchAndAdd(1); +} + +template +inline BaseType AtomicArithmetic:: +operator--(int) { + return FetchAndSub(1); +} + +template +inline BaseType AtomicArithmetic:: +operator++() { + return FetchAndAdd(1) + 1; +} + +template +inline BaseType AtomicArithmetic:: +operator--() { + return FetchAndSub(1) - 1; +} + +template +inline BaseType AtomicArithmetic:: +operator+=(DifferenceType val) { + return FetchAndAdd(val) + val; +} + +template +inline BaseType AtomicArithmetic:: +operator-=(DifferenceType val) { + return FetchAndSub(val) - val; +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_ARITHMETIC_H_ diff --git b/base_cpp/include/embb/base/internal/atomic/atomic_base.h a/base_cpp/include/embb/base/internal/atomic/atomic_base.h new file mode 100644 index 0000000..bfa3dd6 --- /dev/null +++ a/base_cpp/include/embb/base/internal/atomic/atomic_base.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_BASE_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_BASE_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +template +class AtomicBase { + // The copy constructor is undefined, since it cannot be implemented + // atomically. + AtomicBase(const AtomicBase&); + // The assignment operator is undefined, since it cannot be implemented + // atomically. + AtomicBase& operator=(const AtomicBase&); + + protected: + typedef typename embb::base::internal::atomic:: + AtomicTraits:: + NativeType NativeType; + + mutable NativeType AtomicValue; + + public: + /** + * Default constructor. + * Initializes the object with zero. + */ + AtomicBase(); + + /** + * Constructor. + * Initializes the object with the passed value. + * + * \param val Initial value + */ + explicit AtomicBase(BaseType val); + + // The members below are documented in atomic.h + BaseType operator=(BaseType val); + operator BaseType() const; + bool IsLockFree() const; + bool IsArithmetic() const; + bool IsInteger() const; + bool IsPointer() const; + void Store(BaseType val); + BaseType Load() const; + BaseType Swap(BaseType val); + bool CompareAndSwap(BaseType& expected, BaseType desired); +}; + +template +inline AtomicBase::AtomicBase() : AtomicValue(0) { +} + +template +inline AtomicBase::AtomicBase(BaseType val) /*: AtomicValue(val)*/ { + memcpy(&AtomicValue, &val, sizeof(AtomicValue)); +} + +template +inline BaseType AtomicBase::operator=(BaseType val) { + Store(val); + return val; +} + +template +inline AtomicBase::operator BaseType() const { + return Load(); +} + +template +inline bool AtomicBase::IsArithmetic() const { + return false; +} + +template +inline bool AtomicBase::IsInteger() const { + return false; +} + +template +inline bool AtomicBase::IsPointer() const { + return false; +} + +template +inline void AtomicBase::Store(BaseType val) { + NativeType storage_value; + // Justification for using memcpy instead of pointer casts + // or union type punning: + // - with strict aliasing, type punning using pointer casts or + // unions is unsafe + // - memcpy is not slower, as the compiler will optimize it away + // anyway... + memcpy(&storage_value, &val, sizeof(storage_value)); + + store_implementation< NativeType > + ::Store(&AtomicValue, storage_value); +} + +template +inline BaseType AtomicBase::Load() const { + BaseType return_value; + + NativeType storage_value = + load_implementation< NativeType >::Load(&AtomicValue); + + memcpy(&return_value, &storage_value, sizeof(return_value)); + + return return_value; +} + +template +inline BaseType AtomicBase::Swap(BaseType val) { + NativeType storage_value; + BaseType return_value; + + memcpy(&storage_value, &val, sizeof(storage_value)); + + NativeType storage_value2 = swap_implementation< NativeType > + ::Swap(&AtomicValue, storage_value); + + memcpy(&return_value, &storage_value2, sizeof(return_value)); + + return return_value; +} + +template +inline bool AtomicBase:: +CompareAndSwap(BaseType& expected, BaseType desired) { + NativeType native_expected; + NativeType native_desired; + + memcpy(&native_expected, &expected, sizeof(expected)); + memcpy(&native_desired, &desired, sizeof(desired)); + + bool return_val = + (compare_and_swap_implementation:: + compare_and_swap(&AtomicValue, &native_expected, native_desired)) !=0 + ? true : false; + + memcpy(&expected, &native_expected, sizeof(expected)); + + return return_val; +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_BASE_H_ diff --git b/base_cpp/include/embb/base/internal/atomic/atomic_implementation.h a/base_cpp/include/embb/base/internal/atomic/atomic_implementation.h new file mode 100644 index 0000000..0367be7 --- /dev/null +++ a/base_cpp/include/embb/base/internal/atomic/atomic_implementation.h @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_IMPLEMENTATION_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_IMPLEMENTATION_H_ + +#ifdef DOXYGEN +// Omit this file from Doxygen generation. +#else + +#include + +/** + * The macros below are used to generate calls to the base_c implementation. We + * do not implement a macro for each atomic method, as many methods are equal in + * method signature. Therefore, we only define macros for different method + * signatures, which actually define template specializations. The signature is + * reflected in the macro name. For example the prefix + * RET_VAL_PAR1_POINTER_PAR2_VALUE means that the signature of the atomic has + * return type value, the first parameter is a pointer and the second one a + * value. For each set of template specializations, a master template has to be + * defined. This can be done with the EMBB_ATOMIC_GENERAL_TEMPLATE macro. + * Example: + * + * This code... + * ==== + * EMBB_ATOMIC_GENERAL_TEMPLATE(load_implementation) + * EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER (1, + * load_implementation, Load, embb_internal__atomic_load_ + * ==== + * + * ...will generate: + * ==== + * template< typename TYPE > class load_implementation {}; + * + * template<> class load_implementation< EMBB_BASE_BASIC_TYPE_SIZE_1 > { + * public: + * static inline EMBB_BASE_BASIC_TYPE_SIZE_1 Load + * ( EMBB_BASE_BASIC_TYPE_SIZE_1 * par1) { + * return embb_internal__atomic_load_1(par1); + * } + * }; + * ==== + */ + +/** +* \def EMBB_ATOMIC_GENERAL_TEMPLATE +* See general comment on top of file +* \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ +* lib (here) +*/ +#define EMBB_ATOMIC_GENERAL_TEMPLATE(IMPLEMENTATION_CLASS) \ + template< typename TYPE > \ +class IMPLEMENTATION_CLASS \ +{}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1) \ + { \ + return EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1); \ + } \ +}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) par2) \ + { \ + return EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1, par2); \ + } \ +}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline int \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par2, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) par3) \ + { \ + return EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1, par2, par3); \ + } \ +}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline void \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) par2) \ + { \ + EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1, par2); \ + } \ +}; + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * The macro calls below actually define the atomic implementations using the + * macros defined in this file, as documented above. + */ + +// load_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(load_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(1, load_implementation, Load, embb_internal__atomic_load_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(2, load_implementation, Load, embb_internal__atomic_load_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(4, load_implementation, Load, embb_internal__atomic_load_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(8, load_implementation, Load, embb_internal__atomic_load_) +#endif + +// fetch_and_add_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(fetch_and_add_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(1, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(2, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(4, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(8, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +#endif + +// store_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(store_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, store_implementation, Store, embb_internal__atomic_store_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, store_implementation, Store, embb_internal__atomic_store_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, store_implementation, Store, embb_internal__atomic_store_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, store_implementation, Store, embb_internal__atomic_store_) +#endif + +// and_assign_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(and_assign_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +#endif + +// compare_and_swap_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(compare_and_swap_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(1, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(2, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(4, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(8, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +#endif + +// or_assign_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(or_assign_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +#endif + +// xor_assign_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(xor_assign_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +#endif + +// swap_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(swap_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(1, swap_implementation, Swap, embb_internal__atomic_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(2, swap_implementation, Swap, embb_internal__atomic_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(4, swap_implementation, Swap, embb_internal__atomic_swap_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(8, swap_implementation, Swap, embb_internal__atomic_swap_) +#endif + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // DOXYGEN + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_IMPLEMENTATION_H_ diff --git b/base_cpp/include/embb/base/internal/atomic/atomic_integer.h a/base_cpp/include/embb/base/internal/atomic/atomic_integer.h new file mode 100644 index 0000000..c609bd9 --- /dev/null +++ a/base_cpp/include/embb/base/internal/atomic/atomic_integer.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_INTEGER_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_INTEGER_H_ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * Subclass for integers. + * This class provides bitwise Boolean operations to manipulate integers. + * + * \tparam BaseType Underlying type (must be an integral type) + */ +template +class AtomicInteger : public AtomicArithmetic { + private: + typedef typename AtomicArithmetic:: + NativeType NativeType; + + public: + // See base class + AtomicInteger(); + // See base class + explicit AtomicInteger(BaseType val); + + // See base class + BaseType operator=(BaseType val); + + // See base class + bool IsInteger() const; + + // The methods below are documented in atomic.h + void operator&=(BaseType val); + void operator|=(BaseType val); + void operator^=(BaseType val); +}; + +template +inline AtomicInteger::AtomicInteger() : +AtomicArithmetic() {} + +template +inline AtomicInteger::AtomicInteger(BaseType val) : +AtomicArithmetic(val) {} + +template +inline BaseType AtomicInteger::operator=(BaseType val) { + return AtomicArithmetic::operator=(val); +} + +template +inline bool AtomicInteger::IsInteger() const { + return true; +} + +template +inline void AtomicInteger::operator&=(BaseType val) { + NativeType native_operand; + memcpy(&native_operand, &val, sizeof(val)); + + and_assign_implementation:: + and_assign(&this->AtomicValue, native_operand); +} + +template +inline void AtomicInteger::operator|=(BaseType val) { + NativeType native_operand; + memcpy(&native_operand, &val, sizeof(val)); + + or_assign_implementation:: + or_assign(&this->AtomicValue, native_operand); +} + +template +inline void AtomicInteger::operator^=(BaseType val) { + NativeType native_operand; + memcpy(&native_operand, &val, sizeof(val)); + + xor_assign_implementation:: + xor_assign(&this->AtomicValue, native_operand); +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_INTEGER_H_ diff --git b/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h a/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h new file mode 100644 index 0000000..5a5bcae --- /dev/null +++ a/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_POINTER_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_POINTER_H_ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * Subclass for pointers. + * This class provides dereferencing operators for pointers. + * + * \tparam BaseType Underlying type (must be a non-void pointer type) + */ +template +class AtomicPointer : public AtomicArithmetic { + private: + typedef typename AtomicArithmetic:: + NativeType NativeType; + + public: + // See base class + AtomicPointer(); + // See base class + explicit AtomicPointer(BaseType* ptr); + + // See base class + BaseType* operator=(BaseType* ptr); + + // See base class + bool IsPointer() const; + + // The methods below are documented in atomic.h + BaseType* operator->() const; + BaseType& operator*() const; +}; + +template +inline AtomicPointer::AtomicPointer() : + AtomicArithmetic() {} + +template +inline AtomicPointer:: + AtomicPointer(BaseType* val) : + AtomicArithmetic(val) {} + +template +inline BaseType* AtomicPointer:: + operator=(BaseType* val) { + return AtomicArithmetic:: + operator=(val); +} + +template +inline bool AtomicPointer:: + IsPointer() const { + return true; +} + +template +inline BaseType* AtomicPointer:: + operator->() const { + return this->Load(); +} + +template +inline BaseType& AtomicPointer:: + operator*() const { + return *(this->Load()); +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_POINTER_H_ diff --git b/base_cpp/include/embb/base/internal/atomic/atomic_utility.h a/base_cpp/include/embb/base/internal/atomic/atomic_utility.h new file mode 100644 index 0000000..89f08c9 --- /dev/null +++ a/base_cpp/include/embb/base/internal/atomic/atomic_utility.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_UTILITY_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_UTILITY_H_ + +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +template struct AtomicTraits; + +/** + * The templates below are use to map the size in bytes to the respective + * native type and to define this type. The basic types are defined in the + * base_c component. + */ + +// Specialization for bytes +template<> struct AtomicTraits<1> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_1 NativeType; }; + +// Specialization for half-words +template<> struct AtomicTraits<2> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_2 NativeType; }; + +// Specialization for words +template<> struct AtomicTraits<4> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_4 NativeType; }; + +// Specialization for double-words +template<> struct AtomicTraits<8> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_8 NativeType; }; + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_UTILITY_H_ diff --git b/base_cpp/include/embb/base/internal/cmake_config.h.in a/base_cpp/include/embb/base/internal/cmake_config.h.in new file mode 100644 index 0000000..02226f6 --- /dev/null +++ a/base_cpp/include/embb/base/internal/cmake_config.h.in @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_INTERNAL_CMAKE_CONFIG_H_ +#define EMBB_BASE_CPP_INTERNAL_CMAKE_CONFIG_H_ + +/* This file is used as input for CMake. CMake creates a file cmake_config.h in + its current build directory under the path builddir/embb/base/internal/. From + there, cmake_config.h can be included as usual using + #include + */ + +/** + * If defined, exceptions are used in the C++ part. Otherwise, error messages + * are written to stderr and the program is exited with corresponding error codes. + * Try blocks are converted to normal code blocks, whereas catch blocks are + * compiled but never executed. + */ +#cmakedefine EMBB_USE_EXCEPTIONS + +#endif /* EMBB_BASE_CPP_INTERNAL_CMAKE_CONFIG_H_ */ diff --git b/base_cpp/include/embb/base/internal/condition_variable-inl.h a/base_cpp/include/embb/base/internal/condition_variable-inl.h new file mode 100644 index 0000000..97f4caf --- /dev/null +++ a/base_cpp/include/embb/base/internal/condition_variable-inl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_CONDITION_VARIABLE_INL_H_ +#define EMBB_BASE_INTERNAL_CONDITION_VARIABLE_INL_H_ + +namespace embb { +namespace base { + +#include + +template +bool ConditionVariable::WaitFor(UniqueLock& lock, + const Duration& duration) { + int status = embb_condition_wait_for(&condition_var_, &(lock.mutex_->mutex_), + &(duration.rep_)); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Error in ConditionVariable::WaitFor"); + } + if (status == EMBB_TIMEDOUT) return false; + return true; +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_CONDITION_VARIABLE_INL_H_ diff --git b/base_cpp/include/embb/base/internal/config.h a/base_cpp/include/embb/base/internal/config.h new file mode 100644 index 0000000..ffab0c2 --- /dev/null +++ a/base_cpp/include/embb/base/internal/config.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_CONFIG_H_ +#define EMBB_BASE_INTERNAL_CONFIG_H_ + +#include +#include + +/* Disable exceptions in STL of MSVC. Leads to errors when used like this!!! */ +/*#if defined(EMBB_PLATFORM_COMPILER_MSVC) && !defined(EMBB_USE_EXCEPTIONS) +#define _HAS_EXCEPTIONS 0 +#endif*/ + +#endif // EMBB_BASE_INTERNAL_CONFIG_H_ diff --git b/base_cpp/include/embb/base/internal/duration-inl.h a/base_cpp/include/embb/base/internal/duration-inl.h new file mode 100644 index 0000000..982fcb7 --- /dev/null +++ a/base_cpp/include/embb/base/internal/duration-inl.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_DURATION_INL_H_ +#define EMBB_BASE_INTERNAL_DURATION_INL_H_ + +#include + +namespace embb { +namespace base { + +template +const Duration& Duration::Zero() { + static Duration zero; + return zero; +} + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +// Suppress non-thread-safe static initialization warning +// in Max() and Min() +#pragma warning(push) +#pragma warning(disable : 4640) +#endif + +template +const Duration& Duration::Max() { + static Duration maximum(Tick::Max()); + return maximum; +} + +template +const Duration& Duration::Min() { + static Duration minimum(Tick::Min()); + return minimum; +} + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) // Reset warning 4640 +#endif + +template +Duration::Duration() /*: rep_(EMBB_DURATION_INIT)*/ { + rep_.nanoseconds = 0; + rep_.seconds = 0; +} + +template +Duration::Duration(unsigned long long ticks) { + /*: rep_(EMBB_DURATION_INIT) << does not work with vs2012*/ + rep_.nanoseconds = 0; + rep_.seconds = 0; + Tick::SetAndCheck(rep_, ticks); +} + +template +Duration::Duration(const Duration& to_copy) { + /*: rep_(EMBB_DURATION_INIT)*/ + rep_.nanoseconds = 0; + rep_.seconds = 0; + Tick::SetAndCheck(rep_, Tick::Get(to_copy.rep_)); +} + +template +Duration& Duration::operator=(const Duration& to_assign) { + Tick::SetAndCheck(rep_, Tick::Get(to_assign.rep_)); + return *this; +} + +template +unsigned long long Duration::Count() const { + return Tick::Get(rep_); +} + +template +Duration& Duration::operator+=(const Duration& rhs) { + int status = embb_duration_add(&rep_, &(rhs.rep_)); + Tick::CheckExceptions(status, "Add-assign duration"); + return *this; +} + +template +Duration::Duration(const embb_duration_t& duration) : rep_() { + int status = Tick::Set(rep_, 0); + assert(status == EMBB_SUCCESS); + status = embb_duration_add(&rep_, &duration); + assert(status == EMBB_SUCCESS); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_DURATION_INL_H_ diff --git b/base_cpp/include/embb/base/internal/function0.h a/base_cpp/include/embb/base/internal/function0.h new file mode 100644 index 0000000..396c7da --- /dev/null +++ a/base_cpp/include/embb/base/internal/function0.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTION0_H_ +#define EMBB_BASE_INTERNAL_FUNCTION0_H_ + +#include +#include + +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function0 { + public: + virtual ~Function0() {} + virtual R operator () () = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer0 + : public Function0 { + public: + typedef R(*FuncPtrType)(); + explicit FunctionPointer0(FuncPtrType func) : function_(func) {} + virtual R operator () () { + return function_(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer0(function_); + } + + private: + FuncPtrType function_; +}; + +template <> +class FunctionPointer0 + : public Function0 { + public: + typedef void(*FuncPtrType)(); + explicit FunctionPointer0(FuncPtrType func) : function_(func) {} + virtual void operator () () { + function_(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer0(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer0 + : public Function0 { + public: + typedef R(C::*MemFuncPtrType)(); + typedef C & ClassRefType; + MemberFunctionPointer0(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer0(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer0 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () () { + return (object_.*function_)(); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer0(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer0 + : public Function0 { + public: + typedef void(C::*MemFuncPtrType)(); + typedef C & ClassRefType; + MemberFunctionPointer0(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer0(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer0 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () () { + (object_.*function_)(); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer0(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper0 + : public Function0 { + public: + FunctorWrapper0() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper0(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper0(FunctorWrapper0 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper0() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () () { + return (*object_)(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper0(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper0 + : public Function0 { + public: + FunctorWrapper0() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper0(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper0(FunctorWrapper0 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper0() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () () { + (*object_)(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper0(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function0 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper0(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)()) { + Free(); + function_ = new(storage_) + internal::FunctionPointer0(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper0(obj); + } + explicit Function(R(*func)()) { + function_ = new(storage_) + internal::FunctionPointer0(func); + } + template + Function(C & obj, R(C::*func)()) { + function_ = new(storage_) + internal::MemberFunctionPointer0(obj, func); + } + R operator () () { + return (*function_)(); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer0)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function0(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)()) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)()) { + return Function(func); +} + +// bind to Function0 +template +Function Bind( + C & obj, + R(C::*func)()) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)()) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION0_H_ diff --git b/base_cpp/include/embb/base/internal/function1.h a/base_cpp/include/embb/base/internal/function1.h new file mode 100644 index 0000000..b880d86 --- /dev/null +++ a/base_cpp/include/embb/base/internal/function1.h @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTION1_H_ +#define EMBB_BASE_INTERNAL_FUNCTION1_H_ + +#include +#include + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function1 { + public: + virtual ~Function1() {} + virtual R operator () (T1) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer1 + : public Function1 { + public: + typedef R(*FuncPtrType)(T1); + explicit FunctionPointer1(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1) { + return function_(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer1(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer1 + : public Function1 { + public: + typedef void(*FuncPtrType)(T1); + explicit FunctionPointer1(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1) { + function_(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer1(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer1 + : public Function1 { + public: + typedef R(C::*MemFuncPtrType)(T1); + typedef C & ClassRefType; + MemberFunctionPointer1(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer1(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer1 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1) { + return (object_.*function_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer1(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer1 + : public Function1 { + public: + typedef void(C::*MemFuncPtrType)(T1); + typedef C & ClassRefType; + MemberFunctionPointer1(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer1(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer1 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1) { + (object_.*function_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer1(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper1 + : public Function1 { + public: + FunctorWrapper1() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper1(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper1(FunctorWrapper1 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper1() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1) { + return (*object_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper1(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper1 + : public Function1 { + public: + FunctorWrapper1() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper1(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper1(FunctorWrapper1 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper1() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1) { + (*object_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper1(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound1Functor0 { + public: + Bound1Functor0(Function func, + T1 p1) : function_(func) + , p1_(p1) {} + Bound1Functor0(Bound1Functor0 const & func) : function_(func.function_) + , p1_(func.p1_) {} + R operator() () { + return function_(p1_); + } + + private: + Function function_; + T1 p1_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function1 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper1(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer1(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper1(obj); + } + explicit Function(R(*func)(T1)) { + function_ = new(storage_) + internal::FunctionPointer1(func); + } + template + Function(C & obj, R(C::*func)(T1)) { + function_ = new(storage_) + internal::MemberFunctionPointer1(obj, func); + } + R operator () (T1 p1) { + return (*function_)(p1); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer1)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function1(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1) { + return Function( + internal::Bound1Functor0( + func, p1)); +} + +template +Function Bind(R(*func)(T1), + T1 p1) { + return Bind(Function(func), p1); +} + +template +Function Bind(C & obj, R(C::*func)(T1), + T1 p1) { + return Bind(Function(obj, func), p1); +} + +// bind to Function1 +template +Function Bind( + C & obj, + R(C::*func)(T1), + Placeholder::Arg_1) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1), + Placeholder::Arg_1) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION1_H_ diff --git b/base_cpp/include/embb/base/internal/function2.h a/base_cpp/include/embb/base/internal/function2.h new file mode 100644 index 0000000..3b9f9ba --- /dev/null +++ a/base_cpp/include/embb/base/internal/function2.h @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTION2_H_ +#define EMBB_BASE_INTERNAL_FUNCTION2_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function2 { + public: + virtual ~Function2() {} + virtual R operator () (T1, T2) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer2 + : public Function2 { + public: + typedef R(*FuncPtrType)(T1, T2); + explicit FunctionPointer2(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2) { + return function_(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer2(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer2 + : public Function2 { + public: + typedef void(*FuncPtrType)(T1, T2); + explicit FunctionPointer2(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2) { + function_(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer2(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer2 + : public Function2 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2); + typedef C & ClassRefType; + MemberFunctionPointer2(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer2(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer2 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2) { + return (object_.*function_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer2(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer2 + : public Function2 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2); + typedef C & ClassRefType; + MemberFunctionPointer2(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer2(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer2 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2) { + (object_.*function_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer2(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper2 + : public Function2 { + public: + FunctorWrapper2() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper2(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper2(FunctorWrapper2 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper2() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2) { + return (*object_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper2(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper2 + : public Function2 { + public: + FunctorWrapper2() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper2(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper2(FunctorWrapper2 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper2() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2) { + (*object_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper2(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound2Functor0 { + public: + Bound2Functor0(Function func, + T1 p1, T2 p2) : function_(func) + , p1_(p1), p2_(p2) {} + Bound2Functor0(Bound2Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_) {} + R operator() () { + return function_(p1_, p2_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; +}; + +// bind to function1 +template +class Bound2Functor1_Arg1 { + public: + Bound2Functor1_Arg1(Function func + , T2 p2) : function_(func) + , p2_(p2) {} + Bound2Functor1_Arg1(Bound2Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_) {} + R operator() (T1 p1) { + return function_(p1, p2_); + } + + private: + Function function_; + T2 p2_; +}; + +template +class Bound2Functor1_Arg2 { + public: + Bound2Functor1_Arg2(Function func + , T1 p1) : function_(func) + , p1_(p1) {} + Bound2Functor1_Arg2(Bound2Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_) {} + R operator() (T2 p2) { + return function_(p1_, p2); + } + + private: + Function function_; + T1 p1_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function2 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper2(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer2(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper2(obj); + } + explicit Function(R(*func)(T1, T2)) { + function_ = new(storage_) + internal::FunctionPointer2(func); + } + template + Function(C & obj, R(C::*func)(T1, T2)) { + function_ = new(storage_) + internal::MemberFunctionPointer2(obj, func); + } + R operator () (T1 p1, T2 p2) { + return (*function_)(p1, p2); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer2)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function2(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2) { + return Function( + internal::Bound2Functor0( + func, p1, p2)); +} + +template +Function Bind(R(*func)(T1, T2), + T1 p1, T2 p2) { + return Bind(Function(func), p1, p2); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2), + T1 p1, T2 p2) { + return Bind(Function(obj, func), p1, p2); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2) { + return Function( + internal::Bound2Functor1_Arg1(func + , p2)); +} + +template +Function Bind(R(*func)(T1, T2) + , Placeholder::Arg_1 p1, T2 p2) { + return Bind(Function(func), p1, p2); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2) + , Placeholder::Arg_1 p1, T2 p2) { + return Bind(Function(obj, func), p1, p2); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1) { + return Function( + internal::Bound2Functor1_Arg2(func + , p1)); +} + +template +Function Bind(R(*func)(T1, T2) + , T1 p1, Placeholder::Arg_1 p2) { + return Bind(Function(func), p1, p2); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2) + , T1 p1, Placeholder::Arg_1 p2) { + return Bind(Function(obj, func), p1, p2); +} + +// bind to Function2 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2), + Placeholder::Arg_1, + Placeholder::Arg_2) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2), + Placeholder::Arg_1, + Placeholder::Arg_2) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION2_H_ diff --git b/base_cpp/include/embb/base/internal/function3.h a/base_cpp/include/embb/base/internal/function3.h new file mode 100644 index 0000000..545feca --- /dev/null +++ a/base_cpp/include/embb/base/internal/function3.h @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTION3_H_ +#define EMBB_BASE_INTERNAL_FUNCTION3_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function3 { + public: + virtual ~Function3() {} + virtual R operator () (T1, T2, T3) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer3 + : public Function3 { + public: + typedef R(*FuncPtrType)(T1, T2, T3); + explicit FunctionPointer3(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2, T3 p3) { + return function_(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer3(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer3 + : public Function3 { + public: + typedef void(*FuncPtrType)(T1, T2, T3); + explicit FunctionPointer3(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2, T3 p3) { + function_(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer3(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer3 + : public Function3 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2, T3); + typedef C & ClassRefType; + MemberFunctionPointer3(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer3(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer3 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2, T3 p3) { + return (object_.*function_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer3(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer3 + : public Function3 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2, T3); + typedef C & ClassRefType; + MemberFunctionPointer3(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer3(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer3 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2, T3 p3) { + (object_.*function_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer3(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper3 + : public Function3 { + public: + FunctorWrapper3() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper3(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper3(FunctorWrapper3 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper3() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2, T3 p3) { + return (*object_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper3(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper3 + : public Function3 { + public: + FunctorWrapper3() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper3(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper3(FunctorWrapper3 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper3() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2, T3 p3) { + (*object_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper3(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound3Functor0 { + public: + Bound3Functor0(Function func, + T1 p1, T2 p2, T3 p3) : function_(func) + , p1_(p1), p2_(p2), p3_(p3) {} + Bound3Functor0(Bound3Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_) {} + R operator() () { + return function_(p1_, p2_, p3_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; +}; + +// bind to function1 +template +class Bound3Functor1_Arg1 { + public: + Bound3Functor1_Arg1(Function func + , T2 p2, T3 p3) : function_(func) + , p2_(p2), p3_(p3) {} + Bound3Functor1_Arg1(Bound3Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_), p3_(func.p3_) {} + R operator() (T1 p1) { + return function_(p1, p2_, p3_); + } + + private: + Function function_; + T2 p2_; + T3 p3_; +}; + +template +class Bound3Functor1_Arg2 { + public: + Bound3Functor1_Arg2(Function func + , T1 p1, T3 p3) : function_(func) + , p1_(p1), p3_(p3) {} + Bound3Functor1_Arg2(Bound3Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_), p3_(func.p3_) {} + R operator() (T2 p2) { + return function_(p1_, p2, p3_); + } + + private: + Function function_; + T1 p1_; + T3 p3_; +}; + +template +class Bound3Functor1_Arg3 { + public: + Bound3Functor1_Arg3(Function func + , T1 p1, T2 p2) : function_(func) + , p1_(p1), p2_(p2) {} + Bound3Functor1_Arg3(Bound3Functor1_Arg3 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_) {} + R operator() (T3 p3) { + return function_(p1_, p2_, p3); + } + + private: + Function function_; + T1 p1_; + T2 p2_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function3 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper3(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2, T3)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer3(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper3(obj); + } + explicit Function(R(*func)(T1, T2, T3)) { + function_ = new(storage_) + internal::FunctionPointer3(func); + } + template + Function(C & obj, R(C::*func)(T1, T2, T3)) { + function_ = new(storage_) + internal::MemberFunctionPointer3(obj, func); + } + R operator () (T1 p1, T2 p2, T3 p3) { + return (*function_)(p1, p2, p3); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer3)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function3(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2, T3)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2, T3)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2, T3 p3) { + return Function( + internal::Bound3Functor0( + func, p1, p2, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3), + T1 p1, T2 p2, T3 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3), + T1 p1, T2 p2, T3 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2, T3 p3) { + return Function( + internal::Bound3Functor1_Arg1(func + , p2, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3) + , Placeholder::Arg_1 p1, T2 p2, T3 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3) + , Placeholder::Arg_1 p1, T2 p2, T3 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1, T3 p3) { + return Function( + internal::Bound3Functor1_Arg2(func + , p1, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3) + , T1 p1, Placeholder::Arg_1 p2, T3 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3) + , T1 p1, Placeholder::Arg_1 p2, T3 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, Placeholder::Arg_1) { + return Function( + internal::Bound3Functor1_Arg3(func + , p1, p2)); +} + +template +Function Bind(R(*func)(T1, T2, T3) + , T1 p1, T2 p2, Placeholder::Arg_1 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3) + , T1 p1, T2 p2, Placeholder::Arg_1 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +// bind to Function3 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2, T3), + Placeholder::Arg_1, + Placeholder::Arg_2, + Placeholder::Arg_3) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2, T3), + Placeholder::Arg_1, + Placeholder::Arg_2, + Placeholder::Arg_3) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION3_H_ diff --git b/base_cpp/include/embb/base/internal/function4.h a/base_cpp/include/embb/base/internal/function4.h new file mode 100644 index 0000000..22415e5 --- /dev/null +++ a/base_cpp/include/embb/base/internal/function4.h @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTION4_H_ +#define EMBB_BASE_INTERNAL_FUNCTION4_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function4 { + public: + virtual ~Function4() {} + virtual R operator () (T1, T2, T3, T4) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer4 + : public Function4 { + public: + typedef R(*FuncPtrType)(T1, T2, T3, T4); + explicit FunctionPointer4(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return function_(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer4(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer4 + : public Function4 { + public: + typedef void(*FuncPtrType)(T1, T2, T3, T4); + explicit FunctionPointer4(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + function_(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer4(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer4 + : public Function4 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2, T3, T4); + typedef C & ClassRefType; + MemberFunctionPointer4(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer4(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer4 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return (object_.*function_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer4(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer4 + : public Function4 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2, T3, T4); + typedef C & ClassRefType; + MemberFunctionPointer4(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer4(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer4 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + (object_.*function_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer4(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper4 + : public Function4 { + public: + FunctorWrapper4() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper4(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper4(FunctorWrapper4 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper4() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return (*object_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper4(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper4 + : public Function4 { + public: + FunctorWrapper4() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper4(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper4(FunctorWrapper4 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper4() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + (*object_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper4(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound4Functor0 { + public: + Bound4Functor0(Function func, + T1 p1, T2 p2, T3 p3, T4 p4) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p4_(p4) {} + Bound4Functor0(Bound4Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) + {} + R operator() () { + return function_(p1_, p2_, p3_, p4_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T4 p4_; +}; + +// bind to function1 +template +class Bound4Functor1_Arg1 { + public: + Bound4Functor1_Arg1(Function func + , T2 p2, T3 p3, T4 p4) : function_(func) + , p2_(p2), p3_(p3), p4_(p4) {} + Bound4Functor1_Arg1(Bound4Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) {} + R operator() (T1 p1) { + return function_(p1, p2_, p3_, p4_); + } + + private: + Function function_; + T2 p2_; + T3 p3_; + T4 p4_; +}; + +template +class Bound4Functor1_Arg2 { + public: + Bound4Functor1_Arg2(Function func + , T1 p1, T3 p3, T4 p4) : function_(func) + , p1_(p1), p3_(p3), p4_(p4) {} + Bound4Functor1_Arg2(Bound4Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_), p3_(func.p3_), p4_(func.p4_) {} + R operator() (T2 p2) { + return function_(p1_, p2, p3_, p4_); + } + + private: + Function function_; + T1 p1_; + T3 p3_; + T4 p4_; +}; + +template +class Bound4Functor1_Arg3 { + public: + Bound4Functor1_Arg3(Function func + , T1 p1, T2 p2, T4 p4) : function_(func) + , p1_(p1), p2_(p2), p4_(p4) {} + Bound4Functor1_Arg3(Bound4Functor1_Arg3 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p4_(func.p4_) {} + R operator() (T3 p3) { + return function_(p1_, p2_, p3, p4_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T4 p4_; +}; + +template +class Bound4Functor1_Arg4 { + public: + Bound4Functor1_Arg4(Function func + , T1 p1, T2 p2, T3 p3) : function_(func) + , p1_(p1), p2_(p2), p3_(p3) {} + Bound4Functor1_Arg4(Bound4Functor1_Arg4 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_) {} + R operator() (T4 p4) { + return function_(p1_, p2_, p3_, p4); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function4 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper4(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2, T3, T4)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer4(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper4(obj); + } + explicit Function(R(*func)(T1, T2, T3, T4)) { + function_ = new(storage_) + internal::FunctionPointer4(func); + } + template + Function(C & obj, R(C::*func)(T1, T2, T3, T4)) { + function_ = new(storage_) + internal::MemberFunctionPointer4(obj, func); + } + R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return (*function_)(p1, p2, p3, p4); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer4)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function4(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2, T3, T4)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2, T3, T4)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2, T3 p3, T4 p4) { + return Function( + internal::Bound4Functor0( + func, p1, p2, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4), + T1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4), + T1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2, T3 p3, T4 p4) { + return Function( + internal::Bound4Functor1_Arg1(func + , p2, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1, T3 p3, T4 p4) { + return Function( + internal::Bound4Functor1_Arg2(func + , p1, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, Placeholder::Arg_1, T4 p4) { + return Function( + internal::Bound4Functor1_Arg3(func + , p1, p2, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1) { + return Function( + internal::Bound4Functor1_Arg4(func + , p1, p2, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +// bind to Function4 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2, T3, T4), + Placeholder::Arg_1, + Placeholder::Arg_2, + Placeholder::Arg_3, + Placeholder::Arg_4) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2, T3, T4), + Placeholder::Arg_1, + Placeholder::Arg_2, + Placeholder::Arg_3, + Placeholder::Arg_4) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION4_H_ diff --git b/base_cpp/include/embb/base/internal/function5.h a/base_cpp/include/embb/base/internal/function5.h new file mode 100644 index 0000000..3221e57 --- /dev/null +++ a/base_cpp/include/embb/base/internal/function5.h @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTION5_H_ +#define EMBB_BASE_INTERNAL_FUNCTION5_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function5 { + public: + virtual ~Function5() {} + virtual R operator () (T1, T2, T3, T4, T5) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer5 + : public Function5 { + public: + typedef R(*FuncPtrType)(T1, T2, T3, T4, T5); + explicit FunctionPointer5(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return function_(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer5(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer5 + : public Function5 { + public: + typedef void(*FuncPtrType)(T1, T2, T3, T4, T5); + explicit FunctionPointer5(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + function_(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer5(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer5 + : public Function5 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2, T3, T4, T5); + typedef C & ClassRefType; + MemberFunctionPointer5(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer5(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer5 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return (object_.*function_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer5(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer5 + : public Function5 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2, T3, T4, T5); + typedef C & ClassRefType; + MemberFunctionPointer5(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer5(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer5 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + (object_.*function_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer5(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper5 + : public Function5 { + public: + FunctorWrapper5() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper5(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper5(FunctorWrapper5 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper5() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return (*object_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper5(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper5 + : public Function5 { + public: + FunctorWrapper5() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper5(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper5(FunctorWrapper5 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper5() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + (*object_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper5(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound5Functor0 { + public: + Bound5Functor0(Function func, + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) {} + Bound5Functor0(Bound5Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) + , p5_(func.p5_) {} + R operator() () { + return function_(p1_, p2_, p3_, p4_, p5_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T4 p4_; + T5 p5_; +}; + +// bind to function1 +template +class Bound5Functor1_Arg1 { + public: + Bound5Functor1_Arg1(Function func + , T2 p2, T3 p3, T4 p4, T5 p5) : function_(func) + , p2_(p2), p3_(p3), p4_(p4), p5_(p5) {} + Bound5Functor1_Arg1(Bound5Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_), p3_(func.p3_), p4_(func.p4_), p5_(func.p5_) {} + R operator() (T1 p1) { + return function_(p1, p2_, p3_, p4_, p5_); + } + + private: + Function function_; + T2 p2_; + T3 p3_; + T4 p4_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg2 { + public: + Bound5Functor1_Arg2(Function func + , T1 p1, T3 p3, T4 p4, T5 p5) : function_(func) + , p1_(p1), p3_(p3), p4_(p4), p5_(p5) {} + Bound5Functor1_Arg2(Bound5Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_), p3_(func.p3_), p4_(func.p4_), p5_(func.p5_) {} + R operator() (T2 p2) { + return function_(p1_, p2, p3_, p4_, p5_); + } + + private: + Function function_; + T1 p1_; + T3 p3_; + T4 p4_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg3 { + public: + Bound5Functor1_Arg3(Function func + , T1 p1, T2 p2, T4 p4, T5 p5) : function_(func) + , p1_(p1), p2_(p2), p4_(p4), p5_(p5) {} + Bound5Functor1_Arg3(Bound5Functor1_Arg3 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p4_(func.p4_), p5_(func.p5_) {} + R operator() (T3 p3) { + return function_(p1_, p2_, p3, p4_, p5_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T4 p4_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg4 { + public: + Bound5Functor1_Arg4(Function func + , T1 p1, T2 p2, T3 p3, T5 p5) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p5_(p5) {} + Bound5Functor1_Arg4(Bound5Functor1_Arg4 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p5_(func.p5_) {} + R operator() (T4 p4) { + return function_(p1_, p2_, p3_, p4, p5_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg5 { + public: + Bound5Functor1_Arg5(Function func + , T1 p1, T2 p2, T3 p3, T4 p4) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p4_(p4) {} + Bound5Functor1_Arg5(Bound5Functor1_Arg5 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) {} + R operator() (T5 p5) { + return function_(p1_, p2_, p3_, p4_, p5); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T4 p4_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function5 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper5(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2, T3, T4, T5)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer5(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper5(obj); + } + explicit Function(R(*func)(T1, T2, T3, T4, T5)) { + function_ = new(storage_) + internal::FunctionPointer5(func); + } + template + Function(C & obj, R(C::*func)(T1, T2, T3, T4, T5)) { + function_ = new(storage_) + internal::MemberFunctionPointer5(obj, func); + } + R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return (*function_)(p1, p2, p3, p4, p5); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer5)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function5(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2, T3, T4, T5)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2, T3, T4, T5)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor0( + func, p1, p2, p3, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5), + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5), + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor1_Arg1(func + , p2, p3, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1, T3 p3, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor1_Arg2(func + , p1, p3, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, Placeholder::Arg_1, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor1_Arg3(func + , p1, p2, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1, T5 p5) { + return Function( + internal::Bound5Functor1_Arg4(func + , p1, p2, p3, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, T3 p3, T4 p4, Placeholder::Arg_1) { + return Function( + internal::Bound5Functor1_Arg5(func + , p1, p2, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, T4 p4, Placeholder::Arg_1 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, T4 p4, Placeholder::Arg_1 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +// bind to Function5 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2, T3, T4, T5), + Placeholder::Arg_1, + Placeholder::Arg_2, + Placeholder::Arg_3, + Placeholder::Arg_4, + Placeholder::Arg_5) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2, T3, T4, T5), + Placeholder::Arg_1, + Placeholder::Arg_2, + Placeholder::Arg_3, + Placeholder::Arg_4, + Placeholder::Arg_5) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION5_H_ diff --git b/base_cpp/include/embb/base/internal/functionT.h a/base_cpp/include/embb/base/internal/functionT.h new file mode 100644 index 0000000..9935cb7 --- /dev/null +++ a/base_cpp/include/embb/base/internal/functionT.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_FUNCTIONT_H_ +#define EMBB_BASE_INTERNAL_FUNCTIONT_H_ + +#include + +namespace embb { +namespace base { + +using embb::base::internal::Nil; + +template < + typename, + typename = Nil, + typename = Nil, + typename = Nil, + typename = Nil, + typename = Nil > +class Function; + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTIONT_H_ diff --git b/base_cpp/include/embb/base/internal/memory_allocation-inl.h a/base_cpp/include/embb/base/internal/memory_allocation-inl.h new file mode 100644 index 0000000..7565bc4 --- /dev/null +++ a/base_cpp/include/embb/base/internal/memory_allocation-inl.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_MEMORY_ALLOCATION_INL_H_ +#define EMBB_BASE_INTERNAL_MEMORY_ALLOCATION_INL_H_ + +#include + +#include + +namespace embb { +namespace base { + +inline void* Allocatable::operator new(size_t size) { + return Allocation::Allocate(size); +} + +inline void Allocatable::operator delete(void* ptr, size_t) { + Allocation::Free(ptr); +} + +inline void* Allocatable::operator new[](size_t size) { + return Allocation::Allocate(size); +} + +inline void Allocatable::operator delete[](void* ptr, size_t) { + Allocation::Free(ptr); +} + +inline void* CacheAlignedAllocatable::operator new(size_t size) { + return Allocation::AllocateCacheAligned(size); +} + +inline void CacheAlignedAllocatable::operator delete(void* ptr, size_t) { + Allocation::FreeAligned(ptr); +} + +inline void* CacheAlignedAllocatable::operator new[](size_t size) { + return Allocation::AllocateCacheAligned(size); +} + +inline void CacheAlignedAllocatable::operator delete[](void* ptr, size_t) { + Allocation::FreeAligned(ptr); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_MEMORY_ALLOCATION_INL_H_ diff --git b/base_cpp/include/embb/base/internal/mutex-inl.h a/base_cpp/include/embb/base/internal/mutex-inl.h new file mode 100644 index 0000000..ffdbf12 --- /dev/null +++ a/base_cpp/include/embb/base/internal/mutex-inl.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_MUTEX_INL_H_ +#define EMBB_BASE_INTERNAL_MUTEX_INL_H_ + +#include +#include + +namespace embb { +namespace base { + +template +UniqueLock::UniqueLock() : mutex_(NULL), locked_(false) { +} + +template +UniqueLock::UniqueLock(Mutex& mutex) + : mutex_(&mutex), locked_(false) { + mutex_->Lock(); + locked_ = true; +} + +template +UniqueLock::UniqueLock(Mutex& mutex, DeferLockTag) + : mutex_(&mutex), locked_(false) { +} + +template +UniqueLock::UniqueLock(Mutex& mutex, embb::base::TryLockTag) + : mutex_(&mutex), locked_(false) { + locked_ = mutex_->TryLock(); +} + +template +UniqueLock::UniqueLock(Mutex& mutex, AdoptLockTag) + : mutex_(&mutex), locked_(true) { +} + +template +UniqueLock::~UniqueLock() { + if (OwnsLock()) { + mutex_->Unlock(); + } +} + +template +void UniqueLock::Lock() { + if ((mutex_ == NULL) || locked_) { + EMBB_THROW(ErrorException, "Mutex not set or locked"); + } + mutex_->Lock(); + locked_ = true; +} + +template +bool UniqueLock::TryLock() { + if ((mutex_ == NULL) || locked_) { + EMBB_THROW(ErrorException, "Mutex not set or locked"); + } + locked_ = mutex_->TryLock(); + return locked_; +} + +template +void UniqueLock::Unlock() { + if ((mutex_ == NULL) || (!locked_)) { + EMBB_THROW(ErrorException, "Mutex not set or unlocked"); + } + locked_ = false; + mutex_->Unlock(); +} + +template +void UniqueLock::Swap(UniqueLock& other) { + std::swap(mutex_, other.mutex_); + std::swap(locked_, other.locked_); +} + +template +Mutex* UniqueLock::Release() { + Mutex* toRelease = mutex_; + mutex_ = NULL; + locked_ = false; + return toRelease; +} + +template +bool UniqueLock::OwnsLock() const { + assert(!(locked_ && (mutex_ == NULL))); + return locked_; +} + + + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_MUTEX_INL_H_ diff --git b/base_cpp/include/embb/base/internal/nil.h a/base_cpp/include/embb/base/internal/nil.h new file mode 100644 index 0000000..36503c0 --- /dev/null +++ a/base_cpp/include/embb/base/internal/nil.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_NIL_H_ +#define EMBB_BASE_INTERNAL_NIL_H_ + +namespace embb { +namespace base { +namespace internal { + +/* + * Internal helper class to mark not specified template parameters. + */ +class Nil {}; + +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_NIL_H_ diff --git b/base_cpp/include/embb/base/internal/platform.h a/base_cpp/include/embb/base/internal/platform.h new file mode 100644 index 0000000..65b82f1 --- /dev/null +++ a/base_cpp/include/embb/base/internal/platform.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_PLATFORM_H_ +#define EMBB_BASE_INTERNAL_PLATFORM_H_ + +/** + * \file Contains platform-specific includes, typedefs, and defines. + */ + +#include +#include + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +namespace embb { +namespace base { +namespace internal { + +typedef embb_thread_t ThreadType; +typedef DWORD IDType; +typedef embb_mutex_t MutexType; +typedef embb_condition_t ConditionVariableType; + +} // namespace internal +} // namespace base +} // namespace embb + +#elif defined EMBB_PLATFORM_THREADING_POSIXTHREADS + +namespace embb { +namespace base { +namespace internal { + +typedef embb_thread_t ThreadType; +typedef embb_thread_id_t IDType; +typedef embb_mutex_t MutexType; +typedef embb_condition_t ConditionVariableType; + +} // namespace internal +} // namespace base +} // namespace embb + +#else // EMBB_PLATFORM_THREADING_POSIXTHREADS + +#error "No threading platform defined!" + +#endif // else + +#endif // EMBB_BASE_INTERNAL_PLATFORM_H_ diff --git b/base_cpp/include/embb/base/internal/thread-inl.h a/base_cpp/include/embb/base/internal/thread-inl.h new file mode 100644 index 0000000..8c856f0 --- /dev/null +++ a/base_cpp/include/embb/base/internal/thread-inl.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_THREAD_INL_H_ +#define EMBB_BASE_INTERNAL_THREAD_INL_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +template +Thread::Thread(Function function) : rep_() { + internal::ThreadClosure* closure = + Allocation::New >(function); + int result = embb_thread_create( + &rep_, + NULL, + internal::ThreadClosure::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +Thread::Thread(CoreSet& core_set, Function function) : rep_() { + typedef internal::ThreadClosure Closure; + Closure* closure = Allocation::New(function); + int result = embb_thread_create( + &rep_, + &core_set.rep_, + internal::ThreadClosure::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +Thread::Thread(Function function, Arg1 arg1) : rep_() { + typedef internal::ThreadClosureArg1 Closure; + Closure* closure = Allocation::New(function, arg1); + int result = embb_thread_create( + &rep_, + NULL, + internal::ThreadClosureArg1::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +Thread::Thread(Function function, Arg1 arg1, Arg2 arg2) : rep_() { + typedef internal::ThreadClosureArg2 Closure; + Closure* closure = Allocation::New(function, arg1, arg2); + int result = embb_thread_create( + &rep_, + NULL, + internal::ThreadClosureArg2::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +void Thread::CheckThreadCreationErrors(int result, ThreadClosure* closure) { + if (result != EMBB_SUCCESS) { + if (closure != NULL) { + Allocation::Delete(closure); + } + const char* message = "Could not create thread."; + if (result == EMBB_NOMEM) { + EMBB_THROW(NoMemoryException, message); + } + EMBB_THROW(ErrorException, message); + } +} + +template +std::basic_ostream& + operator<<(std::basic_ostream& os, Thread::ID id) { + return os << id.id_; +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_THREAD_INL_H_ diff --git b/base_cpp/include/embb/base/internal/thread_closures.h a/base_cpp/include/embb/base/internal/thread_closures.h new file mode 100644 index 0000000..9de9d72 --- /dev/null +++ a/base_cpp/include/embb/base/internal/thread_closures.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_THREAD_CLOSURES_H_ +#define EMBB_BASE_INTERNAL_THREAD_CLOSURES_H_ + +#include + +namespace embb { +namespace base { +namespace internal { + +/** + * Thread closure for thread start function with no arguments. + * + * Provides a thread start function from which a priorly stored function object + * is called. + */ +template +struct ThreadClosure { + Function function_; + + static int ThreadStart(void* arg) { + ThreadClosure *self = static_cast(arg); + self->function_(); + Allocation::Delete(self); + return 0; + } + + explicit ThreadClosure(const Function& function) : function_(function) {} +}; + +/** + * Thread closure for thread start function with one argument. + * + * Provides a thread start function from which a priorly stored function object + * is called. + */ +template +struct ThreadClosureArg1 { + Function function_; + Arg1 arg1_; + + static int ThreadStart(void* arg) { + ThreadClosureArg1 *self = static_cast(arg); + self->function_(self->arg1_); + Allocation::Delete(self); + return 0; + } + + ThreadClosureArg1(const Function& function, const Arg1& arg1) + : function_(function), arg1_(arg1) {} +}; + +/** + * Thread closure for thread start function with two arguments. + * + * Provides a thread start function from which a priorly stored function object + * is called. + */ +template +struct ThreadClosureArg2 { + Function function_; + Arg1 arg1_; + Arg2 arg2_; + + static int ThreadStart(void* arg) { + ThreadClosureArg2 *self = static_cast(arg); + self->function_(self->arg1_, self->arg2_); + Allocation::Delete(self); + return 0; + } + + ThreadClosureArg2( + const Function& function, const Arg1& arg1, const Arg2& arg2) + : function_(function), arg1_(arg1), arg2_(arg2) {} +}; + +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_THREAD_CLOSURES_H_ diff --git b/base_cpp/include/embb/base/internal/thread_specific_storage-inl.h a/base_cpp/include/embb/base/internal/thread_specific_storage-inl.h new file mode 100644 index 0000000..e142aee --- /dev/null +++ a/base_cpp/include/embb/base/internal/thread_specific_storage-inl.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_INTERNAL_THREAD_SPECIFIC_STORAGE_INL_H_ +#define EMBB_BASE_INTERNAL_THREAD_SPECIFIC_STORAGE_INL_H_ + +#include +#include +#include + +#include + +namespace embb { +namespace base { + +template +ThreadSpecificStorage::ThreadSpecificStorage() + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage(Initializer initializer) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage( + Initializer1 initializer1, Initializer2 initializer2) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer1, initializer2); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage( + Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer1, initializer2, + initializer3); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage( + Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3, Initializer4 initializer4) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer1, initializer2, + initializer3, initializer4); + } +} + +template +ThreadSpecificStorage::~ThreadSpecificStorage() { + for (unsigned int i = 0; i < rep_.size; i++) { + Type* value = static_cast(rep_.values[i]); + assert(value != NULL); + Allocation::Delete(value); + } + embb_tss_delete(&rep_); + Allocation::Free(usage_flags_); +} + +template +Type& ThreadSpecificStorage::Get() { + Type* value = static_cast(embb_tss_get(&rep_)); + if (value == NULL) { + EMBB_THROW(ErrorException, "No thread index could be obtained"); + } + unsigned int thread_index = 0; + int status = embb_internal_thread_index(&thread_index); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + usage_flags_[thread_index] = true; + return *value; +} + +template +const Type& ThreadSpecificStorage::Get() const { + const Type* value = static_cast(embb_tss_get(&rep_)); + if (value == NULL) { + EMBB_THROW(ErrorException, "No thread index could be obtained"); + } + unsigned int thread_index = 0; + int status = embb_internal_thread_index(&thread_index); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + usage_flags_[thread_index] = true; + return *value; +} + +template +void ThreadSpecificStorage::Prepare() { + int status = embb_tss_create(&rep_); + if (status == EMBB_NOMEM) { + EMBB_THROW(NoMemoryException, "Not enough memory to allocate " + "thread-specific storage"); + } + usage_flags_ = static_cast( + Allocation::Allocate(sizeof(bool) * rep_.size)); + for (unsigned int i = 0; i < rep_.size; i++) { + usage_flags_[i] = false; + } +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_THREAD_SPECIFIC_STORAGE_INL_H_ diff --git b/base_cpp/include/embb/base/log.h a/base_cpp/include/embb/base/log.h new file mode 100644 index 0000000..275e95a --- /dev/null +++ a/base_cpp/include/embb/base/log.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_LOG_H_ +#define EMBB_BASE_LOG_H_ + +#include + +/** + * \defgroup CPP_LOG Logging + * \ingroup CPP_BASE + * Simple logging facilities. + */ + +namespace embb { +namespace base { + +/** + * Simple logging facilities. + * + * \ingroup CPP_LOG + */ +class Log { + private: + // do not allow construction + Log(); + + public: + /** + * Sets the global log level. + * This determines what messages will be shown, messages with a more detailed + * log level will be filtered out. The default log level is EMBB_LOG_LEVEL_NONE. + * \notthreadsafe + */ + static void SetLogLevel( + embb_log_level_t log_level /**< [in] Log level to use for + filtering */ + ); + + /** + * Sets the global logging function. + * The logging function implements the mechanism for transferring log messages + * to their destination. \c context is a pointer to data the user needs in the + * function to determine where the messages should go (may be NULL if no + * additional data is needed). The default logging function is + * embb_log_write_file() with context set to \c stdout. + * \see embb_log_function_t + * \notthreadsafe + */ + static void SetLogFunction( + void * context, /**< [in] User context to supply as the + first parameter of the logging + function*/ + embb_log_function_t func /**< [in] The logging function */ + ); + + /** + * Logs a message to the given channel with the specified log level. + * If the log level is greater than the configured log level for the channel, + * the message will be ignored. + * \see embb::base::Log::SetLogLevel, embb::base::Log::SetLogFunction + * \threadsafe + */ + static void Write( + char const * channel, /**< [in] User specified channel id + for filtering the log later on. + Might be NULL, channel identifier + will be "global" in that case */ + embb_log_level_t log_level, /**< [in] Log level to use */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined + by the format specifiers in + \c message */ + ); + + /** + * Logs a message to the given channel with EMBB_LOG_LEVEL_TRACE. + * In non-debug builds, this function does nothing. + * \see embb::base::Log::Write + * \threadsafe + */ + static void Trace( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined + by the format specifiers in + \c message */ + ); + + /** + * Logs a message to the given channel with EMBB_LOG_LEVEL_INFO. + * In non-debug builds, this function does nothing. + * \see embb::base::Log::Write + * \threadsafe + */ + static void Info( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined + by the format specifiers in + \c message */ + ); + + /** + * Logs a message to the given channel with EMBB_LOG_LEVEL_WARNING. + * \see embb::base::Log::Write + * \threadsafe + */ + static void Warning( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined + by the format specifiers in + \c message */ + ); + + /** + * Logs a message to the given channel with EMBB_LOG_LEVEL_ERROR. + * \see embb::base::Log::Write + * \threadsafe + */ + static void Error( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined + by the format specifiers in + \c message */ + ); +}; + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_LOG_H_ diff --git b/base_cpp/include/embb/base/memory_allocation.h a/base_cpp/include/embb/base/memory_allocation.h new file mode 100644 index 0000000..7b415a3 --- /dev/null +++ a/base_cpp/include/embb/base/memory_allocation.h @@ -0,0 +1,938 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_MEMORY_ALLOCATION_H_ +#define EMBB_BASE_MEMORY_ALLOCATION_H_ + +#include +#include +#include +#include +#include + +#include +#include + +/** + * \defgroup CPP_BASE_MEMORY_ALLOCATION Memory Allocation + * Functions, classes, and allocators for dynamic memory allocation. + * + * \ingroup CPP_BASE + */ + +namespace embb { +namespace base { +/** + * Common (static) functionality for unaligned and aligned memory allocation. + * This class is a wrapper for the functions in + * \ref embb/base/c/memory_allocation.h + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +class Allocation { + public: + /** + * Allocates memory for an instance of type \c Type and default-initializes + * it. + * + * Keeps track of allocated memory in debug mode. + * + * \return Pointer to new instance of type \c Type + * + * \throws embb::base::NoMemoryException if not enough memory is available + * for the given type. + * + * \see Delete() + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \tparam Type %Type of the object to be allocated + */ + template + static Type* New() { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(); + } + + #ifdef DOXYGEN + + /** + * Allocates memory unaligned for an instance of type \c Type and initializes + * it with the specified arguments. + * + * Keeps track of allocated memory in debug mode. + * + * \return Pointer to new instance of type \c Type + * + * \throws embb::base::NoMemoryException if not enough memory is available + * for the given type. + * + * \see Delete() + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \tparam Type %Type of the instance to be allocated + * \tparam Arg1 %Type of (first) constructor argument + */ + template + static Type* New( + Arg1 argument1, + /**< [IN] (First) argument for constructor of \c Type */ + ... + ); + +#else // DOXYGEN + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2, arg3); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2, arg3, arg4); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2, arg3, arg4, arg5); + } + +#endif // else DOXYGEN + + /** + * Destructs an instance of type \c Type and frees the allocated memory. + * + * \tparam Type %Type of instance to be deleted + */ + template + static void Delete( + Type* to_delete + /**< [IN,OUT] Instance to be deleted */ + ) { + to_delete->~Type(); + embb_free(static_cast(to_delete)); + } + + /** + * Returns the total number of bytes currently allocated. + * + * Wrapper for C function embb_get_bytes_allocated(). + * + * \return Number of currently allocated bytes in debug mode, otherwise 0. + * + * \waitfree + */ + static size_t AllocatedBytes(); + + /** + * Allocates \p size bytes of memory (unaligned). + * + * Wrapper for C function embb_allocate(). + * + * Keeps track of allocated memory in debug mode. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * Allocation::Free(). + * + * \see AllocateAligned(), AllocateCacheAligned(), Free() + */ + static void* Allocate( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by Allocation::Allocate() for + * some pointer \p ptr. + * + * Wrapper for C function embb_free(). + * + * Keeps track of freed memory in debug mode. + * + * \threadsafe + * + * \see Allocate() + */ + static void Free( + void * ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Allocates \p size bytes of memory with alignment \p alignment. + * + * Wrapper for C function embb_alloc_aligned(). + * + * This function can be used to align objects to certain boundaries such as + * cache lines, memory pages, etc. + * + * Keeps track of allocated memory in debug mode. + * + * It is not required that \p size is a multiple of \p alignment as, e.g., + * for the \c aligned\_alloc function of the C11 Standard. + * + * \pre The alignment has to be power of 2 and a multiple of + * size(void*). + * \post The returned pointer is a multiple of \p alignment. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory Debug mode: Let \c n be the number of aligned cells necessary to + * fit the payload. Then, (n+1)*alignment+3*size_of(size_t)-1 + * bytes are allocated.
Release mode: \c size bytes are requested + * using the functions provided by the operating systems. + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * Allocation::FreeAligned(). + * + * \see Allocate(), AllocateCacheAligned(), FreeAligned() + */ + static void* AllocateAligned( + size_t alignment, + /**< [IN] Alignment in bytes */ + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by Allocation::AllocateAligned() or + * Allocation::AllocateCacheAligned() for some pointer \p ptr. + * + * Wrapper for C function embb_free_aligned(). + * + * Keeps track of freed memory in debug mode. + * + * \threadsafe + * + * \see AllocateAligned(), AllocateCacheAligned() + */ + static void FreeAligned( + void * ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Allocates \p size bytes of cache-aligned memory. + * + * Wrapper for C function embb_alloc_cache_aligned(). + * + * Specialized version of Allocation::AllocateAligned(). The alignment is + * chosen automatically (usually 64 bytes). + * + * Keeps track of allocated memory in debug mode. + * + * \post The returned pointer is a multiple of the cache line size. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory See Allocation::AllocateAligned() + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * Allocation::FreeAligned(). + * + * \see Allocate(), AllocateAligned(), FreeAligned() + */ + static void* AllocateCacheAligned( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); +}; + +/** + * Overloaded new/delete operators. + * + * Classes that derive from this class will use the EMBB methods for dynamic + * allocation and deallocation of memory (Allocation::Allocate() and + * Allocation::Free()). In debug mode, memory consumption is tracked in order to + * detect memory leaks. + * + * \see CacheAlignedAllocatable + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +class Allocatable { + public: + /** + * New operator. + * + * Allocates \c size bytes of memory. Must not be called directly! + * + * \return Pointer to allocated block of memory + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \threadsafe + * + * \memory See Allocation::Allocate() + * + * \see operator delete() + */ + static void* operator new( + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Delete operator. + * + * Deletes \c size bytes of memory pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + * + * \see operator new() + */ + static void operator delete( + void* ptr, + /**< [IN,OUT] Pointer to memory block to be freed */ + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Array new operator. + * + * Allocates an array of \c size bytes. Must not be called directly! + * + * \remark Note that the global new[], calling this function, might + * return a different address. This is stated in the standard + * (5.3.4 New [expr.new]): + * \remark "A new-expression passes the amount of space requested to the + * allocation function as the first argument of type std::size_t. + * That argument shall be no less than the size of the object being + * created; it may be greater than the size of the object being + * created only if the object is an array." + * \remark So, even if the returned pointer of this function is aligned, + * the pointer to the array returned by global new[] need not be. + * For example, when using GCC 4.8.3 (64 bit), the size of the array + * is kept in the first 8 bytes of the allocated memory. + * + * \return Pointer to allocated block of memory + * + * \threadsafe + * + * \memory See Allocation::Allocate() + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \see operator delete[]() + */ + static void* operator new[]( + size_t size + /**< [IN] Size of the array in bytes*/ + ); + + /** + * Array delete operator. + * + * Deletes array of \c size bytes pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + * + * \see operator new[]() + */ + static void operator delete[]( + void* ptr, + /**< [IN,OUT] Pointer to the array to be freed */ + size_t size + /**< [IN] Size of the array in bytes */ + ); +}; + +/** + * Overloaded new/delete operators. + * + * Classes that derive from this class will use the EMBB methods for dynamic, + * cache-aligned allocation and deallocation of memory + * (Allocation::AllocateCacheAligned() and Allocation::FreeAligned()). + * In debug mode, memory consumption is tracked in order to detect memory leaks. + * + * \note When using the new[] operator, not each object in the array is aligned, + * but only the constructed array as a whole. + * + * \see Allocatable + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +class CacheAlignedAllocatable { + public: + /** + * New operator. + * + * Allocates \c size bytes of memory. Must not be called directly! + * + * \return Pointer to allocated block of memory + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \threadsafe + * + * \memory See Allocation::AllocateCacheAligned() + * + * \see operator delete() + */ + static void* operator new( + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Delete operator. + * + * Deletes \c size bytes of memory pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + */ + static void operator delete( + void* ptr, + /**< [IN,OUT] Pointer to memory block to be freed */ + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Array new operator. + * + * Allocates an array of \c size bytes. Must not be called directly! + * + * \return Pointer to allocated block of memory + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory See Allocation::AllocateCacheAligned() + * + * \threadsafe + * + * \see operator delete[]() + */ + static void* operator new[]( + size_t size + /**< [IN] size of bytes to allocate for the array*/ + ); + + /** + * Array delete operator. + * + * Deletes array of \c size bytes pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + * + * \see operator new[]() + */ + static void operator delete[]( + void* ptr, + /**< [IN,OUT] Pointer to the array to be freed */ + size_t size + /**< [IN] Size of the array in bytes */ + ); +}; + +/* + * Forward declaration + */ +template +class Allocator; + +/* + * Specialization for void + */ +template <> +class Allocator < void > { + public: + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef Allocator other; + }; +}; + +/** + * %Allocator according to the C++ standard. + * + * For memory allocation and deallocation, + * embb::base::Allocation::Allocate() and + * embb::base::Allocation::Free() are used, respectively. + * + * In debug mode, leak checking is active. The function + * embb::base::Allocation::AllocatedBytes() returns the number of + * currently allocated bytes. + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +template +class Allocator { + public: + /** Quantity of elements type */ + typedef size_t size_type; + + /** Difference between two pointers type */ + typedef ptrdiff_t difference_type; + + /** Pointer to element type */ + typedef Type* pointer; + + /** Pointer to constant element type */ + typedef const Type* const_pointer; + + /** Reference to element type */ + typedef Type& reference; + + /** Reference to constant element type */ + typedef const Type& const_reference; + + /** Element type */ + typedef Type value_type; + + /** + * Rebind allocator to type OtherType + */ + template struct rebind { + /** Type to rebind to */ + typedef Allocator other; + }; + + /** + * Constructs allocator object + */ + Allocator() throw() {} + + /** + * Copies allocator object + */ + Allocator( + const Allocator& + /**< [IN] Other allocator object */ + ) throw() {} + + /** + * Constructs allocator object + * + * Allows construction from allocators for different types (rebind) + */ + template Allocator( + const Allocator& + /**< [IN] Other allocator object*/ + ) + throw() {} + + /** + * Destructs allocator object + */ + ~Allocator() throw() {} + + /** + * Gets address of an object + * + * \return Address of object + * + * \waitfree + */ + pointer address( + reference x + /**< [IN] Reference to object */ + ) const { + return &x; + } + + /** + * Gets address of a constant object + * + * \return Address of object + * + * \waitfree + */ + const_pointer address( + const_reference x + /**< [IN] Reference to constant object */ + ) const { + return &x; + } + + /** + * Allocates but doesn't initialize storage for elements of type Type + * + * \threadsafe + * + * \return Pointer to allocated storage + * + * \memory See Allocation::Allocate() + */ + pointer allocate( + size_type n, + /**< [IN] Number of elements to allocate */ + const void* = 0 + /**< [IN] Optional pointer previously obtained from allocate */ + ) { + if (n > max_size()) + EMBB_THROW(embb::base::NoMemoryException, + "Amount of requested memory too high"); + return reinterpret_cast + (embb::base::Allocation::Allocate(n*sizeof(value_type))); + } + + /** + * Deallocates storage of destroyed elements. + * + * \threadsafe + */ + void deallocate( + pointer p, + /**< [IN,OUT] Pointer to allocated storage */ + size_type + ) { + embb::base::Allocation::Free(p); + } + + /** + * %Allocation maximum + * + * \return Maximum number of elements that can be allocated + * + *\waitfree + */ + size_type max_size() const throw() { + return std::numeric_limits::max() / sizeof(value_type); + } + + /** + * Initializes elements of allocated storage with specified value. + * + * \threadsafe + */ + void construct( + pointer p, + /**< [IN,OUT] Pointer to allocated storage */ + const value_type& val + /**< [IN] Value */ + ) { + new(p)value_type(val); + } + + /** + * Destroys elements of initialized storage. + * + * \threadsafe + */ + void destroy( + pointer p + /**< [IN,OUT] Pointer to allocated storage*/ + ) { + EMBB_UNUSED(p); + p->~value_type(); + } + + private: + /* + * \threadsafe + */ + Allocator& operator=(const Allocator&); +}; + +/* + * Forward declaration + */ +template +class AllocatorCacheAligned; + +/* + * Specialization for void + */ +template <> +class AllocatorCacheAligned < void > { + public: + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef AllocatorCacheAligned other; + }; +}; + +/** + * %Allocator according to the C++ standard. Allocates memory cache-aligned. + * + * For memory allocation and deallocation, + * embb::base::Allocation::AllocateCacheAligned() and + * embb::base::Allocation::FreeAligned() are used, respectively. + * + * In debug mode, leak checking is active. The function + * embb::base::Allocation::AllocatedBytes() returns the number of + * currently allocated bytes. + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +template< typename Type > +class AllocatorCacheAligned : public Allocator < Type > { + public: + /** Quantity of elements type */ + typedef size_t size_type; + + /** Difference between two pointers type */ + typedef ptrdiff_t difference_type; + + /** Pointer to element type */ + typedef Type* pointer; + + /** Pointer to constant element type */ + typedef const Type* const_pointer; + + /** Reference to element type */ + typedef Type& reference; + + /** Reference to constant element type */ + typedef const Type& const_reference; + + /** Element type */ + typedef Type value_type; + + /** + * Rebind allocator to type OtherType + */ + template struct rebind { + /** Type to rebind to */ + typedef Allocator other; + }; + + /** + * Constructs allocator object + */ + AllocatorCacheAligned() throw() { } + + /** + * Copies allocator object + */ + AllocatorCacheAligned( + const AllocatorCacheAligned& a + /**< [IN] Other allocator object */ + ) throw() + : Allocator < Type >(a) { } + + /** + * Constructs allocator object + * + * Allows construction from allocators for different types (rebind) + */ + template + AllocatorCacheAligned( + const AllocatorCacheAligned& + /**< [IN] Other allocator object*/ + ) throw() { } + + /** + * Destructs allocator object + */ + ~AllocatorCacheAligned() throw() { } + + /** + * Allocates but doesn't initialize storage for elements of type Type + * + * \threadsafe + * + * \return Pointer to allocated storage + * + * \memory see Allocation::Allocate() + */ + pointer allocate( + size_type n, + /**< [IN] Number of elements to allocate */ + const void* = 0 + /**< [IN] Optional pointer previously obtained from allocate */ + ) { + if (n > this->max_size()) + EMBB_THROW(embb::base::NoMemoryException, + "Amount of requested memory too high"); + return reinterpret_cast + (embb::base::Allocation::AllocateCacheAligned(n*sizeof(value_type))); + } + + /** + * Deallocates storage of destroyed elements. + * + * \threadsafe + */ + void deallocate( + pointer p, + /**< [IN,OUT] Pointer to allocated storage */ + size_type + ) { + embb::base::Allocation::FreeAligned(p); + } + // Inherit everything else. +}; + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool operator==(const Allocator&, const Allocator&) { + return true; +} + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool operator==(const Allocator&, const Allocator&) { + return true; +} + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool operator!=(const Allocator&, const Allocator&) { + return false; +} + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool +operator!=(const Allocator&, const Allocator&) { + return false; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator==( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return true; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator==( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return true; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator!=( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return false; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator!=( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return false; +} +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_MEMORY_ALLOCATION_H_ diff --git b/base_cpp/include/embb/base/mutex.h a/base_cpp/include/embb/base/mutex.h new file mode 100644 index 0000000..968139e --- /dev/null +++ a/base_cpp/include/embb/base/mutex.h @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_MUTEX_H_ +#define EMBB_BASE_MUTEX_H_ + +#include +#include +#include + +namespace embb { +namespace base { +/** + * \defgroup CPP_CONCEPTS_MUTEX Mutex Concept + * + * \brief Concept for thread synchronization. + * + * \anchor CPP_CONCEPTS_MUTEX_ANCHOR + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * + * The mutex concept is used for thread synchronization and provides a lock. + * At any point in time, only one thread can exclusively hold the lock and + * the lock is held until the thread explicitly releases it. + * + * \par Requirements + * - Let \c Mutex be the mutex type + * - Let \c m be an object of type \c Mutex. + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
Mutex()\c voidConstructs a mutex.
m.TryLock()\c boolTries to lock the mutex and immediately returns. Returns \c false, if + * the mutex could not be acquired (locked), otherwise \c true. + *
m.Lock()\c voidLocks the mutex. When the mutex is already locked, the current thread + * is blocked until the mutex is unlocked.
m.Unlock()\c voidUnlocks the mutex.
+ * \} + */ + +/** + * \defgroup CPP_BASE_MUTEX Mutex and Lock + * + * Mutexes and locks for thread synchronization. + * + * \ingroup CPP_BASE + */ + +/** + * Forward declaration for friending. + */ +class ConditionVariable; + +namespace internal { +/** + * Provides main functionality for mutexes. + */ +class MutexBase { + public: + /** + * Creates internal representation. + * + * \notthreadsafe + */ + explicit MutexBase( + int mutex_type + /**< [IN] Mutex type as used in embb_mutex_init(). */ + ); + + /** + * Destroys internal representation. + */ + virtual ~MutexBase() = 0; + + /** + * Waits until the mutex can be locked and locks it. + * + * \pre The mutex is not locked by the current thread or is of type + * RecursiveMutex. + * \post The mutex is locked + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \post If successful, the mutex is locked + * \return \c true if mutex could be locked, otherwise \c false + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks a locked mutex. + * + * \pre The mutex is locked by the current thread + * \post The mutex is unlocked if the number of unlock operations has reached + * the number of lock operations + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + + private: + /** + * Holds the actual mutex. + */ + internal::MutexType mutex_; + + /** + * For access to native implementation type. + */ + friend class embb::base::ConditionVariable; +}; +} // namespace internal + +/** + * Spinlock + * + * \concept{CPP_CONCEPTS_MUTEX} + * + * \ingroup CPP_BASE_MUTEX + */ +class Spinlock { + public: + /** + * Creates a spinlock which is in unlocked state. + * + * \notthreadsafe + */ + Spinlock(); + + /** + * Destructs a spinlock. + * + * \notthreadsafe + */ + ~Spinlock(); + + /** + * Waits until the spinlock can be locked and locks it. + * + * \note This method yields the current thread in regular, + * implementation-defined intervals. + * + * \pre The spinlock is not locked by the current thread. + * \post The spinlock is locked. + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the spinlock for \c number_spins times and returns. + * + * \pre The spinlock is not locked by the current thread. + * \post If successful, the spinlock is locked. + * \return \c true if the spinlock could be locked, otherwise \c false. + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock( + unsigned int number_spins = 1 + /**< [IN] Maximal number of spins when trying to acquire the lock. + * Note that passing 0 here results in not trying to obtain the lock at all. + * The default parameter is 1. + */ + ); + + /** + * Unlocks the spinlock. + * + * \pre The spinlock is locked by the current thread. + * \post The spinlock is unlocked. + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + + private: + /** + * Disables copy construction and assignment. + */ + Spinlock(const Spinlock&); + Spinlock& operator=(const Spinlock&); + + /** + * Internal spinlock from base_c + */ + embb_spinlock_t spinlock_; +}; + +/** + * Non-recursive, exclusive mutex. + * + * Mutexes of this type cannot be locked recursively, that is, multiple times + * by the same thread with unlocking it in between. Moreover, it cannot be + * copied or assigned. + * + * \see RecursiveMutex + * \ingroup CPP_BASE_MUTEX + * + * \concept{CPP_CONCEPTS_MUTEX} + */ +class Mutex : public internal::MutexBase { + public: + /** + * Creates a mutex which is in unlocked state. + * + * \memory Potentially allocates dynamic memory + * \notthreadsafe + */ + Mutex(); + +#ifdef DOXYGEN + + /** + * Waits until the mutex can be locked and locks it. + * + * \pre The mutex is not locked by the current thread. + * \post The mutex is locked + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \pre The mutex is not locked by the current thread. + * \post If successful, the mutex is locked. + * \return \c true if the mutex could be locked, otherwise \c false. + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks the mutex. + * + * \pre The mutex is locked by the current thread + * \post The mutex is unlocked + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + +#endif // DOXYGEN + + private: + /** + * Disables copy construction and assignment. + */ + Mutex(const Mutex&); + Mutex& operator=(const Mutex&); + + /** + * For access to native implementation type. + */ + friend class ConditionVariable; +}; + +/** + * Recursive, exclusive mutex. + * + * Mutexes of this type can be locked recursively, that is, multiple times by + * the same thread without unlocking it in between. It is unlocked only, if the + * number of unlock operations has reached the number of previous lock + * operations by the same thread. It cannot be copied or assigned. + * + * \see Mutex + * \ingroup CPP_BASE_MUTEX + * + * \concept{CPP_CONCEPTS_MUTEX} + */ +class RecursiveMutex : public internal::MutexBase { + public: + /** + * Creates a mutex which is in unlocked state. + * + * \memory Potentially allocates dynamic memory + * \notthreadsafe + */ + RecursiveMutex(); + +#ifdef DOXYGEN + + /** + * Waits until the mutex can be locked and locks it. + * + * \post The mutex is locked + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \post If successful, the given mutex is locked. + * \return \c true if the mutex could be locked, otherwise \c false. + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks a locked mutex. + * + * \pre The mutex is locked by the current thread. + * \post The mutex is unlocked if the number of unlock operations has reached + * the number of lock operations. + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + +#endif // DOXYGEN + + private: + /** + * Disables copy construction and assignment. + */ + RecursiveMutex(const RecursiveMutex&); + RecursiveMutex& operator=(const RecursiveMutex&); +}; + +/** + * Scoped lock (according to the RAII principle) using a mutex. + * + * The mutex is locked on construction and unlocked on leaving the scope of the + * lock. + * + * \tparam Mutex Used mutex type. Has to fulfil the + * \ref CPP_CONCEPTS_MUTEX_ANCHOR "Mutex Concept". + * + * \see UniqueLock + * \ingroup CPP_BASE_MUTEX + */ +template +class LockGuard { + public: + /** + * Creates the lock and locks the mutex. + * + * \pre The given mutex is unlocked + * \notthreadsafe + */ + explicit LockGuard( + Mutex& mutex + /**< [IN] Mutex to be guarded */ + ) : mutex_(mutex) { + mutex_.Lock(); + } + + /** + * Unlocks the mutex. + */ + ~LockGuard() { + mutex_.Unlock(); + } + + private: + /** + * Holds reference to mutex realizing the lock. + */ + Mutex& mutex_; + + /** + * Disable copy construction and assignment. + */ + LockGuard(const LockGuard&); + LockGuard& operator=(const LockGuard&); +}; + +/** + * \name UniqueLock Tag Variables + * \{ + */ + +/** + * Tag type for deferred UniqueLock construction. + * + * Use the defer_lock variable in constructor calls. + */ +struct DeferLockTag {}; + +/** + * Tag variable for deferred UniqueLock construction. + * + * \ingroup CPP_BASE_MUTEX + */ +const DeferLockTag defer_lock = DeferLockTag(); + +/** + * Tag type for try-lock UniqueLock construction. + * + * Use the try_lock variable in constructor calls. + */ +struct TryLockTag {}; + +/** + * Tag variable for try-lock UniqueLock construction. + * + * \ingroup CPP_BASE_MUTEX + */ +const TryLockTag try_lock = TryLockTag(); + +/** + * Tag type for adopt UniqueLock constructor. + * + * Use the adopt_lock variable in constructor calls. + */ +struct AdoptLockTag {}; + +/** + * Tag variable for adopt UniqueLock construction. + * + * \ingroup CPP_BASE_MUTEX + */ +const AdoptLockTag adopt_lock = AdoptLockTag(); + +/** \} */ + +/** + * Flexible ownership wrapper for a mutex. + * + * Provides exception controlled locking of a mutex with non-recursive semantics, + * that gives more flexibility than LockGuard but also has slightly increased + * memory and processing overhead. Each instance of a UniqueLock can be used by + * one thread only! + * + * \notthreadsafe + * \see Mutex, LockGuard + * \tparam Mutex Used mutex type. Has to fulfil the + * \ref CPP_CONCEPTS_MUTEX_ANCHOR "Mutex Concept". + * \ingroup CPP_BASE_MUTEX + */ +template +class UniqueLock { + public: + /** + * Creates a lock without assigned mutex. + * + * A mutex can be assigned to the lock using the method Swap(). + */ + UniqueLock(); + + /** + * Creates a lock from an unlocked mutex and locks it. + * + * \pre \c mutex is unlocked + */ + explicit UniqueLock( + Mutex& mutex + /**< [IN] Mutex to be managed. */ + ); + + /** + * Creates a lock from an unlocked mutex without locking it. + * + * \pre \c mutex is unlocked + */ + UniqueLock( + Mutex& mutex, + /**< [IN] Mutex to be managed */ + DeferLockTag + /**< [IN] Tag to select correct constructor */ + ); + + /** + * Creates a lock from an unlocked mutex and tries to lock it. + * + * \pre \c mutex is unlocked + */ + UniqueLock( + Mutex& mutex, + /**< [IN] Mutex to be managed */ + TryLockTag + /**< [IN] Tag to select correct constructor */ + ); + + /** + * Creates a lock from an already locked mutex. + * + * \pre \c mutex is locked + */ + UniqueLock( + Mutex& mutex, + /**< [IN] Mutex to be managed */ + AdoptLockTag + /**< [IN] Tag to select correct constructor */ + ); + + /** + * Unlocks the mutex if owned. + */ + ~UniqueLock(); + + /** + * Waits until the mutex is unlocked and locks it. + * + * \throws ErrorException, if no mutex is set or it is locked + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \return \c true if the mutex could be locked, otherwise \c false. + * \throws ErrorException, if no mutex is set + */ + bool TryLock(); + + /** + * Unlocks the mutex. + * + * \throws ErrorException, if no mutex is set or it is not locked + */ + void Unlock(); + + /** + * Exchanges ownership of the wrapped mutex with another lock. + */ + void Swap( + UniqueLock& other + /**< [IN/OUT] The lock to exchange ownership with */ + ); + + /** + * Gives up ownership of the mutex and returns a pointer to it. + * + * \return A pointer to the owned mutex or NULL, if no mutex was owned + */ + Mutex* Release(); + + /** + * Checks whether the mutex is owned and locked. + * + * \return \c true if mutex is locked, otherwise \c false. + */ + bool OwnsLock() const; + + private: + /** + * Holds reference to mutex realizing the lock. + */ + Mutex* mutex_; + + /** + * Stores information about whether the unique lock has locked the mutex. + */ + bool locked_; + + /** + * Disable copy construction and assignment. + */ + UniqueLock(const UniqueLock&); + UniqueLock& operator=(const UniqueLock&); + + /** + * For access to native implementation type. + */ + friend class embb::base::ConditionVariable; +}; +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_MUTEX_H_ diff --git b/base_cpp/include/embb/base/thread.h a/base_cpp/include/embb/base/thread.h new file mode 100644 index 0000000..10fe983 --- /dev/null +++ a/base_cpp/include/embb/base/thread.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_THREAD_H_ +#define EMBB_BASE_THREAD_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_THREAD Thread + * + * Threads supporting thread-to-core affinities. + * + * \ingroup CPP_BASE + */ + +/** + * Represents a thread of execution. + * + * Provides an abstraction from platform-specific threading implementations to + * create, manage, and join threads of execution. Support for thread-to-core + * affinities is given on thread creation by using the core set functionality. + * + * This class is essentially a wrapper for the underlying C implementation. + * + * \ingroup CPP_BASE_THREAD + */ +class Thread { + public: + /** + * Unique %ID of a thread that can be compared with other IDs. + */ + class ID { + public: + /** + * Constructs an empty (invalid) thread %ID. + */ + ID() : id_() {} + + private: + /** + * Constructs an %ID from an IDType instance. Is done by the thread. + */ + explicit ID(internal::IDType id) : id_(id) {} + + /** + * Holds the actual %ID representation. + */ + internal::IDType id_; + + /** + * A thread needs to set its ID on start. + */ + friend class Thread; + + /** + * The streaming operator needs to access the internal %ID representation. + */ + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, Thread::ID id); + + /** + * Comparison operators need to access the internal ID representation. + */ + friend bool operator==(Thread::ID lhs, Thread::ID rhs); + friend bool operator!=(Thread::ID lhs, Thread::ID rhs); + }; + + /** + * Returns the maximum number of threads handled by EMB2. + * + * See embb_thread_get_max_count() for a description of the semantics. + * + * \return Maximum number of threads + * + * \lockfree + * \see SetThreadsMaxCount() + */ + static unsigned int GetThreadsMaxCount(); + + /** + * Sets the maximum number of threads handled by EMB2. + * + * \notthreadsafe + * \see GetThreadsMaxCount() + */ + static void SetThreadsMaxCount( + unsigned int max_count + /**< [IN] Maximum number of threads */ + ); + + /** + * Returns the %ID of the current thread. + * + * The %ID is only valid within the calling thread. + * + * \return %ID of the calling thread + * + * \threadsafe + */ + static ID CurrentGetID(); + + /** + * Reschedule the current thread for later execution. + * + * This is only a request, the realization depends on the implementation and + * the scheduler employed by the operating system. + * + * \threadsafe + */ + static void CurrentYield(); + + /** + * Creates and runs a thread with zero-argument start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \memory A small constant amount of memory to store the function. This + * memory is freed the thread is joined. + * \notthreadsafe + * \tparam Function Function object type + */ + template + explicit Thread( + Function function + /**< [IN] Copyable function object, callable without arguments */ + ); + + /** + * Creates and runs a thread with zero-argument start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \memory A small constant amount of memory to store the function. This + * memory is freed the thread is joined. + * \notthreadsafe + * \tparam Function Function object type + */ + template + explicit Thread( + CoreSet& core_set, + /**< [IN] Set of cores on which the thread shall be executed. */ + Function function + /**< [IN] Copyable function object, callable without arguments */ + ); + + /** + * Creates and runs a thread with one-argument thread start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \memory A small constant amount of memory to store the function. This + * memory is freed the thread is joined. + * \notthreadsafe + * \tparam Function Function object type + * \tparam Argument Type of argument + */ + template + Thread( + Function function, + /**< [IN] Copyable function object, callable with one argument */ + Arg arg + /**< [IN] Argument for function (must be copyable) */ + ); + + /** + * Creates and runs a thread with two-argument thread start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \memory A small constant amount of memory to store the function. This + * memory is freed the thread is joined. + * \notthreadsafe + * \tparam Function Function object type + * \tparam Arg1 Type of first argument + * \tparam Arg2 Type of second argument + */ + template + Thread( + Function function, + /**< [IN] Copyable function object, callable with two arguments */ + Arg1 arg1, + /**< [IN] First argument for function (must be copyable) */ + Arg2 arg2 + /**< [IN] Second argument for function (must be copyable) */ + ); + + /** + * Waits until the thread has finished execution. + * + * \pre The thread has been created but not yet been joined. + * \post The thread has finished execution and dynamic memory allocated during + * creation has been freed. + * \notthreadsafe + */ + void Join(); + + /** + * Returns the thread %ID. + * + * \return %ID of the thread + * + * \threadsafe + */ + ID GetID(); + + private: + /** + * Performs error checks and frees resources from thread constructor. + * + * \tparam ThreadClosure Type of thread closure + */ + template + void CheckThreadCreationErrors( + int result, + /**< [IN] Result value of creating thread with C API */ + ThreadClosure* closure + /**< [IN] Closure used when creating thread */ + ); + + /** + * Disables copying and assignment. + */ + Thread(const Thread&); + Thread& operator=(const Thread&); + + /** + * Holds native implementation thread handle. + */ + internal::ThreadType rep_; +}; + +/** + * Compares two thread IDs for equality. + * + * \return \c true if thread IDs are equivalent, otherwise \c false + * + * \ingroup CPP_BASE_THREAD + */ +bool operator==( + Thread::ID lhs, + /**< [IN] Left-hand side of equality sign */ + Thread::ID rhs + /**< [IN] Right-hand side of equality sign */ + ); + +/** + * Compares two thread IDs for inequality. + * + * \return \c true if thread IDs are not equivalent, otherwise \c false + * + * \ingroup CPP_BASE_THREAD + */ +bool operator!=( + Thread::ID lhs, + /**< [IN] Left-hand side of inequality sign */ + Thread::ID rhs + /**< [IN] Left-hand side of inequality sign */ + ); + +/** + * Writes thread %ID to stream. + * + * \return Reference to the stream + * + * \ingroup CPP_BASE_THREAD + */ +template +std::basic_ostream& + operator<<( + std::basic_ostream& os, + /**< [IN/OUT] Stream to which thread %ID is written */ + Thread::ID id + /**< [IN] %Thread %ID to be written */ + ); + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_THREAD_H_ diff --git b/base_cpp/include/embb/base/thread_specific_storage.h a/base_cpp/include/embb/base/thread_specific_storage.h new file mode 100644 index 0000000..1f07996 --- /dev/null +++ a/base_cpp/include/embb/base/thread_specific_storage.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_THREAD_SPECIFIC_STORAGE_H_ +#define EMBB_BASE_THREAD_SPECIFIC_STORAGE_H_ + +#include +#include +#include + +namespace embb { +namespace base { + +namespace test { +/** + * Forward declaration for friending. + */ +class ThreadSpecificStorageTest; +} + +/** + * \defgroup CPP_BASE_TSS Thread-Specific Storage + * + * %Thread specific storage. + * + * \ingroup CPP_BASE + */ + +/** + * Represents thread-specific storage (TSS). + * + * Provides for each thread a separate slot storing an object of the given type. + * + * \tparam Type Type of the objects + * \ingroup CPP_BASE_TSS + */ +template +class ThreadSpecificStorage { + public: + /** + * Creates the TSS and default initializes all slots. + * + * \throws NoMemoryException if not enough memory is available to allocate + * the TSS slots + * \memory Dynamically allocates embb::base::Thread::GetThreadsMaxCount() + * pointers and slots of the TSS type + * \notthreadsafe + */ + ThreadSpecificStorage(); + +#ifdef DOXYGEN + /** + * Creates the TSS and initializes all slots with up to four constructor + * arguments. + * + * The TSS objects are created by calling their constructor as follows: + * Type(initializer1, ...). + * + * \throws NoMemoryException if not enough memory is available to allocate + * the TSS slots + * \memory Dynamically allocates embb::base::Thread::GetThreadsMaxCount() + * pointers and slots of the TSS type + * \notthreadsafe + */ + template + explicit ThreadSpecificStorage( + Initializer1 initializer1, + /**< [IN] First argument for constructor\n ... */ + ... + ); +#else + /** + * See description of Doxygen variadic constructor above. + */ + template + explicit ThreadSpecificStorage(Initializer initializer); + + /** + * See description of Doxygen variadic constructor above. + */ + template + ThreadSpecificStorage(Initializer1 initializer1, Initializer2 initializer2); + + /** + * See description of Doxygen variadic constructor above. + */ + template + ThreadSpecificStorage(Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3); + + /** + * See description of Doxygen variadic constructor above. + */ + template + ThreadSpecificStorage(Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3, Initializer4 initializer4); +#endif // else defined(DOXYGEN) + + /** + * Destroys the TSS and frees allocated memory for the TSS slots. + */ + ~ThreadSpecificStorage(); + + /** + * Returns a reference to the TSS slot of the current thread. + * + * \return Reference to TSS slot + * + * \throws embb::base::ErrorException if the maximum number of threads has + * been exceeded + * \lockfree + * \see Get() const + */ + Type& Get(); + + /** + * Returns a const reference to the TSS slot of the current thread. + * + * \return Constant reference to TSS slot + * + * \throws embb::base::ErrorException if the maximum number of threads has + * been exceeded + * \lockfree + * \see Get() + */ + const Type& Get() const; + + private: + /** + * Common construction code. + */ + void Prepare(); + + /** + * Representation of TSS implemented in Base C. + */ + embb_tss_t rep_; + + /** + * Flags to indicate the usage of a TSS value. Are set on first access. + */ + bool* usage_flags_; + + /** + * To allow white-box tests. + */ + friend class test::ThreadSpecificStorageTest; +}; + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_THREAD_SPECIFIC_STORAGE_H_ diff --git b/base_cpp/include/embb/base/time.h a/base_cpp/include/embb/base/time.h new file mode 100644 index 0000000..f942588 --- /dev/null +++ a/base_cpp/include/embb/base/time.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_TIME_H_ +#define EMBB_BASE_TIME_H_ + +#include +#include + +namespace embb { +namespace base { + +/** + * Represents an absolute time point. + * + * \ingroup CPP_BASE_TIMEDURATION + */ +class Time { + public: + /** + * Constructs an instance representing the current point of time. + * + * \notthreadsafe + */ + Time(); + + /** + * Constructs an instance representing the current point of time plus + * \c duration. + * + * \notthreadsafe + * \see Duration + * \tparam Tick Type of tick of the Duration + */ + template + explicit Time( + const Duration& duration + /**< [IN] %Duration added to the current point of time. */ + ) : rep_() { + embb_time_in(&rep_, &(duration.rep_)); + } + + private: + /** + * Representation of the absolute time point. + */ + embb_time_t rep_; + + /** + * Needs to access the internal time representation. + */ + friend class ConditionVariable; +}; + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_TIME_H_ diff --git b/base_cpp/src/condition_variable.cc a/base_cpp/src/condition_variable.cc new file mode 100644 index 0000000..8124d2e --- /dev/null +++ a/base_cpp/src/condition_variable.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { + +ConditionVariable::ConditionVariable() : condition_var_() { + int status = embb_condition_init(&condition_var_); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable could not be initialized"); + } +} + +void ConditionVariable::NotifyOne() { + int status = embb_condition_notify_one(&condition_var_); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: NotifyOne failed"); + } +} + +void ConditionVariable::NotifyAll() { + int status = embb_condition_notify_all(&condition_var_); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: NotifyAll failed"); + } +} + +void ConditionVariable::Wait(UniqueLock& lock) { + int status = embb_condition_wait(&condition_var_, &(lock.mutex_->mutex_)); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: Wait failed"); + } +} + +bool ConditionVariable::WaitUntil(UniqueLock& lock, const Time& time) { + int status = embb_condition_wait_until( + &condition_var_, &(lock.mutex_->mutex_), &(time.rep_)); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: WaitUntil failed"); + } + if (status == EMBB_TIMEDOUT) return false; + return true; +} + +} // namespace base +} // namespace embb diff --git b/base_cpp/src/core_set.cc a/base_cpp/src/core_set.cc new file mode 100644 index 0000000..751a373 --- /dev/null +++ a/base_cpp/src/core_set.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +namespace embb { +namespace base { + +unsigned int CoreSet::CountAvailable() { + return embb_core_count_available(); +} + +CoreSet::CoreSet() : rep_() { + embb_core_set_init(&rep_, 0); +} + +CoreSet::CoreSet(bool value) : rep_() { + int value_converted = value == true ? 1 : 0; + embb_core_set_init(&rep_, value_converted); +} + +CoreSet::CoreSet(const CoreSet& to_copy) : rep_() { + embb_core_set_init(&rep_, 0); + embb_core_set_union(&rep_, &(to_copy.rep_)); +} + +CoreSet& CoreSet::operator=(const CoreSet& to_assign) { + embb_core_set_init(&rep_, 0); + embb_core_set_union(&rep_, &(to_assign.rep_)); + return *this; +} + +void CoreSet::Reset(bool value) { + int value_converted = value ? 1 : 0; + embb_core_set_init(&rep_, value_converted); +} + +void CoreSet::Add(unsigned int core) { + embb_core_set_add(&rep_, core); +} + +void CoreSet::Remove(unsigned int core) { + embb_core_set_remove(&rep_, core); +} + +bool CoreSet::IsContained(unsigned int core) const { + return embb_core_set_contains(&rep_, core) != 0; +} + +unsigned int CoreSet::Count() const { + return embb_core_set_count(&rep_); +} + +CoreSet CoreSet::operator&(const CoreSet& rhs) const { + CoreSet result(*this); + embb_core_set_intersection(&(result.rep_), &(rhs.rep_)); + return result; +} + +CoreSet CoreSet::operator|(const CoreSet& rhs) const { + CoreSet result(*this); + embb_core_set_union(&(result.rep_), &(rhs.rep_)); + return result; +} + +CoreSet& CoreSet::operator&=(const CoreSet& rhs) { + embb_core_set_intersection(&rep_, &(rhs.rep_)); + return *this; +} + +CoreSet& CoreSet::operator|=(const CoreSet& rhs) { + embb_core_set_union(&rep_, &(rhs.rep_)); + return *this; +} + +} // namespace base +} // namespace embb diff --git b/base_cpp/src/duration.cc a/base_cpp/src/duration.cc new file mode 100644 index 0000000..2870ef8 --- /dev/null +++ a/base_cpp/src/duration.cc @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +void internal::Tick::CheckExceptions(int status, const char* msg) { + switch (status) { + case EMBB_SUCCESS: return; + case EMBB_OVERFLOW: EMBB_THROW(OverflowException, msg); + case EMBB_UNDERFLOW: EMBB_THROW(UnderflowException, msg); + default: EMBB_THROW(ErrorException, msg); + } +} + +int internal::Seconds::Set(embb_duration_t& duration, + unsigned long long ticks) { + return embb_duration_set_seconds(&duration, ticks); +} + +void internal::Seconds::SetAndCheck(embb_duration_t& duration, + unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from seconds"); +} + +unsigned long long internal::Seconds::Get(const embb_duration_t& duration) { + unsigned long long ticks = 0; + int status = embb_duration_as_seconds(&duration, &ticks); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long internal::Seconds::Min() { + return 1; +} + +unsigned long long internal::Seconds::Max() { + return EMBB_DURATION_MAX_SECONDS; +} + +int internal::Milliseconds::Set(embb_duration_t& duration, + unsigned long long ticks) { + return embb_duration_set_milliseconds(&duration, ticks); +} + +void internal::Milliseconds::SetAndCheck( + embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from milliseconds"); +} + +unsigned long long internal::Milliseconds::Get( + const embb_duration_t& duration) { + unsigned long long ticks = 0; + int status = embb_duration_as_milliseconds(&duration, &ticks); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long internal::Milliseconds::Min() { +#if EMBB_DURATION_MIN_NANOSECONDS > 1000000 + assert(EMBB_DURATION_MIN_NANOSECONDS % 1000000 == 0); + return EMBB_DURATION_MIN_NANOSECONDS / 1000000; +#endif + return 1; +} + +unsigned long long internal::Milliseconds::Max() { +#if EMBB_DURATION_MAX_SECONDS < ULLONG_MAX / 1000 + return ULLONG_MAX; +#else + return EMBB_DURATION_MAX_SECONDS * 1000; +#endif +} + +int internal::Microseconds::Set(embb_duration_t& duration, + unsigned long long ticks) { + return embb_duration_set_microseconds(&duration, ticks); +} + +void internal::Microseconds::SetAndCheck( + embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from microseconds"); +} + +unsigned long long internal::Microseconds::Get( + const embb_duration_t& duration) { + unsigned long long ticks = 0; + + int status = embb_duration_as_microseconds(&duration, &ticks); + + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long internal::Microseconds::Min() { +#if EMBB_DURATION_MIN_NANOSECONDS > 1000 + assert(EMBB_DURATION_MIN_NANOSECONDS % 1000 == 0); + return EMBB_DURATION_MIN_NANOSECONDS / 1000; +#endif + return 1; +} + +unsigned long long internal::Microseconds::Max() { +#if EMBB_DURATION_MAX_SECONDS < ULLONG_MAX / 1000000 + return ULLONG_MAX; +#else + return EMBB_DURATION_MAX_SECONDS * 1000000; +#endif +} + +int internal::Nanoseconds::Set(embb_duration_t& duration, + unsigned long long ticks) { + return embb_duration_set_nanoseconds(&duration, ticks); +} + +void internal::Nanoseconds::SetAndCheck( + embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from microseconds"); +} + +unsigned long long internal::Nanoseconds::Get(const embb_duration_t& duration) { + unsigned long long ticks = 0; + int status = embb_duration_as_nanoseconds(&duration, &ticks); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long internal::Nanoseconds::Min() { + return EMBB_DURATION_MIN_NANOSECONDS; +} + +unsigned long long internal::Nanoseconds::Max() { +#if EMBB_DURATION_MAX_SECONDS < ULLONG_MAX / 1000000000 + return ULLONG_MAX; +#else + return EMBB_DURATION_MAX_SECONDS * 1000000000; +#endif +} + +} // namespace base +} // namespace embb diff --git b/base_cpp/src/function.cc a/base_cpp/src/function.cc new file mode 100644 index 0000000..3eea841 --- /dev/null +++ a/base_cpp/src/function.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace embb { +namespace base { + +Placeholder::Arg_1 Placeholder::_1; + +} // namespace base +} // namespace embb diff --git b/base_cpp/src/functionN.lua a/base_cpp/src/functionN.lua new file mode 100644 index 0000000..5c62f91 --- /dev/null +++ a/base_cpp/src/functionN.lua @@ -0,0 +1,527 @@ +-- Copyright (c) 2014-2016, Siemens AG. All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- 1. Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- 2. Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. + +max_args = 5 + +function make_functionN(arg_cnt) + local file = io.open("function"..arg_cnt..".h", "w") + + local include_guard = "EMBB_BASE_INTERNAL_FUNCTION"..arg_cnt.."_H_" + + local templ_param = "" + local param_list_a = "" + local param_list_c = "" + local param_list = "" + for i = 1, arg_cnt, 1 do + if i > 1 then + templ_param = templ_param..", " + param_list_a = param_list_a..", " + param_list_c = param_list_c..", " + param_list = param_list..", " + end + templ_param = templ_param.."typename T"..i + param_list_a = param_list_a.."T"..i + param_list_c = param_list_c.."T"..i.." p"..i + param_list = param_list.."p"..i + end + + local param_list_a_nil = param_list_a + for i = arg_cnt+1, max_args, 1 do + if i > 1 then + param_list_a_nil = param_list_a_nil..", " + end + param_list_a_nil = param_list_a_nil.."Nil" + end + + local CR_templ + local C_templ + local R_templ + local void_templ + local R_param_list_a + local void_param_list_a + if arg_cnt > 0 then + CR_templ = "template " + C_templ = "template " + R_templ = "template " + void_templ = "template <"..templ_param..">" + R_param_list_a = "R, "..param_list_a + void_param_list_a = "void, "..param_list_a + else + CR_templ = "template " + C_templ = "template " + R_templ = "template " + void_templ = "template <>" + R_param_list_a = "R" + void_param_list_a = "void" + end + + local FunctionN = "Function"..arg_cnt + local FunctionPointerN = "FunctionPointer"..arg_cnt + local MemberFunctionPointerN = "MemberFunctionPointer"..arg_cnt + local FunctorWrapperN = "FunctorWrapper"..arg_cnt + + + file:write("#ifndef "..include_guard.."\n") + file:write("#define "..include_guard.."\n") + file:write("\n") + + file:write("#include \n") + file:write("#include \n") + file:write("\n") + file:write("#include \n") + file:write("#include \n") + file:write("#include \n") + file:write("#include \n") + if arg_cnt > 0 then + file:write("#include \n") + end + if arg_cnt > 1 then + file:write("#include \n") + end + file:write("\n") + + file:write("namespace embb {\n") + file:write("namespace base {\n") + file:write("\n") + file:write("namespace internal {\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("class "..FunctionN.." {\n") + file:write(" public:\n") + file:write(" virtual ~"..FunctionN.."() {}\n") + file:write(" virtual R operator () ("..param_list_a..") = 0;\n") + file:write(" virtual void CopyTo(void* dst) = 0;\n") + file:write("};\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("class "..FunctionPointerN.."\n : public "..FunctionN.."<"..R_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef R(*FuncPtrType)("..param_list_a..");\n") + file:write(" explicit "..FunctionPointerN.."(FuncPtrType func) : function_(func) {}\n") + file:write(" virtual R operator () ("..param_list_c..") {\n") + file:write(" return function_("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctionPointerN.."(function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" FuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(void_templ.."\n") + file:write("class "..FunctionPointerN.."<"..void_param_list_a..">\n : public "..FunctionN.."<"..void_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef void(*FuncPtrType)("..param_list_a..");\n") + file:write(" explicit "..FunctionPointerN.."(FuncPtrType func) : function_(func) {}\n") + file:write(" virtual void operator () ("..param_list_c..") {\n") + file:write(" function_("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctionPointerN.."(function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" FuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("class "..MemberFunctionPointerN.."\n : public "..FunctionN.."<"..R_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef R(C::*MemFuncPtrType)("..param_list_a..");\n") + file:write(" typedef C & ClassRefType;\n") + file:write(" "..MemberFunctionPointerN.."(ClassRefType obj, MemFuncPtrType func)\n : object_(obj), function_(func) {}\n") + file:write(" explicit "..MemberFunctionPointerN.."(ClassRefType obj)\n : object_(obj), function_(&C::operator()) {}\n") + file:write(" void operator = ("..MemberFunctionPointerN.." const & memfunc) {\n") + file:write(" object_ = memfunc.object_;\n") + file:write(" function_ = memfunc.function_;\n") + file:write(" }\n") + file:write(" virtual R operator () ("..param_list_c..") {\n") + file:write(" return (object_.*function_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..MemberFunctionPointerN.."(object_, function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" ClassRefType object_;\n") + file:write(" MemFuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(C_templ.."\n") + file:write("class "..MemberFunctionPointerN.."\n : public "..FunctionN.."<"..void_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef void(C::*MemFuncPtrType)("..param_list_a..");\n") + file:write(" typedef C & ClassRefType;\n") + file:write(" "..MemberFunctionPointerN.."(ClassRefType obj, MemFuncPtrType func)\n : object_(obj), function_(func) {}\n") + file:write(" explicit "..MemberFunctionPointerN.."(ClassRefType obj)\n : object_(obj), function_(&C::operator()) {}\n") + file:write(" void operator = ("..MemberFunctionPointerN.." const & memfunc) {\n") + file:write(" object_ = memfunc.object_;\n") + file:write(" function_ = memfunc.function_;\n") + file:write(" }\n") + file:write(" virtual void operator () ("..param_list_c..") {\n") + file:write(" (object_.*function_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..MemberFunctionPointerN.."(object_, function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" ClassRefType object_;\n") + file:write(" MemFuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("class "..FunctorWrapperN.."\n : public "..FunctionN.."<"..R_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" "..FunctorWrapperN.."() : object_(NULL), ref_count_(NULL) {}\n") + file:write(" explicit "..FunctorWrapperN.."(C const & obj) {\n") + file:write(" object_ = Allocation::New(obj);\n") + file:write(" ref_count_ = Allocation::New >(1);\n") + file:write(" }\n") + file:write(" explicit "..FunctorWrapperN.."("..FunctorWrapperN.." const & other) {\n") + file:write(" object_ = other.object_;\n") + file:write(" ref_count_ = other.ref_count_;\n") + file:write(" ++*ref_count_;\n") + file:write(" }\n") + file:write(" virtual ~"..FunctorWrapperN.."() {\n") + file:write(" if (0 == --*ref_count_) {\n") + file:write(" Allocation::Delete(ref_count_);\n") + file:write(" Allocation::Delete(object_);\n") + file:write(" }\n") + file:write(" }\n") + file:write(" virtual R operator () ("..param_list_c..") {\n") + file:write(" return (*object_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctorWrapperN.."(*this);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" C * object_;\n") + file:write(" Atomic * ref_count_;\n") + file:write("};\n") + file:write("\n") + + file:write(C_templ.."\n") + file:write("class "..FunctorWrapperN.."\n : public "..FunctionN.."<"..void_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" "..FunctorWrapperN.."() : object_(NULL), ref_count_(NULL) {}\n") + file:write(" explicit "..FunctorWrapperN.."(C const & obj) {\n") + file:write(" object_ = Allocation::New(obj);\n") + file:write(" ref_count_ = Allocation::New >(1);\n") + file:write(" }\n") + file:write(" explicit "..FunctorWrapperN.."("..FunctorWrapperN.." const & other) {\n") + file:write(" object_ = other.object_;\n") + file:write(" ref_count_ = other.ref_count_;\n") + file:write(" ++*ref_count_;\n") + file:write(" }\n") + file:write(" virtual ~"..FunctorWrapperN.."() {\n") + file:write(" if (0 == --*ref_count_) {\n") + file:write(" Allocation::Delete(ref_count_);\n") + file:write(" Allocation::Delete(object_);\n") + file:write(" }\n") + file:write(" }\n") + file:write(" virtual void operator () ("..param_list_c..") {\n") + file:write(" (*object_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctorWrapperN.."(*this);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" C * object_;\n") + file:write(" Atomic * ref_count_;\n") + file:write("};\n") + file:write("\n") + + -- bind to function0 + if arg_cnt > 0 then + local param_list_ = "" + for i = 1, arg_cnt, 1 do + if i > 1 then + param_list_ = param_list_..", " + end + param_list_ = param_list_.."p"..i.."_" + end + file:write("// bind to function0\n") + file:write(R_templ.."\n") + file:write("class Bound"..arg_cnt.."Functor0 {\n") + file:write(" public:\n") + file:write(" Bound"..arg_cnt.."Functor0(Function<"..R_param_list_a.."> func,\n "..param_list_c..") : function_(func)\n ") + for i = 1, arg_cnt, 1 do + file:write(", p"..i.."_(p"..i..")") + end + file:write(" {}\n") + file:write(" Bound"..arg_cnt.."Functor0(Bound"..arg_cnt.."Functor0 const & func) : function_(func.function_)\n ") + for i = 1, arg_cnt, 1 do + file:write(", p"..i.."_(func.p"..i.."_)") + if i == 4 then + file:write("\n ") + end + end + file:write(" {}\n") + file:write(" R operator() () {\n") + file:write(" return function_("..param_list_..");\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" Function<"..R_param_list_a.."> function_;\n") + for i = 1, arg_cnt, 1 do + file:write(" T"..i.." p"..i.."_;\n") + end + file:write("};\n") + file:write("\n") + end + + -- bind to function1 + if arg_cnt > 1 then + file:write("// bind to function1\n") + for arg = 1, arg_cnt, 1 do + local param_list_ = "" + local param_list_c_ = "" + for i = 1, arg_cnt, 1 do + if i > 1 then + param_list_ = param_list_..", " + end + param_list_ = param_list_.."p"..i + if i ~= arg then + param_list_ = param_list_.."_" + param_list_c_ = param_list_c_..", T"..i.." p"..i + end + end + file:write(R_templ.."\n") + file:write("class Bound"..arg_cnt.."Functor1_Arg"..arg.." {\n") + file:write(" public:\n") + file:write(" Bound"..arg_cnt.."Functor1_Arg"..arg.."(Function<"..R_param_list_a.."> func\n "..param_list_c_..") : function_(func)\n ") + for i = 1, arg_cnt, 1 do + if i ~= arg then + file:write(", p"..i.."_(p"..i..")") + end + end + file:write(" {}\n") + file:write(" Bound"..arg_cnt.."Functor1_Arg"..arg.."(Bound"..arg_cnt.."Functor1_Arg"..arg.." const & func)\n : function_(func.function_)\n ") + for i = 1, arg_cnt, 1 do + if i ~= arg then + file:write(", p"..i.."_(func.p"..i.."_)") + end + end + file:write(" {}\n") + file:write(" R operator() (T"..arg.." p"..arg..") {\n") + file:write(" return function_("..param_list_..");\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" Function<"..R_param_list_a.."> function_;\n") + for i = 1, arg_cnt, 1 do + if i ~= arg then + file:write(" T"..i.." p"..i.."_;\n") + end + end + file:write("};\n") + file:write("\n") + end + end + + file:write("} // namespace internal\n") + file:write("\n") + + file:write("\n") + file:write("using embb::base::internal::Nil;\n") + file:write("\n") + + file:write(R_templ.."\n") + if arg_cnt < max_args then + file:write("class Function {\n") + else + file:write("class Function {\n") + end + file:write(" public:\n") + file:write(" typedef internal::"..FunctionN.."<"..R_param_list_a.."> * FuncPtrType;\n") + file:write(" Function() : function_(NULL) {}\n") + file:write(" template \n") + file:write(" explicit Function(C const & obj) {\n") + file:write(" function_ = new(storage_)\n internal::"..FunctorWrapperN.."(obj);\n") + file:write(" }\n") + file:write(" Function(Function const & func) {\n") + file:write(" func.function_->CopyTo(&storage_[0]);\n") + file:write(" function_ = reinterpret_cast(&storage_[0]);\n") + file:write(" }\n") + file:write(" ~Function() {\n") + file:write(" Free();\n") + file:write(" }\n") + file:write(" void operator = (R(*func)("..param_list_a..")) {\n") + file:write(" Free();\n") + file:write(" function_ = new(storage_)\n internal::"..FunctionPointerN.."<"..R_param_list_a..">(func);\n") + file:write(" }\n") + file:write(" void operator = (Function & func) {\n") + file:write(" Free();\n") + file:write(" func.function_->CopyTo(&storage_[0]);\n") + file:write(" function_ = reinterpret_cast(&storage_[0]);\n") + file:write(" }\n") + file:write(" template \n") + file:write(" void operator = (C const & obj) {\n") + file:write(" Free();\n") + file:write(" function_ = new(storage_)\n internal::"..FunctorWrapperN.."(obj);\n") + file:write(" }\n") + file:write(" explicit Function(R(*func)("..param_list_a..")) {\n") + file:write(" function_ = new(storage_)\n internal::"..FunctionPointerN.."<"..R_param_list_a..">(func);\n") + file:write(" }\n") + file:write(" template \n") + file:write(" Function(C & obj, R(C::*func)("..param_list_a..")) {\n") + file:write(" function_ = new(storage_)\n internal::"..MemberFunctionPointerN.."(obj, func);\n") + file:write(" }\n") + file:write(" R operator () ("..param_list_c..") {\n") + file:write(" return (*function_)("..param_list..");\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" char storage_[sizeof(\n internal::"..MemberFunctionPointerN..")];\n") + file:write(" FuncPtrType function_;\n") + file:write(" void Free() {\n") + file:write(" if (NULL != function_) {\n") + file:write(" function_->~"..FunctionN.."();\n") + file:write(" function_ = NULL;\n") + file:write(" }\n") + file:write(" }\n") + file:write("};\n") + file:write("\n") + + -- wrap member function + file:write("// wrap member function\n") + file:write(CR_templ.."\n") + file:write("Function<"..R_param_list_a.."> MakeFunction(C & obj,\n R(C::*func)("..param_list_a..")) {\n") + file:write(" return Function<"..R_param_list_a..">(obj, func);\n") + file:write("}\n") + file:write("\n") + + -- wrap function pointer + file:write("// wrap function pointer\n") + file:write(R_templ.."\n") + file:write("Function<"..R_param_list_a.."> MakeFunction(\n R(*func)("..param_list_a..")) {\n") + file:write(" return Function<"..R_param_list_a..">(func);\n") + file:write("}\n") + file:write("\n") + + -- bind to function0 + if arg_cnt > 0 then + file:write("// bind to function0\n") + + file:write(R_templ.."\n") + file:write("Function Bind(Function<"..R_param_list_a.."> func,\n "..param_list_c..") {\n") + file:write(" return Function(\n internal::Bound"..arg_cnt.."Functor0<"..R_param_list_a..">(\n func, "..param_list.."));\n") + file:write("}\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("Function Bind(R(*func)("..param_list_a.."),\n "..param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("Function Bind(C & obj, R(C::*func)("..param_list_a.."),\n "..param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(obj, func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + end + + -- bind to function1 + if arg_cnt > 1 then + file:write("// bind to function1\n") + for arg = 1, arg_cnt, 1 do + local target_arg = "T"..arg + local bound_param_list_a = "" + local bound_param_list_c = "" + local bound_param_list = "" + for i = 1, arg_cnt, 1 do + if i == arg then + bound_param_list_c = bound_param_list_c..", Placeholder::Arg_1 p"..i + bound_param_list_a = bound_param_list_a..", Placeholder::Arg_1" + else + bound_param_list_c = bound_param_list_c..", T"..i.." p"..i + bound_param_list_a = bound_param_list_a..", T"..i.." p"..i + bound_param_list = bound_param_list..", p"..i + end + end + + file:write(R_templ.."\n") + file:write("Function Bind(Function<"..R_param_list_a.."> func\n "..bound_param_list_a..") {\n") + file:write(" return Function(\n internal::Bound"..arg_cnt.."Functor1_Arg"..arg.."<"..R_param_list_a..">(func\n "..bound_param_list.."));\n") + file:write("}\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("Function Bind(R(*func)("..param_list_a..")\n "..bound_param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("Function Bind(C & obj, R(C::*func)("..param_list_a..")\n "..bound_param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(obj, func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + end + end + + -- bind to functionN + file:write("// bind to "..FunctionN.."\n") + + local bound_param_list_c = "" + for i = 1, arg_cnt, 1 do + bound_param_list_c = bound_param_list_c..",\n Placeholder::Arg_"..i + end + + file:write(CR_templ.."\n") + file:write("Function<"..R_param_list_a.."> Bind(\n C & obj,\n R(C::*func)("..param_list_a..")"..bound_param_list_c..") {\n") + file:write(" return Function<"..R_param_list_a..">(obj, func);\n") + file:write("}\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("Function<"..R_param_list_a.."> Bind(\n R(*func)("..param_list_a..")"..bound_param_list_c..") {\n") + file:write(" return Function<"..R_param_list_a..">(func);\n") + file:write("}\n") + file:write("\n") + + file:write("} // namespace base\n") + file:write("} // namespace embb\n") + file:write("\n") + + file:write("#endif // "..include_guard.."\n") + + file:flush() + file:close() +end + +for i = 0, max_args, 1 do + make_functionN(i) +end diff --git b/base_cpp/src/log.cc a/base_cpp/src/log.cc new file mode 100644 index 0000000..c2f10f3 --- /dev/null +++ a/base_cpp/src/log.cc @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { + +Log::Log() { + // empty +} + +void Log::SetLogLevel( + embb_log_level_t log_level) { + embb_log_set_log_level(log_level); +} + +void Log::SetLogFunction( + void * context, + embb_log_function_t func) { + embb_log_set_log_function(context, func); +} + +void Log::Write( + char const * channel, + embb_log_level_t log_level, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, log_level, message, argp); + va_end(argp); +} + +void Log::Trace( + char const * channel, + char const * message, + ...) { +#if defined(EMBB_DEBUG) + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_TRACE, message, argp); + va_end(argp); +#else + EMBB_UNUSED(channel); + EMBB_UNUSED(message); +#endif +} + +void Log::Info( + char const * channel, + char const * message, + ...) { +#if defined(EMBB_DEBUG) + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_INFO, message, argp); + va_end(argp); +#else + EMBB_UNUSED(channel); + EMBB_UNUSED(message); +#endif +} + +void Log::Warning( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_WARNING, message, argp); + va_end(argp); +} + +void Log::Error( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_ERROR, message, argp); + va_end(argp); +} + +} // namespace base +} // namespace embb diff --git b/base_cpp/src/memory_allocation.cc a/base_cpp/src/memory_allocation.cc new file mode 100644 index 0000000..34cb28e --- /dev/null +++ a/base_cpp/src/memory_allocation.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +size_t embb::base::Allocation::AllocatedBytes() { + return embb_get_bytes_allocated(); +} + +void* embb::base::Allocation::Allocate(size_t size) { + void* alloc; + if ((alloc = embb_alloc(size)) == 0) { + EMBB_THROW(embb::base::NoMemoryException, "Could not allocate memory"); + } + return alloc; +} + +void embb::base::Allocation::Free(void * ptr) { + embb_free(ptr); +} + +void* embb::base::Allocation::AllocateAligned(size_t alignment, size_t size) { + void* alloc; + if ((alloc = embb_alloc_aligned(alignment, size)) == 0) { + EMBB_THROW(embb::base::NoMemoryException, "Could not allocate memory"); + } + return alloc; +} + +void embb::base::Allocation::FreeAligned(void * ptr) { + embb_free_aligned(ptr); +} + +void* embb::base::Allocation::AllocateCacheAligned(size_t size) { + void* alloc; + if ((alloc = embb_alloc_cache_aligned(size)) == 0) { + EMBB_THROW(embb::base::NoMemoryException, "Could not allocate memory"); + } + return alloc; +} diff --git b/base_cpp/src/mutex.cc a/base_cpp/src/mutex.cc new file mode 100644 index 0000000..4988e0f --- /dev/null +++ a/base_cpp/src/mutex.cc @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { +namespace internal { + +MutexBase::MutexBase(int mutex_type) : mutex_() { + int result = embb_mutex_init(&mutex_, mutex_type); + if (EMBB_SUCCESS != result) { + EMBB_THROW(ErrorException, "Could not initialize mutex."); + } +} + +MutexBase::~MutexBase() { + embb_mutex_destroy(&mutex_); +} + +void MutexBase::Lock() { + int result = embb_mutex_lock(&mutex_); + if (EMBB_SUCCESS != result) { + EMBB_THROW(ErrorException, "Could not lock mutex."); + } +} + +bool MutexBase::TryLock() { + int result = embb_mutex_try_lock(&mutex_); + return result == EMBB_SUCCESS; +} + +void MutexBase::Unlock() { + int result = embb_mutex_unlock(&mutex_); + if (EMBB_SUCCESS != result) { + EMBB_THROW(ErrorException, "Could not unlock mutex."); + } +} + +} // namespace internal + +Mutex::Mutex() : MutexBase(EMBB_MUTEX_PLAIN) { +} + +RecursiveMutex::RecursiveMutex() : MutexBase(EMBB_MUTEX_RECURSIVE) { +} + +Spinlock::Spinlock() { + embb_spin_init(&spinlock_); +} + +Spinlock::~Spinlock() { + embb_spin_destroy(&spinlock_); +} + +void Spinlock::Lock() { + int status = embb_spin_lock(&spinlock_); + + // Currently, embb_spin_lock will always return EMBB_SUCCESS. However, + // This might change. + if (status != EMBB_SUCCESS) { + EMBB_THROW(ErrorException, "Error while locking spinlock"); + } +} + +bool Spinlock::TryLock(unsigned int number_spins) { + int status = embb_spin_try_lock(&spinlock_, number_spins); + + if (status == EMBB_BUSY) { + return false; + } else if (status != EMBB_SUCCESS) { + EMBB_THROW(ErrorException, "Error while try-locking spinlock"); + } + + return true; +} + +void Spinlock::Unlock() { + int status = embb_spin_unlock(&spinlock_); + + if (status != EMBB_SUCCESS) { + EMBB_THROW(ErrorException, "Error while unlocking spinlock"); + } +} +} // namespace base +} // namespace embb diff --git b/base_cpp/src/thread.cc a/base_cpp/src/thread.cc new file mode 100644 index 0000000..3766dd9 --- /dev/null +++ a/base_cpp/src/thread.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { + +unsigned int Thread::GetThreadsMaxCount() { + return embb_thread_get_max_count(); +} + +void Thread::SetThreadsMaxCount(unsigned int max_count) { + embb_thread_set_max_count(max_count); +} + +Thread::ID Thread::CurrentGetID() { +#if defined EMBB_PLATFORM_THREADING_WINTHREADS + return Thread::ID(GetCurrentThreadId()); +#else + return Thread::ID(embb_thread_current().embb_internal_handle); +#endif +} + +void Thread::CurrentYield() { + embb_thread_yield(); +} + +void Thread::Join() { + if (embb_thread_join(&rep_, NULL) == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Could not join thread."); + } +} + +Thread::ID Thread::GetID() { +#if defined EMBB_PLATFORM_THREADING_WINTHREADS + return Thread::ID(GetCurrentThreadId()); +#elif defined EMBB_PLATFORM_THREADING_POSIXTHREADS + return Thread::ID(rep_.embb_internal_handle); +#endif +} + +bool operator==(Thread::ID lhs, Thread::ID rhs) { +#if defined EMBB_PLATFORM_THREADING_WINTHREADS + return lhs.id_ == rhs.id_; +#else + return pthread_equal(lhs.id_, rhs.id_) != 0; +#endif +} + +bool operator!=(Thread::ID lhs, Thread::ID rhs) { +#if defined EMBB_PLATFORM_THREADING_WINTHREADS + return lhs.id_ != rhs.id_; +#else + return pthread_equal(lhs.id_, rhs.id_) == 0; +#endif +} + +} // namespace base +} // namespace embb diff --git b/base_cpp/src/time.cc a/base_cpp/src/time.cc new file mode 100644 index 0000000..1f827c5 --- /dev/null +++ a/base_cpp/src/time.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { + +Time::Time() : rep_() { + embb_time_now(&rep_); +} + +} // namespace base +} // namespace embb + + diff --git b/base_cpp/test/atomic_test.cc a/base_cpp/test/atomic_test.cc new file mode 100644 index 0000000..a9ffe38 --- /dev/null +++ a/base_cpp/test/atomic_test.cc @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +size_t AtomicTest::numIterations_; + +void AtomicTest::TestStressLoadStore::barrier(int thread) { + const int my_sense(thread_sense[thread]); + if (--count == 0) { + const int tmp(z.Load()); + PT_EXPECT(tmp == 1 || tmp == 2); + x.Store(0); + y.Store(0); + z.Store(0); + count.Store(4); + sense.Store(my_sense); + } else { + while (sense.Load() != my_sense) {} + } + thread_sense[thread] = !my_sense; +} + +void AtomicTest::TestStressLoadStore::write_x() { + x.Store(1); + barrier(0); +} + +void AtomicTest::TestStressLoadStore::write_y() { + y.Store(1); + barrier(1); +} + +void AtomicTest::TestStressLoadStore::read_x_then_y() { + while (!x.Load()) {} + if (y.Load()) + z++; + barrier(2); +} + +void AtomicTest::TestStressLoadStore::read_y_then_x() { + while (!y.Load()) {} + if (x.Load()) + z++; + barrier(3); +} + +void AtomicTest::TestStressLoadStore::Init() { +} + +void AtomicTest::TestStressLoadStore::CheckAndDestroy() { +} + +AtomicTest::TestStressLoadStore::TestStressLoadStore( + size_t number_threads, size_t number_iterations) + : TestUnit("Load/Store Stress test for Atomics"), x(0), y(0), z(0), + count(4), sense(0) { + PT_ASSERT(number_threads == 1); + thread_sense[0] = 1; + thread_sense[1] = 1; + thread_sense[2] = 1; + thread_sense[3] = 1; + Pre(&TestStressLoadStore::Init, this); + Add(&TestStressLoadStore::write_x, this, + number_threads, number_iterations); + Add(&TestStressLoadStore::write_y, this, + number_threads, number_iterations); + Add(&TestStressLoadStore::read_x_then_y, this, + number_threads, number_iterations); + Add(&TestStressLoadStore::read_y_then_x, this, + number_threads, number_iterations); + Post(&TestStressLoadStore::CheckAndDestroy, this); +} + +AtomicTest::TestStressProduceConsume::TestStressProduceConsume( + size_t number_threads, size_t number_iterations) + : TestUnit("Produce/Consume Stress test for Atomics"), flag(0), + counter_producer(0), counter_consumer(0) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressProduceConsume::Init, this); + + Add(&TestStressProduceConsume::produce, this, + number_threads, number_iterations); + Add(&TestStressProduceConsume::consume, this, + number_threads, number_iterations); + + Post(&TestStressProduceConsume::CheckAndDestroy, this); +} + +void AtomicTest::TestStressProduceConsume::produce() { + counter_consumer++; + while (flag) {} + prod_cons_value = static_cast(counter_consumer); + while (flag.Swap(true)) {} +} + +void AtomicTest::TestStressProduceConsume::consume() { + counter_producer++; + while (flag == 0) {} + PT_EXPECT(prod_cons_value == static_cast(counter_producer)); + flag = 0; +} + +void AtomicTest::TestStressProduceConsume::Init() { +} + +void AtomicTest::TestStressProduceConsume::CheckAndDestroy() { +} + +AtomicTest::TestStressIncrementDecrement::TestStressIncrementDecrement( + size_t number_threads, size_t number_iterations) + : TestUnit("Increment/Decrement Stress test for Atomics"), inc_dec_value(0) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressIncrementDecrement::Init, this); + + Add(&TestStressIncrementDecrement::increment, this, + number_threads, number_iterations); + Add(&TestStressIncrementDecrement::decrement, this, + number_threads, number_iterations); + + Post(&TestStressIncrementDecrement::CheckAndDestroy, this); +} + +void AtomicTest::TestStressIncrementDecrement::decrement() { + inc_dec_value--; +} + +void AtomicTest::TestStressIncrementDecrement::increment() { + inc_dec_value++; +} + +void AtomicTest::TestStressIncrementDecrement::Init() { +} + +void AtomicTest::TestStressIncrementDecrement::CheckAndDestroy() { + // Increment and decrement operations must neutralize each other + PT_EXPECT(inc_dec_value == 0); +} + + + +AtomicTest::TestStressSwap::TestStressSwap( + size_t number_threads, size_t number_iterations) + : TestUnit("Swap Stress test for Atomics"), swap1_counter(1) + , swap2_counter(2) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressSwap::Init, this); + + Add(&TestStressSwap::swap1, this, + number_threads, number_iterations); + Add(&TestStressSwap::swap2, this, + number_threads, number_iterations); + + Post(&TestStressSwap::CheckAndDestroy, this); +} + +void AtomicTest::TestStressSwap::Init() { +} + +void AtomicTest::TestStressSwap::CheckAndDestroy() { + // Each element is read at most once + bitsets[2] = bitsets[0]; + bitsets[2] &= bitsets[1]; + PT_EXPECT(bitsets[2].none()); + bitsets[2] = bitsets[0]; + bitsets[2] |= bitsets[1]; + // All elements except one are read (push, pop, push and so on... the last + // push is not read), however this must not always be the same element. + PT_EXPECT(bitsets[2].count() == + static_cast(AtomicTest::GetNumberOfIterations() * 2)); +} + +void AtomicTest::TestStressSwap::swap1() { + const int j(swap_value.Swap(swap1_counter)); + PT_EXPECT(!(bitsets[0].test(static_cast(j)))); + bitsets[0].set(static_cast(j)); + swap1_counter += 2; +} + +void AtomicTest::TestStressSwap::swap2() { + const int j(swap_value.Swap(swap2_counter)); + PT_EXPECT(!(bitsets[1].test(static_cast(j)))); + bitsets[1].set(static_cast(j)); + swap2_counter += 2; +} + +AtomicTest::TestStressCompareAndSwap::TestStressCompareAndSwap( + size_t number_threads, size_t number_iterations) + : TestUnit("Compare and Swap Stress test for Atomics"), swap1_counter(1) + , swap2_counter(2) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressCompareAndSwap::Init, this); + + Add(&TestStressCompareAndSwap::compare_and_swap1, this, + number_threads, number_iterations); + Add(&TestStressCompareAndSwap::compare_and_swap2, this, + number_threads, number_iterations); + + Post(&TestStressCompareAndSwap::CheckAndDestroy, this); +} + +void AtomicTest::TestStressCompareAndSwap::Init() { +} + +void AtomicTest::TestStressCompareAndSwap::CheckAndDestroy() { + // Each element is read at most once + bitsets[2] = bitsets[0]; + bitsets[2] &= bitsets[1]; + PT_EXPECT(bitsets[2].none()); + bitsets[2] = bitsets[0]; + bitsets[2] |= bitsets[1]; + // All elements except one are read (push, pop, push and so on... the last + // push is not read), however this must not always be the same element. + PT_EXPECT(bitsets[2].count() == + static_cast(AtomicTest::GetNumberOfIterations() * 2)); +} + +void AtomicTest::TestStressCompareAndSwap::compare_and_swap1() { + int j(0); + while (!swap_value.CompareAndSwap(j, swap1_counter)) {} + PT_EXPECT(!(bitsets[0].test(static_cast(j)))); + bitsets[0].set(static_cast(j)); + swap1_counter += 2; +} + +void AtomicTest::TestStressCompareAndSwap::compare_and_swap2() { + int j(0); + while (!swap_value.CompareAndSwap(j, swap2_counter)) {} + PT_EXPECT(!(bitsets[1].test(static_cast(j)))); + bitsets[1].set(static_cast(j)); + swap2_counter += 2; +} + + + +AtomicTest::AtomicTest() { + numIterations_ = partest::TestSuite::GetDefaultNumIterations(); + PT_ASSERT_LT_MSG(numIterations_, + static_cast(ATOMIC_TESTS_ITERATIONS), "Maximum allowed iterations"); + CreateUnit("BasicTestsSingleThreaded") + .Add(&AtomicTest::BasicTests, this); + CreateUnit(static_cast(1), numIterations_); + CreateUnit(static_cast(1), numIterations_); + CreateUnit(static_cast(1), + numIterations_); + CreateUnit(static_cast(1), numIterations_); + CreateUnit(static_cast(1), numIterations_); +} + +typedef enum { RED, GREEN, BLUE } colors_t; + +void AtomicTest::BasicTests() { + embb::base::Atomic b; // Boolean + embb::base::Atomic c; // Enumeration + embb::base::Atomic v; // Void pointer + embb::base::Atomic i; // Integer + embb::base::Atomic n; // Non-void pointer + + //template specializations + PT_EXPECT(!b.IsArithmetic() && !b.IsInteger() && !b.IsPointer()); + PT_EXPECT(!c.IsArithmetic() && !c.IsInteger() && !c.IsPointer()); + PT_EXPECT(!v.IsArithmetic() && !v.IsInteger() && !v.IsPointer()); + PT_EXPECT(i.IsArithmetic() && i.IsInteger() && !i.IsPointer()); + PT_EXPECT(n.IsArithmetic() && !n.IsInteger() && n.IsPointer()); + + + // Constructors + PT_EXPECT(c == RED); + colors_t d(GREEN); + PT_EXPECT(d == GREEN); + // Assignment + PT_EXPECT((c = GREEN) == GREEN); + PT_EXPECT(c == GREEN); + // Swap + PT_EXPECT(c.Swap(BLUE) == GREEN); + PT_EXPECT(c == BLUE); + // Compare-and-swap + + d = RED; + PT_EXPECT(!c.CompareAndSwap(d, GREEN)); + PT_EXPECT(d == BLUE); + PT_EXPECT(c.CompareAndSwap(d, GREEN)); + PT_EXPECT(c == GREEN); + + //Arithmetic opertions... + PT_EXPECT(i == 0); + // Fetch-and-add + PT_EXPECT(i.FetchAndAdd(10) == 0); + PT_EXPECT(i == 10); + // Fetch-and-sub + PT_EXPECT(i.FetchAndSub(5) == 10); + PT_EXPECT(i == 5); + // Increment (postfix) + PT_EXPECT(i++ == 5); + PT_EXPECT(i == 6); + // Decrement (postfix) + PT_EXPECT(i-- == 6); + PT_EXPECT(i == 5); + // Increment (prefix) + PT_EXPECT(++i == 6); + PT_EXPECT(i == 6); + // Decrement (prefix) + PT_EXPECT(--i == 5); + PT_EXPECT(i == 5); + // Addition + PT_EXPECT((i += 10) == 15); + PT_EXPECT(i == 15); + // Subtraction + PT_EXPECT((i -= 10) == 5); + PT_EXPECT(i == 5); + + //Boolean operations... + // And + i = 0; + i &= 1; + PT_EXPECT(i == 0); + i = 1; + i &= 1; + PT_EXPECT(i == 1); + // Or + i = 1; + i |= 0; + PT_EXPECT(i == 1); + i |= 1; + PT_EXPECT(i == 1); + i = 0; + i |= 0; + PT_EXPECT(i == 0); + // Xor + i = 0; + i ^= 0; + PT_EXPECT(i == 0); + i ^= 1; + PT_EXPECT(i == 1); + + //Pointers...; + n = NULL; + // Stride + PT_EXPECT((uintptr_t)++n == sizeof(int)); + // Dereferencing + n = new int(0x13579BDF); + PT_EXPECT(*n == 0x13579BDF); + delete n; + + // Scalar values + embb::base::Atomic *j = new embb::base::Atomic(); + PT_EXPECT(*j == 0); + int *k = new int(0); + PT_EXPECT(j->CompareAndSwap(*k, 0x13579BDF)); + PT_EXPECT(*j == 0x13579BDF); + delete j; + delete k; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_cpp/test/atomic_test.h a/base_cpp/test/atomic_test.h new file mode 100644 index 0000000..9dea845 --- /dev/null +++ a/base_cpp/test/atomic_test.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_ATOMIC_TEST_H_ +#define BASE_CPP_TEST_ATOMIC_TEST_H_ + +#include +#include +#include +#include + +//we need that statically, otherwise we cannot use bitset in test +#define ATOMIC_TESTS_ITERATIONS 10000 + +namespace embb { +namespace base { +namespace test { + +class AtomicTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + AtomicTest(); + + static size_t GetNumberOfIterations() { + return numIterations_; + } + + private: + static size_t numIterations_; + void BasicTests(); + + + class TestStressLoadStore : public partest::TestUnit { + public: + TestStressLoadStore(size_t number_threads, size_t number_iterations); + + private: + void barrier(int thread); + void write_x(); + void write_y(); + void read_x_then_y(); + void read_y_then_x(); + + embb::base::Atomic x, y; + embb::base::Atomic z; + + // A sense-reversing barrier + // See M. Herlihy and N. Shavit. The Art of Multiprocessor Programming. + // Morgan Kaufmann, 2008. + + embb::base::Atomic count; + embb::base::Atomic sense; + + // Do not use a std::vector here, since std::vector may pack + // multiple elements into a single word. As a result, they cannnot be + // modified independently by multiple threads. + bool thread_sense[4]; + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressProduceConsume : public partest::TestUnit { + public: + TestStressProduceConsume(size_t number_threads, size_t number_iterations); + + private: + size_t prod_cons_value; + embb::base::Atomic flag; + int counter_producer; + int counter_consumer; + + void produce(); + void consume(); + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressIncrementDecrement : public partest::TestUnit { + public: + TestStressIncrementDecrement(size_t number_threads, size_t number_iterations); + + private: + embb::base::Atomic inc_dec_value; + + void increment(); + void decrement(); + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressSwap : public partest::TestUnit { + public: + TestStressSwap(size_t number_threads, size_t number_iterations); + + private: + std::bitset bitsets[3]; + embb::base::Atomic swap_value; + + int swap1_counter; + int swap2_counter; + + void swap1(); + void swap2(); + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressCompareAndSwap : public partest::TestUnit { + public: + TestStressCompareAndSwap(size_t number_threads, size_t number_iterations); + + private: + std::bitset bitsets[3]; + embb::base::Atomic swap_value; + + int swap1_counter; + int swap2_counter; + + void compare_and_swap1(); + void compare_and_swap2(); + void Init(); + void CheckAndDestroy(); + }; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_ATOMIC_TEST_H_ diff --git b/base_cpp/test/condition_var_test.cc a/base_cpp/test/condition_var_test.cc new file mode 100644 index 0000000..1159c38 --- /dev/null +++ a/base_cpp/test/condition_var_test.cc @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + + +namespace embb { +namespace base { +namespace test { + +ConditionVarTest::ConditionVarTest() + : num_threads_(partest::TestSuite::GetDefaultNumThreads()) { + embb_counter_init(&counter_); + CreateUnit("Timed wait timouts") + .Add(&ConditionVarTest::TestTimedWaitTimeouts, this); + + if (num_threads_ >= 2) { + CreateUnit("Condition Notify Test") + .Add(&ConditionVarTest::TestNotify, this, num_threads_); + } else { + std::cout << + "Warning: Condition Notify Test needs a minimum of two threads" + << std::endl; + } +} + +void ConditionVarTest::TestTimedWaitTimeouts() { + // Set up data structures + ConditionVariable cond; + Mutex mutex; + UniqueLock lock(mutex); + + // Wait for now tests already passed time point + bool success = cond.WaitUntil(lock, Time()); + PT_EXPECT_EQ(success, false); + + // Wait for a future timepoint + success = cond.WaitUntil(lock, Time(DurationMilliseconds(1))); + PT_EXPECT_EQ(success, false); + + // Wait for a zero duration + success = cond.WaitFor(lock, DurationMilliseconds()); + PT_EXPECT_EQ(success, false); + + // Wait for some duration + success = cond.WaitFor(lock, DurationMilliseconds(1)); + PT_EXPECT_EQ(success, false); +} + +void ConditionVarTest::TestNotify() { + size_t threadID = partest::TestSuite::GetCurrentThreadID(); + + if (threadID != 0) { + UniqueLock lock(mutex_cond_notify_); + embb_counter_increment(&counter_); + cond_notify_.Wait(lock); + embb_counter_increment(&counter_); + } else { + while (embb_counter_get(&counter_) < + static_cast(num_threads_-1)) + {} // all threads entered critical section + UniqueLock lock_notify(mutex_cond_notify_); + lock_notify.Unlock(); + // All threads called wait on the condition (Even last thread) + + embb_counter_init(&counter_); + UniqueLock lock_wait(mutex_cond_wait_); + + cond_notify_.NotifyOne(); + + cond_wait_.WaitUntil(lock_wait, Time(DurationMilliseconds(1))); + while (embb_counter_get(&counter_) == 0) + {} // If hangs here signal has not succeeded + PT_ASSERT_EQ_MSG(embb_counter_get(&counter_), + static_cast(1), "Only 1 thread notified"); + cond_notify_.NotifyAll(); + + cond_wait_.WaitUntil(lock_wait, Time(DurationMilliseconds(2))); + + while (embb_counter_get(&counter_) != + static_cast(num_threads_-1)) + {} // If this hangs then not all threads were notified. + } +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git b/base_cpp/test/condition_var_test.h a/base_cpp/test/condition_var_test.h new file mode 100644 index 0000000..597fc9f --- /dev/null +++ a/base_cpp/test/condition_var_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_CONDITION_VAR_TEST_H_ +#define BASE_CPP_TEST_CONDITION_VAR_TEST_H_ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +class ConditionVarTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ConditionVarTest(); + + private: + /** + * Tests timeout of condition wait until and wait for methods. + */ + void TestTimedWaitTimeouts(); + + /** + * Tests if notify works correctly when each thread waits on the same lock. + */ + void TestNotify(); + + size_t num_threads_; + embb_counter_t counter_; + ConditionVariable cond_notify_; + Mutex mutex_cond_notify_; + ConditionVariable cond_wait_; + Mutex mutex_cond_wait_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_CONDITION_VAR_TEST_H_ diff --git b/base_cpp/test/core_set_test.cc a/base_cpp/test/core_set_test.cc new file mode 100644 index 0000000..b23db7b --- /dev/null +++ a/base_cpp/test/core_set_test.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +CoreSetTest::CoreSetTest() { + CreateUnit("Test all").Add(&CoreSetTest::Test, this); +} + +void CoreSetTest::Test() { + unsigned int cores = CoreSet::CountAvailable(); + PT_EXPECT_GT(cores, static_cast(0)); + { // Default construction + CoreSet set; + PT_EXPECT_EQ(set.Count(), (size_t)0); + } + { // One argument construction with no cores set + CoreSet set(false); + PT_EXPECT_EQ(set.Count(), (size_t)0); + } + { // One argument construction with all cores set + CoreSet set(true); + PT_EXPECT_EQ(set.Count(), cores); + } + { // Copy construction + CoreSet set(true); + CoreSet copy(set); + PT_EXPECT_EQ(copy.Count(), set.Count()); + } + { // Assignment + CoreSet set(true); + CoreSet assigned = set; + PT_EXPECT_EQ(assigned.Count(), set.Count()); + } + { // Resetting all cores + CoreSet set; + set.Reset(true); + PT_EXPECT_EQ(set.Count(), cores); + set.Reset(false); + PT_EXPECT_EQ(set.Count(), (size_t)0); + } + { // Adding, removing, and checking single cores + CoreSet set; + for (unsigned int i = 0; i < cores; i++) { + set.Add(i); + bool is_contained = set.IsContained(i); + PT_EXPECT_EQ(is_contained, true); + if (i < cores - 1) { + is_contained = set.IsContained(i+1); + PT_EXPECT_EQ(is_contained, false); + } + PT_EXPECT_EQ(set.Count(), i+1); + } + for (unsigned int i = 0; i < cores; i++) { + set.Remove(i); + bool is_contained = set.IsContained(i); + PT_EXPECT_EQ(is_contained, false); + PT_EXPECT_EQ(set.Count(), cores - i - 1); + } + } + { // Logical operators + CoreSet lhs(false); + CoreSet rhs(true); + PT_EXPECT_EQ((lhs & rhs).Count(), (size_t)0); + PT_EXPECT_EQ((lhs | rhs).Count(), cores); + lhs &= rhs; + PT_EXPECT_EQ(lhs.Count(), (size_t)0); + lhs |= rhs; + PT_EXPECT_EQ(lhs.Count(), rhs.Count()); + } +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git b/base_cpp/test/core_set_test.h a/base_cpp/test/core_set_test.h new file mode 100644 index 0000000..bf70437 --- /dev/null +++ a/base_cpp/test/core_set_test.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_CORE_SET_TEST_H_ +#define BASE_CPP_TEST_CORE_SET_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for Core and CoreSet. + */ +class CoreSetTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + CoreSetTest(); + + private: + /** + * Tests all functionalities. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_CORE_SET_TEST_H_ diff --git b/base_cpp/test/duration_test.cc a/base_cpp/test/duration_test.cc new file mode 100644 index 0000000..dcad281 --- /dev/null +++ a/base_cpp/test/duration_test.cc @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace embb { +namespace base { +namespace test { + +DurationTest::DurationTest() { + CreateUnit("Seconds").Add(&DurationTest::Test, this); + CreateUnit("Milliseconds").Add(&DurationTest::Test, + this); + CreateUnit("Microseconds").Add(&DurationTest::Test, + this); + CreateUnit("Nanoseconds").Add(&DurationTest::Test, + this); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git b/base_cpp/test/duration_test.h a/base_cpp/test/duration_test.h new file mode 100644 index 0000000..d36e2b5 --- /dev/null +++ a/base_cpp/test/duration_test.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_DURATION_TEST_H_ +#define BASE_CPP_TEST_DURATION_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class DurationTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + DurationTest(); + + private: + /** + * Test second ticks. + */ + template + void Test(); +}; + +template +void DurationTest::Test() { + Duration duration; + PT_EXPECT_EQ(duration.Count(), (size_t)0); + + unsigned long long min_ticks = Duration::Min().Count(); + + duration += Duration(min_ticks); + PT_EXPECT_EQ(duration.Count(), min_ticks); + + duration += duration + duration; + PT_EXPECT_EQ(duration.Count(), min_ticks * 3); + + Duration duration_copy(duration); + PT_EXPECT_EQ(duration_copy.Count(), min_ticks * 3); + + Duration duration_assigned = duration; + PT_EXPECT_EQ(duration_assigned.Count(), min_ticks * 3); +} + +} // namespace test +} // namespace base +} // namespace embb + + +#endif // BASE_CPP_TEST_DURATION_TEST_H_ diff --git b/base_cpp/test/log_test.cc a/base_cpp/test/log_test.cc new file mode 100644 index 0000000..fe8fde8 --- /dev/null +++ a/base_cpp/test/log_test.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace test { + +LogTest::LogTest() { + CreateUnit("Test all").Add(&LogTest::Test, this); +} + +static std::string logged_message; + +static void test_log_function(void * context, char const * msg) { + EMBB_UNUSED(context); + logged_message = msg; +} + +void LogTest::Test() { + using embb::base::Log; + char const * test_msg = "hello"; + + Log::SetLogFunction(0, test_log_function); + + Log::SetLogLevel(EMBB_LOG_LEVEL_TRACE); + logged_message = "none"; + Log::Trace("chn", test_msg); +#ifdef EMBB_DEBUG + PT_EXPECT(logged_message == "[chn] - [TRACE] hello"); +#else + PT_EXPECT(logged_message == "none"); +#endif + logged_message = "none"; + Log::Info("chn", test_msg); +#ifdef EMBB_DEBUG + PT_EXPECT(logged_message == "[chn] - [INFO ] hello"); +#else + PT_EXPECT(logged_message == "none"); +#endif + logged_message = "none"; + Log::Warning("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [WARN ] hello"); + logged_message = "none"; + Log::Error("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [ERROR] hello"); + + Log::SetLogLevel(EMBB_LOG_LEVEL_INFO); + logged_message = "none"; + Log::Trace("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Info("chn", test_msg); +#ifdef EMBB_DEBUG + PT_EXPECT(logged_message == "[chn] - [INFO ] hello"); +#else + PT_EXPECT(logged_message == "none"); +#endif + logged_message = "none"; + Log::Warning("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [WARN ] hello"); + logged_message = "none"; + Log::Error("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [ERROR] hello"); + + Log::SetLogLevel(EMBB_LOG_LEVEL_WARNING); + logged_message = "none"; + Log::Trace("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Info("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Warning("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [WARN ] hello"); + logged_message = "none"; + Log::Error("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [ERROR] hello"); + + Log::SetLogLevel(EMBB_LOG_LEVEL_ERROR); + logged_message = "none"; + Log::Trace("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Info("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Warning("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Error("chn", test_msg); + PT_EXPECT(logged_message == "[chn] - [ERROR] hello"); + + Log::SetLogLevel(EMBB_LOG_LEVEL_NONE); + logged_message = "none"; + Log::Trace("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Info("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Warning("chn", test_msg); + PT_EXPECT(logged_message == "none"); + logged_message = "none"; + Log::Error("chn", test_msg); + PT_EXPECT(logged_message == "none"); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git b/base_cpp/test/log_test.h a/base_cpp/test/log_test.h new file mode 100644 index 0000000..d258a4e --- /dev/null +++ a/base_cpp/test/log_test.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_LOG_TEST_H_ +#define BASE_CPP_TEST_LOG_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for Log. + */ +class LogTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + LogTest(); + + private: + /** + * Tests all functionalities. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_LOG_TEST_H_ diff --git b/base_cpp/test/main.cc a/base_cpp/test/main.cc new file mode 100644 index 0000000..31b57d2 --- /dev/null +++ a/base_cpp/test/main.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using embb::base::test::CoreSetTest; +using embb::base::test::DurationTest; +using embb::base::test::ConditionVarTest; +using embb::base::test::MutexTest; +using embb::base::test::SpinLockTest; +using embb::base::test::ThreadSpecificStorageTest; +using embb::base::test::AtomicTest; +using embb::base::test::MemoryAllocationTest; +using embb::base::test::ThreadTest; +using embb::base::test::LogTest; + +PT_MAIN("Base C++") { + unsigned int max_threads = + static_cast(2 * partest::TestSuite::GetDefaultNumThreads()); + embb_thread_set_max_count(max_threads); + + PT_RUN(CoreSetTest); + PT_RUN(DurationTest); + PT_RUN(ConditionVarTest); + PT_RUN(MutexTest); + PT_RUN(SpinLockTest); + PT_RUN(ThreadSpecificStorageTest); + PT_RUN(AtomicTest); + PT_RUN(MemoryAllocationTest); + PT_RUN(ThreadTest); + PT_RUN(LogTest); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/base_cpp/test/memory_allocation_test.cc a/base_cpp/test/memory_allocation_test.cc new file mode 100644 index 0000000..f43a66a --- /dev/null +++ a/base_cpp/test/memory_allocation_test.cc @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +MemoryAllocationTest::MemoryAllocationTest() { + CreateUnit("ClassAllocationTest") + .Add(&MemoryAllocationTest::ClassAllocationTest, this); + CreateUnit("AllocatorTest").Add(&MemoryAllocationTest::AllocatorTest, this); +} + +void MemoryAllocationTest::AllocatorTest() { + static const int alloc_iterations = 10000; + + ::std::vector < DummyClassForAllocatorTests, + embb::base::AllocatorCacheAligned< DummyClassForAllocatorTests > > + aligned_allocating_vector; + + ::std::vector < DummyClassForAllocatorTests, + embb::base::Allocator< DummyClassForAllocatorTests > > + unaligned_allocating_vector; + + for (unsigned int i = 0; + i != static_cast(alloc_iterations); + ++i) { + DummyClassForAllocatorTests d; + d.A = 0xF10000 | i; + d.F = 0xF20000 | i; + aligned_allocating_vector.push_back(d); + unaligned_allocating_vector.push_back(d); + } + + for (unsigned int i = 0; + i != static_cast(alloc_iterations); + ++i) { + unsigned int A = 0xF10000 | i; + unsigned int F = 0xF20000 | i; + PT_ASSERT_EQ(aligned_allocating_vector[i].A, A); + PT_ASSERT_EQ(aligned_allocating_vector[i].F, F); + PT_ASSERT_EQ(unaligned_allocating_vector[i].A, A); + PT_ASSERT_EQ(unaligned_allocating_vector[i].F, F); + } +} + +void MemoryAllocationTest::ClassAllocationTest() { + ::std::vector< DummyClassForAlignedAllocation* > aligned_allocs; + ::std::vector< DummyClassForUnalignedAllocation* > unaligned_allocs; + + size_t expected = 0; + + static const unsigned int alloc_iterations = 10; + + for (unsigned int i = 0; i != alloc_iterations; ++i) { + aligned_allocs.push_back( new DummyClassForAlignedAllocation ); + //write something + aligned_allocs[i]->b = 0xF000 | i; +#ifdef EMBB_DEBUG + size_t n = (sizeof(DummyClassForAlignedAllocation) + + (EMBB_PLATFORM_CACHE_LINE_SIZE - 1)) / EMBB_PLATFORM_CACHE_LINE_SIZE; + expected += (n + 1)*EMBB_PLATFORM_CACHE_LINE_SIZE + + (sizeof(size_t) * 3 - 1); +#endif // else EMBB_DEBUG + + //check that the memory is aligned! + PT_ASSERT_EQ((uintptr_t)aligned_allocs[i] % EMBB_PLATFORM_CACHE_LINE_SIZE, + (uintptr_t)0); + } + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + //delete! + expected = 0; + for (unsigned int i = 0; i != alloc_iterations; ++i) { + //check if written correctly + PT_ASSERT_EQ(aligned_allocs[i]->b, 0xF000 | i); + delete aligned_allocs[i]; + } + + //everything should be deleted + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + for (unsigned int i = 0; i != alloc_iterations; ++i) { + unaligned_allocs.push_back(new DummyClassForUnalignedAllocation); + //write something + unaligned_allocs[i]->b = 0xA000 | i; + + +#ifdef EMBB_DEBUG + expected += sizeof(DummyClassForUnalignedAllocation) + 2 * sizeof(size_t); +#endif // else EMBB_DEBUG + } + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + //delete! + expected = 0; + for (unsigned int i = 0; i != alloc_iterations; ++i) { + //check if written correctly + PT_ASSERT_EQ(unaligned_allocs[i]->b, 0xA000 | i); + delete unaligned_allocs[i]; + } + + //everything should be deleted + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + + DummyClassForAlignedAllocation * aligned_allocated = + new DummyClassForAlignedAllocation[alloc_iterations]; +#ifdef EMBB_DEBUG + size_t n = (sizeof(DummyClassForAlignedAllocation)*alloc_iterations + + (EMBB_PLATFORM_CACHE_LINE_SIZE - 1)) / EMBB_PLATFORM_CACHE_LINE_SIZE; + expected += (n + 1)*EMBB_PLATFORM_CACHE_LINE_SIZE + + (sizeof(size_t) * 3 - 1); +#endif // else EMBB_DEBUG + + // This assert does _not_ hold, but is left for documentation. + // It is not guaranteed that the pointer to the array is aligned. + // See the documentation of the overloaded new[] operator in + // class MemoryAllocation. + // PT_ASSERT_EQ((uintptr_t)aligned_allocated % + // EMBB_PLATFORM_CACHE_LINE_SIZE, 0); + + //delete! + expected = 0; + delete[] aligned_allocated; + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + DummyClassForUnalignedAllocation * unaligned_allocated = + new DummyClassForUnalignedAllocation[alloc_iterations]; +#ifdef EMBB_DEBUG + expected += sizeof(DummyClassForUnalignedAllocation)*alloc_iterations + + 2 * sizeof(size_t); +#endif // else EMBB_DEBUG + + PT_ASSERT(embb::base::Allocation::AllocatedBytes() >= expected); + + //delete! + expected = 0; + delete[] unaligned_allocated; + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_cpp/test/memory_allocation_test.h a/base_cpp/test/memory_allocation_test.h new file mode 100644 index 0000000..2c1dbda --- /dev/null +++ a/base_cpp/test/memory_allocation_test.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_MEMORY_ALLOCATION_TEST_H_ +#define BASE_CPP_TEST_MEMORY_ALLOCATION_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class MemoryAllocationTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + MemoryAllocationTest(); + + private: + void ClassAllocationTest(); + void AllocatorTest(); + + class DummyClassForAllocatorTests { + public: + unsigned int A; + double B; + long C; + long long D; + long E; + unsigned int F; + }; + + class DummyClassForAlignedAllocation + : public embb::base::CacheAlignedAllocatable { + public: + int a; + size_t b; + }; + + class DummyClassForUnalignedAllocation + : public embb::base::Allocatable { + public: + int a; + size_t b; + }; +}; + +} // namespace test +} // namespace base +} // namespace embb + + +#endif // BASE_CPP_TEST_MEMORY_ALLOCATION_TEST_H_ diff --git b/base_cpp/test/mutex_test.cc a/base_cpp/test/mutex_test.cc new file mode 100644 index 0000000..496b3c8 --- /dev/null +++ a/base_cpp/test/mutex_test.cc @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { +MutexTest::MutexTest() : mutex_(), counter_(0), + number_threads_(partest::TestSuite::GetDefaultNumThreads()), + number_iterations_(partest::TestSuite::GetDefaultNumIterations()) { + CreateUnit("Mutex protected counter") + .Pre(&MutexTest::PreMutexCount, this) + .Add(&MutexTest::TestMutexCount, this, number_threads_, + number_iterations_) + .Post(&MutexTest::PostMutexCount, this); + CreateUnit("Recursive mutex") + .Add(&MutexTest::TestRecursiveMutex, this); + CreateUnit("Lock guard protected counter") + .Pre(&MutexTest::PreLockGuardCount, this) + .Add(&MutexTest::TestLockGuardCount, this, number_threads_, + number_iterations_) + .Post(&MutexTest::PostLockGuardCount, this); + CreateUnit("Unique Lock").Add(&MutexTest::TestUniqueLock, this); +} + +void MutexTest::PreMutexCount() { + counter_ = 0; +} + +void MutexTest::TestMutexCount() { + mutex_.Lock(); + ++counter_; + mutex_.Unlock(); +} + +void MutexTest::PostMutexCount() { + PT_EXPECT_EQ((size_t)counter_, number_iterations_ * number_threads_); +} + +void MutexTest::TestRecursiveMutex() { + embb::base::RecursiveMutex mutex; + int number = 5; + for (int i = 0; i < number; i++) { + mutex.Lock(); + bool obtained = mutex.TryLock(); + PT_EXPECT_EQ(obtained, true); + } + for (int i = 0; i < number; i++) { + mutex.Unlock(); + mutex.Unlock(); + } +} + +void MutexTest::PreLockGuardCount() { + counter_ = 0; +} + +void MutexTest::TestLockGuardCount() { + LockGuard guard(mutex_); + ++counter_; +} + +void MutexTest::PostLockGuardCount() { + PT_EXPECT_EQ((size_t)counter_, number_iterations_ * number_threads_); +} + +void MutexTest::TestUniqueLock() { + { // Test standard usage and releasing +#ifdef EMBB_USE_EXCEPTIONS + bool exception_thrown = false; +#endif + UniqueLock lock(mutex_); + PT_EXPECT_EQ(lock.OwnsLock(), true); + +#ifdef EMBB_USE_EXCEPTIONS + // Locked lock should not by re-lockable + EMBB_TRY { + lock.Lock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); + + // Locked lock should not by re-try-lockable + EMBB_TRY { + lock.TryLock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); +#endif // EMBB_USE_EXCEPTIONS + + lock.Unlock(); + PT_EXPECT_EQ(lock.OwnsLock(), false); + +#ifdef EMBB_USE_EXCEPTIONS + // Unlocked lock should not by re-unlockable + exception_thrown = false; + EMBB_TRY { + lock.Unlock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); +#endif // EMBB_USE_EXCEPTIONS + + lock.Lock(); + PT_EXPECT_EQ(lock.OwnsLock(), true); + + lock.Release()->Unlock(); + PT_EXPECT_EQ(lock.OwnsLock(), false); + +#ifdef EMBB_USE_EXCEPTIONS + // Released lock should not by lockable + exception_thrown = false; + EMBB_TRY { + lock.Lock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); + + // Released lock should not by try-lockable + exception_thrown = false; + EMBB_TRY { + lock.TryLock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); + + // Released lock should not by unlockable + exception_thrown = false; + EMBB_TRY { + lock.Unlock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); +#endif // EMBB_USE_EXCEPTIONS + } + + { // Test deferred lock construction + UniqueLock<> lock(mutex_, embb::base::defer_lock); + PT_EXPECT_EQ(lock.OwnsLock(), false); + } + + { // Test try-lock construction + UniqueLock<> lock(mutex_, embb::base::try_lock); + PT_EXPECT_EQ(lock.OwnsLock(), true); + } + + { // Test adopt lock construction + mutex_.Lock(); + UniqueLock<> lock(mutex_, embb::base::adopt_lock); + PT_EXPECT_EQ(lock.OwnsLock(), true); + } + + { // Test lock swapping + UniqueLock<> lock1(mutex_); + PT_EXPECT_EQ(lock1.OwnsLock(), true); + + { + UniqueLock<> lock2; + PT_EXPECT_EQ(lock2.OwnsLock(), false); + + lock1.Swap(lock2); + PT_EXPECT_EQ(lock1.OwnsLock(), false); + PT_EXPECT_EQ(lock2.OwnsLock(), true); + } + + // At this point, "lock2" was destroyed and "mutex_" must be unlocked. + UniqueLock<> lock3(mutex_, embb::base::try_lock); + PT_EXPECT_EQ(lock3.OwnsLock(), true); + } +} + +SpinLockTest::SpinLockTest() : spinlock_(), counter_(0), +number_threads_(partest::TestSuite::GetDefaultNumThreads()), + number_iterations_(partest::TestSuite::GetDefaultNumIterations()), + counter_iterations_(10000) { + CreateUnit("Spinlock protected counter (using Lock)") + .Add(&SpinLockTest::TestSpinlockCountLock, this, number_threads_, + number_iterations_) + .Post(&SpinLockTest::PostSpinlockCount, this); + + CreateUnit("Spinlock protected counter (using Trylock)") + .Add(&SpinLockTest::TestSpinlockCountLockTryLock, this, number_threads_, + number_iterations_) + .Post(&SpinLockTest::PostSpinlockCount, this); + + CreateUnit("Test spinning (too many spins), single thread") + .Add(&SpinLockTest::TestSpinLockTooManySpins, this, 1, 1); +} + +void SpinLockTest::TestSpinlockCountLock() { + for (unsigned int i = 0; i != counter_iterations_; ++i) { + spinlock_.Lock(); + counter_++; + spinlock_.Unlock(); + } +} + +void SpinLockTest::TestSpinlockCountLockTryLock() { + for (unsigned int i = 0; i != counter_iterations_; ++i) { + while (!spinlock_.TryLock()) {} + counter_++; + spinlock_.Unlock(); + } +} + +void SpinLockTest::PostSpinlockCount() { + PT_EXPECT_EQ(counter_, + number_iterations_ * + number_threads_* + counter_iterations_); + counter_ = 0; +} + +void SpinLockTest::TestSpinLockTooManySpins() { + Spinlock lock; + lock.Lock(); + bool success = lock.TryLock(100); + PT_ASSERT(!success); + lock.Unlock(); + success = lock.TryLock(100); + PT_ASSERT(success); +} +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_cpp/test/mutex_test.h a/base_cpp/test/mutex_test.h new file mode 100644 index 0000000..793ff90 --- /dev/null +++ a/base_cpp/test/mutex_test.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_MUTEX_TEST_H_ +#define BASE_CPP_TEST_MUTEX_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { +/** + * Provides tests for class Mutex. + */ +class MutexTest : public partest::TestCase { + public: + /** + * Constructs the test case and adds test units. + */ + MutexTest(); + + private: + /** + * Uses Mutex to realize multi-threaded counting. + */ + void PreMutexCount(); + void TestMutexCount(); + void PostMutexCount(); + + /** + * Tests the multiple locking and unlocking of a recursive mutex. + */ + void TestRecursiveMutex(); + + /** + * Uses LockGuard to realize multi-threaded counting. + */ + void PreLockGuardCount(); + void TestLockGuardCount(); + void PostLockGuardCount(); + + /** + * Tests the different states of a UniqueLock. + */ + void TestUniqueLock(); + + /** + * Mutex for tests. + */ + embb::base::Mutex mutex_; + + /** + * Shared counter to check effectiveness of mutex. + */ + int counter_; + + /** + * Number of threads used to run tests. + */ + size_t number_threads_; + + /** + * Number of times the test method is called by each thread. + */ + size_t number_iterations_; +}; + +class SpinLockTest : public partest::TestCase { + public: + SpinLockTest(); + + private: + /** + * Uses Spinlock to realize multi-threaded counting. + */ + void TestSpinlockCountLock(); + void TestSpinlockCountLockTryLock(); + void PostSpinlockCount(); + + /** + * Test that TryLock returns false, if lock is already locked. + */ + void TestSpinLockTooManySpins(); + + /** + * Spinlock for tests + */ + Spinlock spinlock_; + + /** + * Shared counter to check effectiveness of mutex. + */ + size_t counter_; + + /** + * Number of threads used to run tests. + */ + size_t number_threads_; + + /** + * Number of times the test method is called by each thread. + */ + size_t number_iterations_; + + /** + * Number of internal iterations, for incrementing the counter. + */ + size_t counter_iterations_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_MUTEX_TEST_H_ diff --git b/base_cpp/test/thread_specific_storage_test.cc a/base_cpp/test/thread_specific_storage_test.cc new file mode 100644 index 0000000..398af51 --- /dev/null +++ a/base_cpp/test/thread_specific_storage_test.cc @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +ThreadSpecificStorageTest::ThreadSpecificStorageTest() + : number_threads_(partest::TestSuite::GetDefaultNumThreads()), tss_() { + PT_EXPECT_GT(Thread::GetThreadsMaxCount(), number_threads_); + CreateUnit("Internal representation") + .Add(&ThreadSpecificStorageTest::TestInternalRepresentation, this, + 1, partest::TestSuite::GetDefaultNumIterations()); + CreateUnit("Multiple TSS variables") + .Add(&ThreadSpecificStorageTest::TestMultipleTSSVariables, this); + CreateUnit("TestConstructors") + .Add(&ThreadSpecificStorageTest::TestConstructors, this); +} + +void ThreadSpecificStorageTest::TestInternalRepresentation() { + embb_internal_thread_index_reset(); + size_t num_threads = partest::TestSuite::GetDefaultNumThreads(); + embb::base::Thread** threads = new embb::base::Thread*[num_threads]; + for (size_t i = 0; i < num_threads; i++) { + threads[i] = new embb::base::Thread( + ThreadSpecificStorageTest::TestInternalRepresentationSetGet, + &tss_, i); + } + for (size_t i = 0; i < num_threads; i++) { + threads[i]->Join(); + delete threads[i]; + } + delete[] threads; + + // Check results + int sum = 0; + for (size_t i = 0; i < tss_.rep_.size; i++) { + if (i < num_threads) { + PT_EXPECT_EQ(tss_.usage_flags_[i], true); + void* value = tss_.rep_.values[i]; + sum += *static_cast(value); + } else { + PT_EXPECT_EQ(tss_.usage_flags_[i], false); + } + } + int expected_sum = 0; + for (size_t i = 0; i < num_threads; i++) { + expected_sum += static_cast(i); + } + PT_EXPECT_EQ(sum, expected_sum); +} + +void ThreadSpecificStorageTest::TestInternalRepresentationSetGet( + ThreadSpecificStorage* tss, size_t rank) { + assert(tss != NULL); + PT_EXPECT_NE(tss->rep_.values, static_cast(NULL)); + PT_EXPECT_EQ(embb_thread_get_max_count(), tss->rep_.size); + PT_EXPECT_LT(rank, tss->rep_.size); + for (unsigned int i = 0; i < tss->rep_.size; i++) { + PT_EXPECT_NE(tss->rep_.values[i], static_cast(NULL)); + } + tss->Get() = rank; + for (size_t i = 0; i < 10; i++) { + size_t stored_rank = tss->Get(); + PT_EXPECT_EQ(rank, stored_rank); + if (rank != stored_rank) break; + } +} + +void ThreadSpecificStorageTest::TestMultipleTSSVariables() { + ThreadSpecificStorage tss2; + PT_EXPECT_NE(&(tss_.Get()), &(tss2.Get())); +} + +void ThreadSpecificStorageTest::TestConstructors() { + { + ThreadSpecificStorage tss(1); + PT_EXPECT_EQ(tss.Get().var, 1); + } + { + ThreadSpecificStorage tss(1, 2); + PT_EXPECT_EQ(tss.Get().var1, 1); + PT_EXPECT_EQ(tss.Get().var2, 2); + } + { + ThreadSpecificStorage tss(1, 2, 3); + PT_EXPECT_EQ(tss.Get().var1, 1); + PT_EXPECT_EQ(tss.Get().var2, 2); + PT_EXPECT_EQ(tss.Get().var3, 3); + } + { + ThreadSpecificStorage tss(1, 2, 3, 4); + PT_EXPECT_EQ(tss.Get().var1, 1); + PT_EXPECT_EQ(tss.Get().var2, 2); + PT_EXPECT_EQ(tss.Get().var3, 3); + PT_EXPECT_EQ(tss.Get().var4, 4); + } +} + +} // namespace test +} // namespace base +} // namespace embb diff --git b/base_cpp/test/thread_specific_storage_test.h a/base_cpp/test/thread_specific_storage_test.h new file mode 100644 index 0000000..7a07c5d --- /dev/null +++ a/base_cpp/test/thread_specific_storage_test.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ +#define BASE_CPP_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests tls implementation. + */ +class ThreadSpecificStorageTest : public partest::TestCase { + public: + /** + * Adds test units. + */ + ThreadSpecificStorageTest(); + + private: + /** + * Tests the correctness of the internal representation. + */ + void TestInternalRepresentation(); + static void TestInternalRepresentationSetGet( + ThreadSpecificStorage* tss, + size_t rank); + + /** + * Test basic functionality. + */ + void TestMultipleTSSVariables(); + + /** + * Type to test TSS constructors with initializers. + */ + struct OneArgumentConstructorType { + explicit OneArgumentConstructorType(int arg) : var(arg) {} + int var; + }; + /** + * Type to test TSS constructors with initializers. + */ + struct TwoArgumentConstructorType { + TwoArgumentConstructorType(int arg1, int arg2) : var1(arg1), var2(arg2) {} + int var1, var2; + }; + /** + * Type to test TSS constructors with initializers. + */ + struct ThreeArgumentConstructorType { + ThreeArgumentConstructorType(int arg1, int arg2, int arg3) + : var1(arg1), var2(arg2), var3(arg3) {} + int var1, var2, var3; + }; + /** + * Type to test TSS constructors with initializers. + */ + struct FourArgumentConstructorType { + FourArgumentConstructorType(int arg1, int arg2, int arg3, int arg4) + : var1(arg1), var2(arg2), var3(arg3), var4(arg4) {} + int var1, var2, var3, var4; + }; + + /** + * Tests the different constructors. + */ + void TestConstructors(); + + /** + * Used to differentiate between used and unused TSS slots. + */ + size_t number_threads_; + + /** + * TSS used in tests. + */ + ThreadSpecificStorage tss_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ diff --git b/base_cpp/test/thread_test.cc a/base_cpp/test/thread_test.cc new file mode 100644 index 0000000..68de306 --- /dev/null +++ a/base_cpp/test/thread_test.cc @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +const double ThreadTest::eps_ = 1e-15; + +ThreadTest::ThreadTest() + : number_threads_(partest::TestSuite::GetDefaultNumThreads()) { + CreateUnit("Starting and joining") + .Pre(&ThreadTest::PreStartingAndJoining, this) + .Add(&ThreadTest::TestStartingAndJoining, this, number_threads_); + CreateUnit("Affinities") + .Add(&ThreadTest::TestThreadAffinities, this); +} + +void ThreadTest::PreStartingAndJoining() { + embb::base::Thread::SetThreadsMaxCount( + static_cast(6 * (number_threads_+1))); +} + +void ThreadTest::TestStartingAndJoining() { + // Static member start methods + embb::base::Thread thread1(ThreadTest::StaticThreadStart); + int arg1 = 2; + embb::base::Thread thread2(ThreadTest::StaticThreadStartArg1, arg1); + double arg2 = 3.0; + embb::base::Thread thread3(ThreadTest::StaticThreadStartArg2, arg1, arg2); + embb::base::Thread::ID thread3id; + thread3id = thread3.GetID(); + PT_EXPECT_EQ(thread3id, thread3.GetID()); + + // Non-static member start methods with functor + MemberStart start4(&ThreadTest::ThreadStart, this); + embb::base::Thread thread4(start4); + + std::string arg3("StringArgument"); + MemberStartArg1 start5(&ThreadTest::ThreadStartArg1, + this, arg3); + embb::base::Thread thread5(start5); + + std::vector arg4; + arg4.push_back(1); + arg4.push_back(2); + double arg5 = 5.0; + MemberStartArg2, double&), + const std::vector, double&> start6( + &ThreadTest::ThreadStartArg2, this, arg4, arg5); + embb::base::Thread thread6(start6); + + thread1.Join(); + thread2.Join(); + thread3.Join(); + thread4.Join(); + thread5.Join(); + thread6.Join(); +} + +void ThreadTest::TestThreadAffinities() { + embb::base::CoreSet core_set(true); + bool success = true; + EMBB_TRY { + embb::base::Thread thread(core_set, StaticThreadStart); + thread.Join(); + } + EMBB_CATCH(embb::base::Exception&) { + success = false; + } + PT_EXPECT_EQ(success, true); +} + +void ThreadTest::StaticThreadStart() { +} + +void ThreadTest::StaticThreadStartArg1(int arg1) { + PT_EXPECT_EQ(arg1, 2); +} + +void ThreadTest::StaticThreadStartArg2(int arg1, double arg2) { + PT_EXPECT_EQ(arg1, 2); + PT_EXPECT_GT(arg2 - 3.0 + eps_, 0.0); +} + +void ThreadTest::ThreadStart() { +} + +void ThreadTest::ThreadStartArg1(const std::string& arg1) { + PT_EXPECT_EQ(arg1, std::string("StringArgument")); +} + +void ThreadTest::ThreadStartArg2(std::vector arg1, double& arg2) { + PT_EXPECT_EQ(arg1[0], 1); + PT_EXPECT_EQ(arg1[1], 2); + PT_EXPECT_GT(arg2 - 5.0 + eps_, 0.0); +} + +} // namespace test +} // namespace base +} // namespace embb + diff --git b/base_cpp/test/thread_test.h a/base_cpp/test/thread_test.h new file mode 100644 index 0000000..48d2f30 --- /dev/null +++ a/base_cpp/test/thread_test.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_THREAD_TEST_H_ +#define BASE_CPP_TEST_THREAD_TEST_H_ + +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for class embb::base::Thread. + */ +class ThreadTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ThreadTest(); + + private: + /** + * Member thread start functor without arguments. + */ + template + struct MemberStart { + Function f_; + ThreadTest* self_; + MemberStart(Function f, ThreadTest* self) : f_(f), self_(self) {} + void operator()() { (self_->*f_)(); } + }; + + /** + * Member thread start functor with one argument. + */ + template + struct MemberStartArg1 { + Function f_; + ThreadTest* self_; + Arg1 arg1_; + MemberStartArg1(Function f, ThreadTest* self, Arg1 arg1) + : f_(f), self_(self), arg1_(arg1) {} + void operator()() { (self_->*f_)(arg1_); } + private: + /** + * Deactivated to avoid MSVC warning "could not create ..." + */ + MemberStartArg1& operator=(const MemberStartArg1&); + }; + + /** + * Member thread start functor with two arguments. + */ + template + struct MemberStartArg2 { + Function f_; + ThreadTest* self_; + Arg1 arg1_; + Arg2 arg2_; + MemberStartArg2(Function f, ThreadTest* self, Arg1 arg1, Arg2 arg2) + : f_(f), self_(self), arg1_(arg1), arg2_(arg2) {} + void operator()() { (self_->*f_)(arg1_, arg2_); } + private: + /** + * Deactivated to avoid MSVC warning "could not create ..." + */ + MemberStartArg2& operator=(const MemberStartArg2&); + }; + + /** + * Sets required number of maximal threads. + */ + void PreStartingAndJoining(); + + /** + * Tests starting and joining threads in different start methods. + */ + void TestStartingAndJoining(); + + /** + * Tests setting thread affinities. + */ + void TestThreadAffinities(); + + /** + * Static member thread start method without arguments. + */ + static void StaticThreadStart(); + + /** + * Static member thread start method with one parameter. + */ + static void StaticThreadStartArg1(int arg1); + + /** + * Static member thread start method with two parameters. + */ + static void StaticThreadStartArg2(int arg1, double arg2); + + /** + * Member thread start method without arguments. + */ + void ThreadStart(); + + /** + * Member thread start method with one argument. + */ + void ThreadStartArg1(const std::string& arg1); + + /** + * Member thread start method with two arguments. + */ + void ThreadStartArg2(std::vector arg1, double& arg2); + + /** + * Number of threads used to run (some) of the tests. + */ + size_t number_threads_; + + /** + * Floating point comparison absolute difference. + */ + static const double eps_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_THREAD_TEST_H_ diff --git b/base_cpp/test/time_test.h a/base_cpp/test/time_test.h new file mode 100644 index 0000000..da00d6d --- /dev/null +++ a/base_cpp/test/time_test.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_CPP_TEST_TIME_TEST_H_ +#define BASE_CPP_TEST_TIME_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class TimeTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + TimeTest(); + + private: + /** + * Test second ticks. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + + +#endif // BASE_CPP_TEST_TIME_TEST_H_ diff --git b/containers_cpp/CMakeLists.txt a/containers_cpp/CMakeLists.txt new file mode 100644 index 0000000..014d2d3 --- /dev/null +++ a/containers_cpp/CMakeLists.txt @@ -0,0 +1,33 @@ +project (project_embb_containers_cpp) + +file(GLOB_RECURSE EMBB_CONTAINERS_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_CONTAINERS_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_CONTAINERS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_CONTAINERS_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_CONTAINERS_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include) + +add_library (embb_containers_cpp ${EMBB_CONTAINERS_CPP_SOURCES} ${EMBB_CONTAINERS_CPP_HEADERS}) +target_link_libraries(embb_containers_cpp embb_base_cpp) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_containers_cpp_test ${EMBB_CONTAINERS_CPP_TEST_SOURCES}) + target_link_libraries(embb_containers_cpp_test embb_containers_cpp + partest embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_containers_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_containers_cpp DESTINATION lib) diff --git b/containers_cpp/include/embb/containers/containers.h a/containers_cpp/include/embb/containers/containers.h new file mode 100644 index 0000000..8e842a6 --- /dev/null +++ a/containers_cpp/include/embb/containers/containers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_CONTAINERS_H_ +#define EMBB_CONTAINERS_CONTAINERS_H_ + +/** + * \defgroup CPP_CONTAINERS Containers + * \ingroup CPP + * Concurrent data structures, mainly containers + */ + +#include +#include +#include +#include +#include +#include + +#endif // EMBB_CONTAINERS_CONTAINERS_H_ diff --git b/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h a/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h new file mode 100644 index 0000000..9e82f73 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ + +namespace embb { +namespace containers { +namespace internal { +// Visual Studio is complaining, that the return in the last line of this +// function is not reachable. This is true, as long as exceptions are enabled. +// Otherwise, the exception becomes an assertion and with disabling assertions, +// the code becomes reachable. So, disabling this warning. +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4702) +#endif + template< typename GuardType > + unsigned int HazardPointer< GuardType >::GetObjectLocalThreadIndex() { + // first, get the EMBB native thread id. + unsigned int embb_thread_index; + + int return_val = embb_internal_thread_index(&embb_thread_index); + + if (return_val != EMBB_SUCCESS) { + EMBB_THROW(embb::base::ErrorException, "Could not get thread id"); + } + + // iterate over the mappings array + for (unsigned int i = 0; i != max_accessors_count_; ++i) { + // end of mappings? then we need to write our id + if (thread_id_mapping_[i] == -1) { + // try to CAS the initial value with out thread id + int expected = -1; + if (thread_id_mapping_[i].CompareAndSwap(expected, + static_cast(embb_thread_index))) { + //successful, return our mapping + return i; + } + } + + if (thread_id_mapping_[i] == static_cast(embb_thread_index)) { + // found our mapping! + return i; + } + } + + // when we reach this point, we have too many accessors + // (no mapping possible) + EMBB_THROW(embb::base::ErrorException, "Too many accessors"); + + return 0; + } +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + + template< typename GuardType > + void HazardPointer< GuardType >::RemoveGuard(int guard_position) { + const unsigned int my_thread_id = GetObjectLocalThreadIndex(); + + // check invariants... + assert(guard_position < max_guards_per_thread_); + assert(my_thread_id < max_accessors_count_); + + // set guard + guards_[guard_position*max_accessors_count_ + my_thread_id] = + undefined_guard_; + } + + template< typename GuardType > + HazardPointer< GuardType >::HazardPointer( + embb::base::Function freeGuardCallback, + GuardType undefined_guard, int guardsPerThread, int accessors) : + max_accessors_count_(accessors < 0 ? + embb::base::Thread::GetThreadsMaxCount() : accessors), + undefined_guard_(undefined_guard), + max_guards_per_thread_(guardsPerThread), + release_object_callback_(freeGuardCallback), + thread_id_mapping_(static_cast*>( + embb::base::Allocation::Allocate(sizeof(embb::base::Atomic) + *max_accessors_count_))), + guards_(static_cast*> + (embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic< GuardType >) * max_guards_per_thread_ * + max_accessors_count_))), + thread_local_retired_lists_temp_(static_cast + (embb::base::Allocation::Allocate( + sizeof(GuardType) * max_guards_per_thread_ * max_accessors_count_ * + max_accessors_count_ + ))), + thread_local_retired_lists_(static_cast + (embb::base::Allocation::Allocate( + sizeof(GuardType) * max_guards_per_thread_ * max_accessors_count_ * + max_accessors_count_ + ))) { + const unsigned int count_guards = + max_guards_per_thread_ * max_accessors_count_; + + const unsigned int count_ret_elements = + count_guards * max_accessors_count_; + + for (unsigned int i = 0; i != max_accessors_count_; ++i) { + //in-place new for each cell + new (&thread_id_mapping_[i]) embb::base::Atomic < int >(-1); + } + + for (unsigned int i = 0; i != count_guards; ++i) { + //in-place new for each cell + new (&guards_[i]) embb::base::Atomic < GuardType >(undefined_guard); + } + + for (unsigned int i = 0; i != count_ret_elements; ++i) { + //in-place new for each cell + new (&thread_local_retired_lists_temp_[i]) GuardType(undefined_guard); + } + + for (unsigned int i = 0; i != count_ret_elements; ++i) { + //in-place new for each cell + new (&thread_local_retired_lists_[i]) GuardType(undefined_guard); + } + } + + template< typename GuardType > + HazardPointer< GuardType >::~HazardPointer() { + const unsigned int count_guards = + max_guards_per_thread_ * max_accessors_count_; + + const unsigned int count_ret_elements = + count_guards * max_accessors_count_; + + // Release references from all retired lists. Note that for this to work, + // the data structure using hazard pointer has still to be active... So + // first, the hazard pointer class shall be destructed, then the memory + // management class (e.g. some pool). Otherwise, the hazard pointer class + // would try to return memory to an already destructed memory manager. + for (unsigned int i = 0; i != count_ret_elements; ++i) { + GuardType pointerToFree = + thread_local_retired_lists_[i]; + if (pointerToFree == undefined_guard_) { + break; + } + release_object_callback_(pointerToFree); + } + + for (unsigned int i = 0; i != max_accessors_count_; ++i) { + thread_id_mapping_[i].~Atomic(); + } + + embb::base::Allocation::Free(thread_id_mapping_); + + for (unsigned int i = 0; i != count_guards; ++i) { + guards_[i].~Atomic(); + } + + embb::base::Allocation::Free(guards_); + + for (unsigned int i = 0; i != count_ret_elements; ++i) { + thread_local_retired_lists_temp_[i].~GuardType(); + } + + embb::base::Allocation::Free(thread_local_retired_lists_temp_); + + for (unsigned int i = 0; i != count_ret_elements; ++i) { + thread_local_retired_lists_[i].~GuardType(); + } + + embb::base::Allocation::Free(thread_local_retired_lists_); + } + + template< typename GuardType > + void HazardPointer< GuardType >::Guard(int guardPosition, + GuardType guardedElement) { + const unsigned int my_thread_id = GetObjectLocalThreadIndex(); + + // check invariants... + assert(guardPosition < max_guards_per_thread_); + assert(my_thread_id < max_accessors_count_); + + // set guard + guards_[guardPosition*max_accessors_count_ + my_thread_id] = guardedElement; + } + + template< typename GuardType > + size_t HazardPointer< GuardType >::ComputeMaximumRetiredObjectCount( + size_t guardsPerThread, int accessors) { + unsigned int accessorCount = (accessors == -1 ? + embb::base::Thread::GetThreadsMaxCount() : + accessors); + + return static_cast( + guardsPerThread * accessorCount * accessorCount); + } + + /** + * Remark: it might be faster to just swap pointers for temp retired list and + * retired list. However, with the current implementation (one array for all + * retired and retired temp lists, respectively) this is not possible. This is + * not changed until this copying accounts for a performance problem. The + * copying is not the bottleneck currently. + */ + template< typename GuardType > + void HazardPointer< GuardType >::CopyRetiredList(GuardType* sourceList, + GuardType* targetList, unsigned int retiredListSize, + GuardType undefinedGuard) { + bool done = false; + for (unsigned int ii = 0; ii != retiredListSize; ++ii) { + if (!done) { + GuardType guardToCopy = sourceList[ii]; + + if (guardToCopy == undefinedGuard) { + done = true; + + if (targetList[ii] == undefinedGuard) { + // end of target list + break; + } + } + targetList[ii] = guardToCopy; + } else { + // we copied the whole source list, remaining values in the target + // have to be zeroed. + if (targetList[ii] == undefinedGuard) { + // end of target list + break; + } else { + targetList[ii] = undefinedGuard; + } + } + } + } + + template< typename GuardType > + void HazardPointer< GuardType >::UpdateRetiredList(GuardType* retired_list, + GuardType* updated_retired_list, unsigned int retired_list_size, + GuardType guarded_element, GuardType considered_hazard, + GuardType undefined_guard) { + // no hazard set here + if (considered_hazard == undefined_guard) + return; + + // if this hazard is currently in the union of + // threadLocalRetiredLists and pointerToRetire, but not yet in + // threadLocalRetiredListsTemp, add it to that list + bool contained_in_union = false; + + // first iterate over our retired list + for (unsigned int i = 0; i != retired_list_size; ++i) { + // when reaching 0, we can stop iterating (end of the "list") + if (retired_list[i] == 0) + break; + + // the hazard is contained in the retired list... it shall go + // into the temp list, if not already there + if (retired_list[i] == considered_hazard) { + contained_in_union = true; + break; + } + } + + // the union also contains pointerToRetire + if (!contained_in_union) { + contained_in_union = (considered_hazard == guarded_element); + } + + // add the pointer to temp. retired list, if not already there + if (contained_in_union) { + for (unsigned int ii = 0; ii != retired_list_size; ++ii) { + // is it already there? + if (updated_retired_list[ii] == considered_hazard) + break; + + // end of the list + if (updated_retired_list[ii] == undefined_guard) { + // add hazard + updated_retired_list[ii] = considered_hazard; + + // we are done here... + break; + } + } + } + } + + template< typename GuardType > + void HazardPointer< GuardType >::EnqueueForDeletion(GuardType toRetire) { + unsigned int my_thread_id = GetObjectLocalThreadIndex(); + + // check for invariant + assert(my_thread_id < max_accessors_count_); + + const unsigned int retired_list_size = max_accessors_count_ * + max_guards_per_thread_; + + const unsigned int count_guards = max_accessors_count_ * + max_guards_per_thread_; + + GuardType* retired_list = + &thread_local_retired_lists_[my_thread_id * retired_list_size]; + + GuardType* retired_list_temp = + &thread_local_retired_lists_temp_[my_thread_id * retired_list_size]; + + // wipe my temp. retired list... + for (unsigned int i = 0; i < retired_list_size; ++i) { + // the list is filled always from left to right, so occurring the first + // undefinedGuard, the remaining ones are also undefinedGuard... + if (retired_list_temp[i] == undefined_guard_) + break; + + retired_list_temp[i] = undefined_guard_; + } + + // we test each hazard if it is in the union of retiredList and + // guardedElement. If it is, it goes into the new retired list... + for (unsigned int i = 0; i != count_guards; ++i) { + // consider each current active guard + GuardType considered_hazard = guards_[i].Load(); + UpdateRetiredList(retired_list, retired_list_temp, retired_list_size, + toRetire, considered_hazard, undefined_guard_); + } + + int retired_list_size_signed = static_cast(retired_list_size); + assert(retired_list_size_signed >= 0); + + // now we created a a new retired list... the elements that are "removed" + // from the old retired list can be safely deleted now... + for (int i = -1; i != retired_list_size_signed; ++i) { + // we iterate over the current retired list... -1 is used as dummy element + // in the iteration, to also iterate over the pointerToRetire, which is + // logically also part of the current retired list... + + // end of the list, stop iterating + if (i >= 0 && retired_list[i] == undefined_guard_) + break; + + GuardType to_check_if_in_new_list = undefined_guard_; + + to_check_if_in_new_list = (i == -1 ? toRetire : retired_list[i]); + + // still in the new retired list? + bool still_in_list = false; + for (unsigned int ii = 0; ii != retired_list_size; ++ii) { + // end of list + if (retired_list_temp[ii] == undefined_guard_) + break; + + if (to_check_if_in_new_list == retired_list_temp[ii]) { + // still in list, cannot delete element! + still_in_list = true; + break; + } + } + + if (!still_in_list) { + this->release_object_callback_(to_check_if_in_new_list); + } + } + + // copy the updated retired list (temp) to the retired list... + CopyRetiredList(retired_list_temp, retired_list, retired_list_size, + undefined_guard_); + } +} // namespace internal +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ diff --git b/containers_cpp/include/embb/containers/internal/hazard_pointer.h a/containers_cpp/include/embb/containers/internal/hazard_pointer.h new file mode 100644 index 0000000..25570dd --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/hazard_pointer.h @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ +#define EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ + +#include +#include +#include +#include +#include +#include + +#if defined(EMBB_PLATFORM_COMPILER_MSVC) +#define EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME +#else +#define EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME typename +#endif + +// forward declaration for white-box test, used in friend declaration of +// HazardPointer class. +namespace embb { +namespace containers { +namespace test { +class HazardPointerTest2; +} +} +} + +namespace embb { +namespace containers { +namespace internal { +/** + * This class contains a hazard pointer implementation following publication: + * + * Maged M. Michael. "Hazard pointers: Safe memory reclamation for lock-free + * objects." IEEE Transactions on Parallel and Distributed Systems, 15.6 (2004) + * : 491-504. + * + * Hazard pointers are a wait-free memory reclamation scheme for lock-free + * algorithms. Loosely speaking, they act as garbage collector. The release of + * objects contained within the memory, managed by the hazard pointer class, is + * intercepted and possibly delayed to avoid concurrency bugs. + * + * Before accessing an object, threads announce their intention to do so (i.e. + * the intention to dereference the respective pointer) to the hazard pointer + * class. This is called guarding. From now on, the hazard pointer class will + * prohibit the release or reuse of the guarded object. This is necessary, to + * assure that the object is not released or reused while it is accessed and to + * assure that it has not unnoticed changed (effectively avoiding the ABA + * problem). + * + * Note that after guarding an object, a consecutive check that the object (i.e. + * its pointer) is still valid is necessary; the object release could already + * have been started when guarding the object. Guarding is repeated, until this + * check eventually succeeds. Note that this "guard-and-check" loop makes the + * usage of the hazard pointer class lock-free, even though its implementation + * is wait-free. + * + * Internally, guarding is realized by providing each thread slots, where + * pointers can be placed that should not be freed (so called guards). When + * trying to release an object, it is checked if the object's pointer is + * guarded, and if so this object is not released, but instead put into a + * retired list for later release, when all guards for this object have been + * removed. + * + * In contrast to the original implementation, our implementation consumes only + * fixed-size memory. Note that the number of threads accessing the hazard + * pointer object accounts quadratic for the memory consumption: managed objects + * are provided from outside and the number of accessors accounts quadric for + * the minimum count of those objects. + * + * Also in contrast to the original implementation, we do not provide a HelpScan + * functionality, which gives threads the possibility, to not participate in the + * garbage collection anymore: other threads will help to clean-up the objects + * protected by the exiting thread. The reason is, that the only use-case would + * be a crashing thread, not participating anymore. However, as the thread has + * to signal its exit himself, this is not possible to realize anyways. In the + * end, it is still guaranteed that all memory is properly returned (in the + * destructor). + * + * Additionally, the original implementation holds a threshold, which determines + * when objects shall be freed. In this implementation, we free whenever it is + * possibly to do so, as we want to keep the memory footprint as low as + * possible. We also don't see a performance drop in the current algorithms that + * are using hazard pointers, when not using a threshold. + * + * \tparam GuardType the type of the guards. Usually the pointer type of some + * object to protect. + */ +template< typename GuardType > +class HazardPointer { + public: + /** + * The user of the hazard pointer class has to provide the memory that is + * managed here. The user has to take into account, that releasing of memory + * might be delayed. He has therefore to provide more memory than he wants to + * guarantee at each point in time. More specific, on top of the guaranteed + * count of objects, he has to provide the additional count of objects that + * can be (worst-case) contained in the retired lists and therefore are not + * released yet. The size sum of all retired lists is guardsPerThread * + * accessorCount * accessorCount, which is computed using this function. So + * the result of function denotes to the user, how many objects he has to + * allocate additionally to the guaranteed count. + * + * \waitfree + */ + static size_t ComputeMaximumRetiredObjectCount( + size_t guardsPerThread, + /**<[IN] the count of guards per thread*/ + int accessors = -1 + /**<[IN] Number of accessors. Determines, how many threads will access + the hazard pointer object. Default value -1 will allow the + maximum amount of threads as defined with + \c embb::base::Thread::GetThreadsMaxCount()*/ + ); + + /** + * Initializes the hazard pointer object + * + * \notthreadsafe + * + * \memory We dynamically allocate the following: + * + * (sizeof(Atomic) * accessors) + (sizeof(Atomic) * + * guards_per_thread * accessors) + (2*sizeof(GuardType) * + * guards_per_thread * accessors^2) + * + * The last addend is the dominant one, as accessorCount accounts + * quadratically for it. + */ + HazardPointer( + embb::base::Function free_guard_callback, + /**<[IN] Callback to the function that shall be called when a retired + guard can be deleted */ + GuardType undefined_guard, + /**<[IN] The guard value denoting "not guarded"*/ + int guards_per_thread, + /**<[IN] Number of guards per thread*/ + int accessors = -1 + /**<[IN] Number of accessors. Determines, how many threads will access + this hazard pointer object. Default value -1 will allow the + maximum amount of threads as defined with + \c embb::base::Thread::GetThreadsMaxCount()*/ + ); + + /** + * Deallocates internal data structures. Additionally releases all objects + * currently held in the retired lists, using the release functor passed in + * the constructor. + * + * \notthreadsafe + */ + ~HazardPointer(); + + /** + * Guards \c to_guard. If the guarded_element is passed to \c EnqueueForDeletion + * it is prevented from release from now on. The user must have a check, that + * EnqueueForDeletion has not been called on to_guard, before the guarding took + * effect. + * + * \waitfree + */ + void Guard( + int guard_position, + /**<[IN] position to place guard*/ + GuardType to_guard + /**<[IN] element to guard*/ + ); + + /** + * Enqueue guarded element for deletion. If not guarded, it is deleted + * immediately. If it is guarded, it is added to a thread local retired list, + * and deleted in a subsequent call to \c EnqueueForDeletion, when no guard is + * placed on it anymore. + */ + void EnqueueForDeletion( + GuardType guarded_element + /**<[IN] element to logically delete*/ + ); + + /** + * Explicitly remove guard from thread local slot. + * + * \waitfree + */ + void RemoveGuard(int guard_position); + + private: + /** + * HazardPointerTest2 is a white-box test, needing access to private members + * of this class. So declaring it as friend. + */ + friend class embb::containers::test::HazardPointerTest2; + + /** + * This number determines the amount of maximal accessors (threads) that + * will access this hazard pointer instance. Note that a thread once + * accessing this object will be permanently count as accessor, even if not + * participating anymore. If too many threads access this object, an + * exception is thrown. + */ + unsigned int max_accessors_count_; + + /** + * The guard value denoting "not guarded" + */ + GuardType undefined_guard_; + + /** + * The maximal count of guards that can be set per thread. + */ + int max_guards_per_thread_; + + /** + * The functor that is called to release an object. This is called by this + * class, when it is safe to do so, i.e., no thread accesses this object + * anymore. + */ + embb::base::Function release_object_callback_; + + /** + * Mapping from EMBB thread id to hazard pointer thread ids. Hazard pointer + * thread ids are in range [0;accesor_count-1]. The position of a EMBB thread + * id in that array determines the respective hazard pointer thread id. + */ + embb::base::Atomic* thread_id_mapping_; + + /** + * The hazard pointer guards, represented as array. Each thread has a fixed + * set of slots (guardsPerThread) within this array. + */ + embb::base::Atomic* guards_; + + /** + * \see threadLocalRetiredLists documentation + */ + GuardType* thread_local_retired_lists_temp_; + + /** + * A list of lists, represented as single array. Each thread maintains a list + * of retired pointers, that are objects that are logically released but not + * released because some thread placed a guard on it. + */ + GuardType* thread_local_retired_lists_; + + /** + * Each thread is assigned a thread index (starting with 0). Get the index of + * the current thread. Note that this is not the global index, but an hazard + * pointer class internal one. The user is free to define less accessors than + * the amount of default threads. This is useful, as the number of accessors + * accounts quadratic for the memory consumption, so the user should have the + * possibility to avoid memory wastage when only having a small, fixed size, + * number of accessors. + * + * @return current (hazard pointer object local) thread index + */ + unsigned int GetObjectLocalThreadIndex(); + + /** + * Copy retired list \c sourceList to retired list \c targetList + */ + static void CopyRetiredList( + GuardType* source_list, + /**<[IN] the source retired list*/ + GuardType* target_list, + /**<[IN] the target retired list*/ + unsigned int single_retired_list_size, + /**<[IN] the size of a thread local retired list*/ + GuardType undefined_guard + /**<[IN] the undefined guard (usually the NULL pointer)*/ + ); + + static void UpdateRetiredList( + GuardType* retired_list, + /**<[IN] the old retired list*/ + GuardType* updated_retired_list, + /**<[IN] the updated retired list*/ + unsigned int retired_list_size, + /**<[IN] the size of a thread local retired list*/ + GuardType to_retire, + /**<[IN] the element to retire*/ + GuardType considered_hazard, + /**<[IN] the currently considered hazard*/ + GuardType undefined_guard + /**<[IN] the undefined guard (usually the NULL pointer)*/ + ); +}; +} // namespace internal +} // namespace containers +} // namespace embb + +#include "./hazard_pointer-inl.h" + +#endif // EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ diff --git b/containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h a/containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h new file mode 100644 index 0000000..a3defd6 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_ + +#include + +/* + * The following algorithm uses hazard pointers and a lock-free value pool for + * memory management. For a description of the algorithm, see + * Maged M. Michael and Michael L. Scott. "Simple, fast, and practical + * non-blocking and blocking concurrent queue algorithms". Proceedings of the + * fifteenth annual ACM symposium on principles of distributed computing. + * ACM, 1996. (Figure 1, Page 4) + */ + +namespace embb { +namespace containers { +namespace internal { +template< typename T > +LockFreeMPMCQueueNode::LockFreeMPMCQueueNode() : +next(NULL) { +} + +template< typename T > +LockFreeMPMCQueueNode::LockFreeMPMCQueueNode(T const& element) : + next(NULL), + element(element) { +} + +template< typename T > +embb::base::Atomic< LockFreeMPMCQueueNode< T >* > & + LockFreeMPMCQueueNode::GetNext() { + return next; +} + +template< typename T > +T LockFreeMPMCQueueNode::GetElement() { + return element; +} +} // namespace internal + +template< typename Type, typename ValuePool > +void LockFreeMPMCQueue:: +DeletePointerCallback(internal::LockFreeMPMCQueueNode* to_delete) { + objectPool.Free(to_delete); +} + +template< typename Type, typename ValuePool > +LockFreeMPMCQueue::~LockFreeMPMCQueue() { + // Nothing to do here, did not allocate anything. +} + +template< typename Type, typename ValuePool > +LockFreeMPMCQueue::LockFreeMPMCQueue(size_t capacity) : + capacity(capacity), + // Object pool, size with respect to the maximum number of retired nodes not + // eligible for reuse. +1 for dummy node. + objectPool( + MPMCQueueNodeHazardPointer_t::ComputeMaximumRetiredObjectCount(2) + + capacity + 1), +// Disable "this is used in base member initializer" warning. +// We explicitly want this. +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4355) +#endif +delete_pointer_callback(*this, + &LockFreeMPMCQueue::DeletePointerCallback), +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + hazardPointer(delete_pointer_callback, NULL, 2) { + // Allocate dummy node to reduce the number of special cases to consider. + internal::LockFreeMPMCQueueNode* dummyNode = objectPool.Allocate(); + // Initially, head and tail point to the dummy node. + head = dummyNode; + tail = dummyNode; +} + +template< typename Type, typename ValuePool > +size_t LockFreeMPMCQueue::GetCapacity() { + return capacity; +} + +template< typename Type, typename ValuePool > +bool LockFreeMPMCQueue::TryEnqueue(Type const& element) { + // Get node from the pool containing element to enqueue. + internal::LockFreeMPMCQueueNode* node = objectPool.Allocate(element); + + // Queue full, cannot enqueue + if (node == NULL) + return false; + internal::LockFreeMPMCQueueNode* my_tail; + for (;;) { + my_tail = tail; + + hazardPointer.Guard(0, my_tail); + + // Check if pointer is still valid after guarding. + if (my_tail != tail) { + continue; // Hazard pointer outdated, retry + } + + internal::LockFreeMPMCQueueNode* my_tail_next = my_tail->GetNext(); + + if (my_tail == tail) { + // If the next pointer of the tail node is null, the tail pointer + // points to the last object. We try to set the next pointer of the + // tail node to our new node. + if (my_tail_next == NULL) { + internal::LockFreeMPMCQueueNode* expected = NULL; + // This fails if the next pointer of the "cached" tail is not null + // anymore, i.e., another thread added a node before we could complete. + if (my_tail->GetNext().CompareAndSwap(expected, node)) + break; // We successfully added our node. + //Still missing: increase tail pointer. + // The tail pointer points not to the last object, first increase + } else { + // Try to increase the tail pointer. + tail.CompareAndSwap(my_tail, my_tail_next); + } + } + } + // We added our node. Try to update tail pointer. Need not succeed, if we + // fail, another thread will help us. + tail.CompareAndSwap(my_tail, node); + + return true; +} + +template< typename Type, typename ValuePool > +bool LockFreeMPMCQueue::TryDequeue(Type & element) { + internal::LockFreeMPMCQueueNode* my_head; + internal::LockFreeMPMCQueueNode* my_tail; + internal::LockFreeMPMCQueueNode* my_next; + internal::LockFreeMPMCQueueNode* expected; + Type data; + for (;;) { + my_head = head; + hazardPointer.Guard(0, my_head); + if (my_head != head) continue; + + my_tail = tail; + my_next = my_head->GetNext(); + hazardPointer.Guard(1, my_next); + if (head != my_head) continue; + + if (my_next == NULL) + return false; + + if (my_head == my_tail) { + expected = my_tail; + tail.CompareAndSwap(expected, my_next); + continue; + } + + data = my_next->GetElement(); + + expected = my_head; + if (head.CompareAndSwap(expected, my_next)) + break; + } + + hazardPointer.EnqueueForDeletion(my_head); + element = data; + return true; +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_ diff --git b/containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h a/containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h new file mode 100644 index 0000000..3324c67 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_ + +#include + +/* + * The following algorithm uses hazard pointers and a lock-free value pool for + * memory management. For a description of the algorithm, see + * Maged M. Michael. "Hazard pointers: Safe memory reclamation for lock-free + * objects". IEEE Transactions on Parallel and Distributed Systems, 15.6 (2004): + * 491-504. + */ + +namespace embb { +namespace containers { +namespace internal { + template< typename T > + LockFreeStackNode< T >::LockFreeStackNode(T const& element) : + element(element) { + } + + template< typename T > + void LockFreeStackNode< T >::SetNext(LockFreeStackNode< T >* next_to_set) { + this->next = next_to_set; + } + + template< typename T > + LockFreeStackNode< T >* LockFreeStackNode< T >::GetNext() { + return next; + } + + template< typename T > + T LockFreeStackNode< T >::GetElement() { + return element; + } +} // namespace internal + +template< typename Type, typename ValuePool > +void LockFreeStack< Type, ValuePool >:: +DeletePointerCallback(internal::LockFreeStackNode* to_delete) { + objectPool.Free(to_delete); +} + +template< typename Type, typename ValuePool > +LockFreeStack< Type, ValuePool >::LockFreeStack(size_t capacity) : +capacity(capacity), +// Disable "this is used in base member initializer" warning. +// We explicitly want this. +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4355) +#endif + delete_pointer_callback(*this, + &LockFreeStack::DeletePointerCallback), +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + // Object pool, size with respect to the maximum number of retired nodes not + // eligible for reuse: + objectPool( + StackNodeHazardPointer_t::ComputeMaximumRetiredObjectCount(1) + + capacity), + hazardPointer(delete_pointer_callback, NULL, 1) { +} + +template< typename Type, typename ValuePool > +size_t LockFreeStack< Type, ValuePool >::GetCapacity() { + return capacity; +} + +template< typename Type, typename ValuePool > +LockFreeStack< Type, ValuePool >::~LockFreeStack() { + // Nothing to do here, did not allocate anything. +} + +template< typename Type, typename ValuePool > +bool LockFreeStack< Type, ValuePool >::TryPush(Type const& element) { + internal::LockFreeStackNode* newNode = + objectPool.Allocate(element); + + // Stack full, cannot push + if (newNode == NULL) + return false; + + for (;;) { + internal::LockFreeStackNode* top_cached = top; + newNode->SetNext(top_cached); + if (top.CompareAndSwap(top_cached, newNode)) + return true; + } +} + +template< typename Type, typename ValuePool > +bool LockFreeStack< Type, ValuePool >::TryPop(Type & element) { + internal::LockFreeStackNode* top_cached = top; + for (;;) { + top_cached = top; + + // Stack empty, cannot pop + if (top_cached == NULL) { + element = Type(); + return false; + } + + // Guard top_cached + hazardPointer.Guard(0, top_cached); + + // Check if top is still top. If this is the case, it has not been + // retired yet (because before retiring that thing, the retiring thread + // would first have to update the top pointer). If not, we wait for the + // next round. + if (top != top_cached) + continue; + + bool compare_and_swap_suc = top.CompareAndSwap(top_cached, + top_cached->GetNext()); + + if (compare_and_swap_suc) { + break; + } else { + // We continue with the next and can unguard top_cached + hazardPointer.Guard(0, NULL); + } + } + + Type data = top_cached->GetElement(); + + // We don't need to read from this reference anymore, unguard it + hazardPointer.Guard(0, NULL); + + hazardPointer.EnqueueForDeletion(top_cached); + + element = data; + return true; +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_ diff --git b/containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h a/containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h new file mode 100644 index 0000000..8bf1754 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_LOCK_FREE_TREE_VALUE_POOL_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_TREE_VALUE_POOL_INL_H_ + +namespace embb { +namespace containers { +template +int LockFreeTreeValuePool:: +GetSmallestPowerByTwoValue(int value) { + int result = 1; + while (result < value) result <<= 1; + return result; +} + +template +bool LockFreeTreeValuePool:: +IsLeaf(int node) { + if (node >= size_ - 1 && node <= 2 * size_ - 1) { + return true; + } + return false; +} + +template +bool LockFreeTreeValuePool:: +IsValid(int node) { + return (node >= 0 && node <= 2 * size_ - 1); +} + +template +int LockFreeTreeValuePool:: +GetLeftChildIndex(int node) { + int index = 2 * node + 1; + assert(IsValid(index)); + return index; +} + +template +int LockFreeTreeValuePool:: +GetRightChildIndex(int node) { + int index = 2 * node + 2; + assert(IsValid(index)); + return index; +} + +template +int LockFreeTreeValuePool:: +NodeIndexToPoolIndex(int node) { + assert(IsLeaf(node)); + return(node - (size_ - 1)); +} + +template +int LockFreeTreeValuePool:: +PoolIndexToNodeIndex(int index) { + int node = index + (size_ - 1); + assert(IsLeaf(node)); + return node; +} + +template +bool LockFreeTreeValuePool:: +IsRoot(int node) { + return(0 == node); +} + +template +int LockFreeTreeValuePool:: +GetParentNode(int node) { + int parent = (node - 1) / 2; + assert(parent >= 0 && parent < size_ - 1); + return parent; +} + +template +int LockFreeTreeValuePool:: +allocate_rec(int node, Type& element) { + // If we are a leaf, we try to allocate a cell using CAS. + if (IsLeaf(node)) { + int pool_index = NodeIndexToPoolIndex(node); + + Type expected = pool_[pool_index]; + if (expected == Undefined) { + element = Type(); + return -1; + } + + if (pool_[pool_index].CompareAndSwap(expected, Undefined)) { + element = expected; + return pool_index; + } + + element = Type(); + return -1; + } + + int current; + int desired; + // Try to decrement node value. + // This is the point, where the algorithm becomes not wait-free. We have to + // atomically decrement the value in the node if the result is greater than + // or equal to zero. This cannot be done atomically. + do { + current = tree_[node]; + desired = current - 1; + if (desired < 0) { + element = Type(); + return -1; + } + } while (!tree_[node].CompareAndSwap(current, desired)); + + int leftResult = allocate_rec(GetLeftChildIndex(node), element); + if (leftResult != -1) { + return leftResult; + } + int rightResult = (allocate_rec(GetRightChildIndex(node), element)); + // We are guaranteed to be successful either in the left or the right branch. + // It should not happen that we cannot allocate in either branch. + assert(rightResult != -1); + + return rightResult; +} + +template +void LockFreeTreeValuePool:: +Fill(int node, int elementsToStore, int power2Value) { + if (IsLeaf(node)) + return; + + tree_[node] = elementsToStore; + + int postPower2Value = power2Value >> 1; + + // Value fits in left cell, don't bother with right cells + if (elementsToStore <= postPower2Value) { + Fill(GetLeftChildIndex(node), elementsToStore, power2Value >> 1); + } else { + Fill(GetLeftChildIndex(node), + postPower2Value, + postPower2Value); + + Fill(GetRightChildIndex(node), + elementsToStore - postPower2Value, + postPower2Value); + } +} + +template +int LockFreeTreeValuePool:: +Allocate(Type & element) { + return allocate_rec(0, element); +} + +template +void LockFreeTreeValuePool:: +Free(Type element, int index) { + assert(element != Undefined); + + // Put the element back + pool_[index].Store(element); + + assert(index >= 0 && index < size_); + int node = PoolIndexToNodeIndex(index); + + while (!IsRoot(node)) { + node = GetParentNode(node); + tree_[node].FetchAndAdd(1); + } +} + +template< typename Type, Type Undefined, class PoolAllocator, + class TreeAllocator > +template< typename ForwardIterator > +LockFreeTreeValuePool:: +LockFreeTreeValuePool(ForwardIterator first, ForwardIterator last) { + // Number of elements to store + real_size_ = static_cast(::std::distance(first, last)); + + // Let k be smallest number so that real_size <= 2^k, size = 2^k + size_ = GetSmallestPowerByTwoValue(real_size_); + + // Size of binary tree without the leaves + tree_size_ = size_ - 1; + + // make sure, signed values are not negative + assert(tree_size_ >= 0); + assert(real_size_ >= 0); + + size_t tree_size_unsigned = static_cast(tree_size_); + size_t real_size_unsigned = static_cast(real_size_); + + // Pool stores elements of type T + pool_ = pool_allocator_.allocate(real_size_unsigned); + + // invoke inplace new for each pool element + for (size_t i = 0; i != real_size_unsigned; ++i) { + new (&pool_[i]) embb::base::Atomic(); + } + + // Tree holds the counter of not allocated elements + tree_ = tree_allocator_.allocate(tree_size_unsigned); + + // invoke inplace new for each tree element + for (size_t i = 0; i != tree_size_unsigned; ++i) { + new (&tree_[i]) embb::base::Atomic(); + } + + int i = 0; + + // Store the elements from the range + for (ForwardIterator curIter(first); curIter != last; ++curIter) { + pool_[i++] = *curIter; + } + + // Initialize the binary tree without leaves (counters) + Fill(0, static_cast(::std::distance(first, last)), size_); +} + +template +LockFreeTreeValuePool:: +~LockFreeTreeValuePool() { + size_t tree_size_unsigned = static_cast(tree_size_); + size_t real_size_unsigned = static_cast(real_size_); + + // invoke destructor for each pool element + for (size_t i = 0; i != real_size_unsigned; ++i) { + pool_[i].~Atomic(); + } + + pool_allocator_.deallocate(pool_, real_size_unsigned); + + // invoke destructor for each tree element + for (size_t i = 0; i != tree_size_unsigned; ++i) { + tree_[i].~Atomic(); + } + + tree_allocator_.deallocate(tree_, tree_size_unsigned); +} + +template +size_t LockFreeTreeValuePool:: +GetMinimumElementCountForGuaranteedCapacity(size_t capacity) { + // for this value pool, this is just capacity... + return capacity; +} + +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LOCK_FREE_TREE_VALUE_POOL_INL_H_ diff --git b/containers_cpp/include/embb/containers/internal/object_pool-inl.h a/containers_cpp/include/embb/containers/internal/object_pool-inl.h new file mode 100644 index 0000000..3663c29 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/object_pool-inl.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_OBJECT_POOL_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_OBJECT_POOL_INL_H_ + +namespace embb { +namespace containers { +template +ObjectPool:: +ReturningTrueIterator::ReturningTrueIterator(size_t count_value) : +count_value(count_value), + ret_value(true) +{} + +template +typename ObjectPool:: +ReturningTrueIterator::self_type +ObjectPool:: +ReturningTrueIterator::operator++() { + self_type i = *this; + count_value++; + return i; +} + +template +typename ObjectPool:: +ReturningTrueIterator::self_type ObjectPool:: +ReturningTrueIterator::operator++(int) { + count_value++; + return *this; +} + +template +typename ObjectPool:: +ReturningTrueIterator::reference ObjectPool:: +ReturningTrueIterator::operator*() { + return ret_value; +} + +template +typename ObjectPool:: +ReturningTrueIterator::pointer ObjectPool:: +ReturningTrueIterator::operator->() { + return &ret_value; +} + +template +bool ObjectPool:: +ReturningTrueIterator::operator==(const self_type& rhs) { + return count_value == rhs.count_value; +} + +template +bool ObjectPool:: +ReturningTrueIterator::operator!=(const self_type& rhs) { + return count_value != rhs.count_value; +} + +template +bool ObjectPool:: +IsContained(const Type &obj) const { + if ((&obj < &objects_array_[0]) || + (&obj > &objects_array_[value_pool_size_ - 1])) { + return false; + } else { + return true; + } +} + +template +int ObjectPool:: +GetIndexOfObject(const Type &obj) const { + assert(IsContained(obj)); + return(static_cast(&obj - &objects_array_[0])); +} + +template +Type* ObjectPool::AllocateRaw() { + bool val; + int allocated_index = value_pool_.Allocate(val); + if (allocated_index == -1) { + return NULL; + } else { + Type* ret_pointer = &(objects_array_[allocated_index]); + + return ret_pointer; + } +} + +template +size_t ObjectPool::GetCapacity() { + return capacity_; +} + +template +ObjectPool::ObjectPool(size_t capacity) : + capacity_(capacity), + value_pool_size_( + ValuePool::GetMinimumElementCountForGuaranteedCapacity(capacity)), + value_pool_(ReturningTrueIterator(0), ReturningTrueIterator( + value_pool_size_)), + objects_array_(object_allocator_.allocate(value_pool_size_)) { +} + +template +void ObjectPool::Free(Type* obj) { + int index = GetIndexOfObject(*obj); + obj->~Type(); + + value_pool_.Free(true, index); +} + +template +Type* ObjectPool::Allocate() { + Type* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)Type(); + + return rawObject; +} + +template +template +Type* ObjectPool::Allocate( + Param1 const& param1) { + Type* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)Type(param1); + + return rawObject; +} + +template +template +Type* ObjectPool::Allocate( + Param1 const& param1, Param2 const& param2) { + Type* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)Type(param1, param2); + + return rawObject; +} + +template +template +Type* ObjectPool::Allocate( + Param1 const& param1, Param2 const& param2, + Param3 const& param3) { + Type* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)Type(param1, param2, param3); + + return rawObject; +} + +template +template +Type* ObjectPool::Allocate( + Param1 const& param1, Param2 const& param2, + Param3 const& param3, Param4 const& param4) { + Type* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)Type(param1, param2, param3, param4); + + return rawObject; +} + +template +ObjectPool::~ObjectPool() { + // Deallocate the objects + object_allocator_.deallocate(objects_array_, value_pool_size_); +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_OBJECT_POOL_INL_H_ diff --git b/containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h a/containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h new file mode 100644 index 0000000..535e6e5 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_WAIT_FREE_ARRAY_VALUE_POOL_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_WAIT_FREE_ARRAY_VALUE_POOL_INL_H_ + +namespace embb { +namespace containers { +template +void WaitFreeArrayValuePool:: +Free(Type element, int index) { + assert(element != Undefined); + + // Just put back the element + pool_array_[index].Store(element); +} + +template +int WaitFreeArrayValuePool:: +Allocate(Type & element) { + for (int i = 0; i != size_; ++i) { + Type expected; + + // If the memory cell is not available, go ahead + if (Undefined == (expected = pool_array_[i].Load())) + continue; + + // Try to get the memory cell + if (pool_array_[i].CompareAndSwap(expected, Undefined)) { + // When the CAS was successful, this element is ours + element = expected; + return i; + } + } + element = Type(); + return -1; +} + +template +template +WaitFreeArrayValuePool:: +WaitFreeArrayValuePool(ForwardIterator first, ForwardIterator last) { + size_t dist = static_cast(std::distance(first, last)); + + size_ = static_cast(dist); + + // conversion may result in negative number. check! + assert(size_ >= 0); + + // Use the allocator to allocate an array of size dist + pool_array_ = allocator_.allocate(dist); + + // invoke inplace new for each pool element + for ( size_t i = 0; i != dist; ++i ) { + new (&pool_array_[i]) embb::base::Atomic(); + } + + int i = 0; + + // Store the elements of the range + for (ForwardIterator curIter(first); curIter != last; ++curIter) { + pool_array_[i++] = *curIter; + } +} + +template +WaitFreeArrayValuePool::~WaitFreeArrayValuePool() { + // invoke destructor for each pool element + for (int i = 0; i != size_; ++i) { + pool_array_[i].~Atomic(); + } + + // free memory + allocator_.deallocate(pool_array_, static_cast(size_)); +} + +template +size_t WaitFreeArrayValuePool:: +GetMinimumElementCountForGuaranteedCapacity(size_t capacity) { + // for this value pool, this is just capacity... + return capacity; +} + +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_WAIT_FREE_ARRAY_VALUE_POOL_INL_H_ diff --git b/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h a/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h new file mode 100644 index 0000000..cc605d4 --- /dev/null +++ a/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_INTERNAL_WAIT_FREE_SPSC_QUEUE_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_WAIT_FREE_SPSC_QUEUE_INL_H_ + +/* + * The following algorithm is described in: + * Maurice Herlihy and Nir Shavit. "The Art of Multiprocessor Programming." + * Page 46. Morgan Kaufmann, 2008. (original: L. Lamport. "Specifying concurrent + * programs"). + */ + +namespace embb { +namespace containers { +template +size_t WaitFreeSPSCQueue:: +AlignCapacityToPowerOfTwo(size_t capacity) { + size_t result = 1; + while (result < capacity) result <<= 1; + return result; +} + +template +WaitFreeSPSCQueue::WaitFreeSPSCQueue(size_t capacity) + : capacity(AlignCapacityToPowerOfTwo(capacity)), + head_index(0), + tail_index(0) { + queue_array = allocator.allocate(this->capacity); +} + +template +size_t WaitFreeSPSCQueue::GetCapacity() { + return capacity; +} + +template +bool WaitFreeSPSCQueue::TryEnqueue(Type const & element) { + if (tail_index - head_index == capacity) + return false; + + queue_array[tail_index % capacity] = element; + this->tail_index++; + return true; +} + +template +bool WaitFreeSPSCQueue::TryDequeue(Type & element) { + if (tail_index - head_index == 0) + return false; + + Type x = queue_array[head_index % capacity]; + this->head_index++; + element = x; + return true; +} + +template +WaitFreeSPSCQueue::~WaitFreeSPSCQueue() { + allocator.deallocate(queue_array, capacity); +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_WAIT_FREE_SPSC_QUEUE_INL_H_ diff --git b/containers_cpp/include/embb/containers/lock_free_mpmc_queue.h a/containers_cpp/include/embb/containers/lock_free_mpmc_queue.h new file mode 100644 index 0000000..93c07fb --- /dev/null +++ a/containers_cpp/include/embb/containers/lock_free_mpmc_queue.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_LOCK_FREE_MPMC_QUEUE_H_ +#define EMBB_CONTAINERS_LOCK_FREE_MPMC_QUEUE_H_ + +#include +#include + +#include +#include +#include + +#include +#include + +namespace embb { +namespace containers { +namespace internal { +/** + * Queue node + * + * Single linked lists, contains the element (\c element) and a pointer to the + * next node (\c next). + * + * \tparam Type Element type + */ + template< typename Type > +class LockFreeMPMCQueueNode { + private: + /** + * Pointer to the next node + */ + embb::base::Atomic< LockFreeMPMCQueueNode< Type >* > next; + + /** + * The stored element + */ + Type element; + + public: + /** + * Creates a queue node + * + * Explicitly allow uninitialized \c element, used for dummy node + */ + LockFreeMPMCQueueNode(); + + /** + * Creates a queue node + */ + LockFreeMPMCQueueNode( + Type const& element + /**< [IN] The element of this queue node */); + + /** + * Returns the next pointer + * + * \return The next pointer + */ + embb::base::Atomic< LockFreeMPMCQueueNode< Type >* > & GetNext(); + + /** + * Returns the element held by this node + */ + Type GetElement(); +}; +} // namespace internal + +/** + * Lock-free queue for multiple producers and multiple consumers + * + * \concept{CPP_CONCEPTS_QUEUE} + * + * \ingroup CPP_CONTAINERS_QUEUES + * + * \see WaitFreeSPSCQueue + * + * \tparam Type Type of the queue elements + * \tparam ValuePool Type of the value pool used as basis for the ObjectPool + * which stores the elements. + */ +template< typename Type, + typename ValuePool = embb::containers::LockFreeTreeValuePool < bool, false > +> +class LockFreeMPMCQueue { + private: + /** + * The capacity of the queue. It is guaranteed that the queue can hold at + * least as many elements, maybe more. + */ + size_t capacity; + + /** + * The object pool, used for lock-free memory allocation. + * + * Warning: the objectPool has to be initialized before the hazardPointer + * object, to be sure that the hazardPointer object is destructed before the + * Pool as the hazardPointer object might return elements to the pool in its + * destructor. So the ordering of the members objectPool and hazardPointer is + * important here! + */ + ObjectPool< internal::LockFreeMPMCQueueNode, ValuePool > objectPool; + + /** + * Callback to the method that is called by hazard pointers if a pointer is + * not hazardous anymore, i.e., can safely be reused. + */ + embb::base::Function < void, internal::LockFreeMPMCQueueNode* > + delete_pointer_callback; + + /** + * Definition of the used hazard pointer type + */ + typedef embb::containers::internal::HazardPointer + < internal::LockFreeMPMCQueueNode* > + MPMCQueueNodeHazardPointer_t; + + /** + * The hazard pointer object, used for memory management. + */ + MPMCQueueNodeHazardPointer_t hazardPointer; + + + /** + * Atomic pointer to the head node of the queue + */ + embb::base::Atomic< internal::LockFreeMPMCQueueNode* > head; + + /** + * Atomic pointer to the tail node of the queue + */ + embb::base::Atomic< internal::LockFreeMPMCQueueNode* > tail; + + /** + * The callback function, used to cleanup non-hazardous pointers. + * \see delete_pointer_callback + */ + void DeletePointerCallback(internal::LockFreeMPMCQueueNode* to_delete); + + public: + /** + * Creates a queue with the specified capacity. + * + * \memory + * Let \c t be the maximum number of threads and \c x be 2.5*t+1. + * Then, x*(3*t+1) elements of size sizeof(void*), \c x + * elements of size sizeof(Type), and \c capacity+1 elements of size + * sizeof(Type) are allocated. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_QUEUE + */ + LockFreeMPMCQueue( + size_t capacity + /**< [IN] Capacity of the queue */); + + /** + * Destroys the queue. + * + * \notthreadsafe + */ + ~LockFreeMPMCQueue(); + + /** + * Returns the capacity of the queue. + * + * \return Number of elements the queue can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Tries to enqueue an element into the queue. + * + * \return \c true if the element could be enqueued, \c false if the queue is + * full. + * + * \lockfree + * + * \note It might be possible to enqueue more elements into the queue than its + * capacity permits. + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryEnqueue( + Type const& element + /**< [IN] Const reference to the element that shall be enqueued */); + + /** + * Tries to dequeue an element from the queue. + * + * \return \c true if an element could be dequeued, \c false if the queue is + * empty. + * + * \lockfree + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryDequeue( + Type & element + /**< [IN, OUT] Reference to the dequeued element. + Unchanged, if the operation + was not successful. */); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_LOCK_FREE_MPMC_QUEUE_H_ diff --git b/containers_cpp/include/embb/containers/lock_free_stack.h a/containers_cpp/include/embb/containers/lock_free_stack.h new file mode 100644 index 0000000..ebe2079 --- /dev/null +++ a/containers_cpp/include/embb/containers/lock_free_stack.h @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_LOCK_FREE_STACK_H_ +#define EMBB_CONTAINERS_LOCK_FREE_STACK_H_ + +#include +#include +#include +#include +#include + +/** + * \defgroup CPP_CONCEPTS_STACK Stack Concept + * Concept for thread-safe stacks + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * A stack is an abstract data type holding a collection of elements of some + * predetermined type. A stack provides two operations: \c TryPush and + * \c TryPop. \c TryPush tries to add an element to the collection, and + * \c TryPop tries to remove an element from the collection. A stack has LIFO + * (Last-In, First-out) semantics, i.e., the last element added to the + * collection (\c TryPush) is removed first (\c TryPop). The capacity \c cap of + * a stack defines the number of elements it can store (depending on the + * implementation, a stack might store more than \c cap elements, since for + * thread-safe memory management, more memory than necessary for holding \c cap + * elements has to be provided). + * + * \par Requirements + * - Let \c Stack be the stack class + * - Let \c Type be the element type of the stack + * - Let \c capacity be a value of type \c size_t + * - Let \c element be a reference to an element of type \c Type + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
\code{.cpp} Stack(capacity) \endcodeNothing + * Constructs a stack with capacity \c capacity that holds elements of + * type \c Type. + *
\code{.cpp} TryPush(element) \endcode\code{.cpp} bool \endcode + * Tries to push \c element onto the stack. Returns \c false if the stack + * is full, otherwise \c true. + *
\code{.cpp} TryPop(element) \endcode\code{.cpp} bool \endcode + * Tries to pop an element from the stack. Returns \c false if the stack is + * empty, otherwise \c true. In the latter case, the popped element is + * stored in \c element which must be passed by reference. + *
+ * + * \} + */ + +/** + * \defgroup CPP_CONTAINERS_STACKS Stacks + * Concurrent stacks + * + * \ingroup CPP_CONTAINERS + * + * \see CPP_CONCEPTS_STACK + */ + +namespace embb { +namespace containers { +namespace internal { +/** + * Stack node + * + * Single linked list, contains the element (\c element) and a pointer to the + * next node (\c next). + * + * \tparam T Element type + */ +template< typename T > +class LockFreeStackNode { + private: + /** + * Pointer to the next node + */ + LockFreeStackNode< T >* next; + + /** + * The stored element + */ + T element; + + public: + /** + * Creates a stack node + */ + LockFreeStackNode( + T const& element + /**< [IN] The element of this stack node */ + ); + + /** + * Sets the next node + */ + void SetNext( + LockFreeStackNode< T >* next + /**< [IN] Pointer to the next node */ + ); + + /** + * Returns the next pointer + * + * \return The next pointer + */ + LockFreeStackNode< T >* GetNext(); + + /** + * Returns the element held by this node + */ + T GetElement(); +}; +} // namespace internal + +/** + * Lock-free stack + * + * \concept{CPP_CONCEPTS_STACK} + * + * \ingroup CPP_CONTAINERS_STACKS + * + * \tparam Type Type of the stack elements + * \tparam ValuePool Type of the value pool used as basis for the ObjectPool + * which stores the elements. + */ +template< typename Type, +typename ValuePool = embb::containers::LockFreeTreeValuePool < bool, false > > +class LockFreeStack { + private: + /** + * The capacity of the stack. It is guaranteed that the stack can hold at + * least as many elements, maybe more. + */ + size_t capacity; + + /** + * Callback to the method that is called by hazard pointers if a pointer is + * not hazardous anymore, i.e., can safely be reused. + */ + embb::base::Function*> + delete_pointer_callback; + + /** + * The callback function, used to cleanup non-hazardous pointers. + * \see delete_pointer_callback + */ + void DeletePointerCallback(internal::LockFreeStackNode* to_delete); + + /** + * The object pool, used for lock-free memory allocation. + * + * Warning: the objectPool has to be initialized before the hazardPointer + * object, to be sure that the hazardPointer object is destructed before the + * Pool as the hazardPointer object might return elements to the pool in its + * destructor. So the ordering of the members objectPool and hazardPointer is + * important here! + */ + ObjectPool< internal::LockFreeStackNode, ValuePool > objectPool; + + /** + * Definition of the used hazard pointer type + */ + typedef internal::HazardPointer < internal::LockFreeStackNode* > + StackNodeHazardPointer_t; + + /** + * The hazard pointer object, used for memory management. + */ + StackNodeHazardPointer_t hazardPointer; + + /** + * Atomic pointer to the top node of the stack (element that is popped next) + */ + embb::base::Atomic*> top; + + public: + /** + * Creates a stack with the specified capacity. + * + * \memory + * Let \c t be the maximum number of threads and \c x be 1.25*t+1. + * Then, x*(3*t+1) elements of size sizeof(void*), \c x + * elements of size sizeof(Type), and \c capacity elements of size + * sizeof(Type) are allocated. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_STACK + */ + LockFreeStack( + size_t capacity + /**< [IN] Capacity of the stack */ + ); + + /** + * Returns the capacity of the stack. + * + * \return Number of elements the stack can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Destroys the stack. + * + * \notthreadsafe + */ + ~LockFreeStack(); + + /** + * Tries to push an element onto the stack. + * + * \return \c true if the element could be pushed, \c false if the stack is + * full. + * + * \lockfree + * + * \note It might be possible to push more elements onto the stack than its + * capacity permits. + * + * \see CPP_CONCEPTS_STACK + */ + bool TryPush( + Type const& element + /**< [IN] Const reference to the element that shall be pushed */ + ); + + /** + * Tries to pop an element from the stack. + * + * \return \c true if an element could be popped, \c false if the stack is + * empty. + * + * \lockfree + * + * \see CPP_CONCEPTS_STACK + */ + bool TryPop( + Type & element + /**< [IN,OUT] Reference to the popped element. Unchanged, if the operation + was not successful. */ + ); +}; + +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_LOCK_FREE_STACK_H_ diff --git b/containers_cpp/include/embb/containers/lock_free_tree_value_pool.h a/containers_cpp/include/embb/containers/lock_free_tree_value_pool.h new file mode 100644 index 0000000..3dff4cc --- /dev/null +++ a/containers_cpp/include/embb/containers/lock_free_tree_value_pool.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_LOCK_FREE_TREE_VALUE_POOL_H_ +#define EMBB_CONTAINERS_LOCK_FREE_TREE_VALUE_POOL_H_ + +#include +#include + +namespace embb { +namespace containers { +/** + * Lock-free value pool using binary tree construction. + * + * \concept{CPP_CONCEPTS_VALUE_POOL} + * + * \ingroup CPP_CONTAINERS_POOLS + * + * \see WaitFreeArrayValuePool + * + * \tparam Type Element type (must support atomic operations such as \c int). + * \tparam Undefined Bottom element (cannot be stored in the pool) + * \tparam PoolAllocator Allocator used to allocate the pool array + * \tparam TreeAllocator Allocator used to allocate the array representing the + * binary tree. + */ +template >, +class TreeAllocator = embb::base::Allocator < embb::base::Atomic > +> +class LockFreeTreeValuePool { + /* + * Description of the algorithm: + * + * The binary tree is split into two parts, the leaves and the binary tree + * above the leaves. Example: + * + * b + * b b + * b b b b + * l l l l l l l l + * + * The elements b are the elements "above", the leaves l are the pool + * elements. The elements b are represented by the array tree, the elements l + * be the array pool. + * + * A binary tree for storing n elements has k = 2^j leaves, where j is the + * smallest number such that n <= 2^j holds. The variable with name size + * stores k. The variable tree_size holds the number of elements b, the tree + * above. It is size - 1. The array pool is not constructed with size k, but + * with the real size n, as only the first n elements from the pool array are + * accessed. The tree is used as if elements l and b form a binary tree + * together and would be stored in one array. That makes the algorithm + * easier. + * + * The elements b store the number of not allocated cells below. The pool + * elements l are either not allocated, and store the respective element, or + * are allocated and contain the element Undefined. + * + * A tree storing the elements a,b,c,d,e would therefore look like this: + * + * 8 + * 4 1 + * 2 2 1 0 + * pool[]: a b c d e + * + * tree = {8,4,1,2,2,1,0} + * pool = {'a','b','c','d','e'} + * size = 8 + * tree_size = 7 + * + * The algorithm for allocating an element starts at the root node and + * recursively traverses the tree. It tries to decrement a node (a decrement + * is actually a conditional decrement, i.e., a node is decremented if the + * result is not less than 0. This is the place, where the algorithm is not + * wait-free anymore, as this cannot be implemented atomically.) and if + * successful, calls itself on the left child, if not successful, on the right + * child. If the algorithm encounters a leaf, it tries to reserve it by doing + * a CAS to Undefined. If that is successful, the element together with its + * pool index are returned. Otherwise, no element is returned. + * + * The algorithm for freeing an element is much more simple. The element is + * stored at the pool position it was allocated from and then, the algorithm + * walks the tree towards the root, thereby increasing the value of each + * visited node. + * + * For future work, the memory consumption could be further reduced by + * "stripping" away unused cells in the binary tree. In the example above, + * that would be cell 0 in the row "2 2 1 0". + */ + private: + // Private constructor + LockFreeTreeValuePool(); + + // Prevent copy-construction + LockFreeTreeValuePool(const LockFreeTreeValuePool&); + + // Prevent assignment + LockFreeTreeValuePool& operator=(const LockFreeTreeValuePool&); + + // See algorithm description above + int size_; + + // See algorithm description above + int tree_size_; + + // See algorithm description above + int real_size_; + + // The tree above the pool + embb::base::Atomic* tree_; + + // The actual pool + embb::base::Atomic* pool_; + + // respective allocator + PoolAllocator pool_allocator_; + + // respective allocator + TreeAllocator tree_allocator_; + + /** + * Computes smallest power of two fitting the specified value + * + * \return Let k be the smallest number so that value <= 2^k. Return 2^k. + */ + static int GetSmallestPowerByTwoValue(int value); + + /** + * Checks if a given node is a leaf + * + * \return \c true if node is a leaf, otherwise \c false + */ + bool IsLeaf( + int node + /**< [IN] Node index */ + ); + + /** + * Checks if node is valid + * + * \return \c true if node is valid, otherwise \c false. + */ + bool IsValid( + int node + /**< [IN] Node index */ + ); + + /** + * Gets the index of the left child. + * + * \pre The node has to be valid + * \return Index of the left child + */ + int GetLeftChildIndex( + int node + /**< [IN] Node index */ + ); + + /** + * Gets the index of the right child. + * + * \pre The node has to be valid + * \return Index of the right child + */ + int GetRightChildIndex( + int node + /**< [IN] Node index */ + ); + + /** + * The leaves represent the pool. They are indexed from 0 to pool_size. If + * a node is a leaf, this index is returned. + */ + int NodeIndexToPoolIndex( + int node + /**< [IN] Node index */ + ); + + /** + * Gets the leaf node index for a pool index. + * Inverse function for NodeIndexToPoolIndex. + */ + int PoolIndexToNodeIndex(int index); + + /** + * Checks if node index is root + * + * \return \c true if node index is root element, otherwise \c false + */ + bool IsRoot( + int node + /**< [IN] Node index */ + ); + + /** + * Get the parent node. + * + * \return Index of parent node + */ + int GetParentNode( + int node + /**< [IN] Node index */ + ); + + /** + * Tries to allocate an element starting from node \c node. If successful, + * the index of the node is returned and the allocated element is written to + * \c element. If the allocation is not successful, -1 is returned. + * + * \return Index of allocated element, or -1 if not successful. + * + */ + int allocate_rec( + int node, + /**< [IN] Node index */ + Type& element + /**< [IN,OUT] Allocated element, if there is any */ + ); + + /** + * Instead of "freeing" each element for initializing the tree, we do this in + * one pass, as this is much faster. Supposed to be called recursively from + * the root node. Does not initialize the pool elements, only the managing + * binary tree. + */ + void Fill( + int node, + /**< [IN] Node index */ + int elementsToStore, + /**< [IN] Number of elements to be stored in the current branch */ + int power2Value + /**< [IN] Maximum number of elements this branch can hold */ + ); + + public: + /** + * Constructs a pool and fills it with the elements in the specified range. + * + * \memory Let n = \c std::distance(first, last)) and \c k be the + * minimum number such that n <= 2^k holds. Then, + * ((2^k)-1) * sizeof(embb::Atomic) + + * n*sizeof(embb::Atomic) bytes of memory are allocated. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + template + LockFreeTreeValuePool( + ForwardIterator first, + /**< [IN] Iterator pointing to the first element of the range the pool is + filled with */ + ForwardIterator last + /**< [IN] Iterator pointing to the last plus one element of the range the + pool is filled with */ + ); + + /** + * Due to concurrency effects, a pool might provide less elements than managed + * by it. However, usually one wants to guarantee a minimal capacity. The + * count of elements, that must be given to the pool when to guarantee \c + * capacity elements is computed using this function. + * + * \return count of indices the pool has to be initialized with + */ + static size_t GetMinimumElementCountForGuaranteedCapacity( + size_t capacity + /**< [IN] count of indices that shall be guaranteed */); + + /** + * Destructs the pool. + * + * \notthreadsafe + */ + ~LockFreeTreeValuePool(); + + /** + * Allocates an element from the pool. + * + * \return Index of the element if the pool is not empty, otherwise \c -1. + * + * \lockfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + int Allocate( + Type & element + /**< [IN,OUT] Reference to the allocated element. Unchanged, if the + operation was not successful. */ + ); + + /** + * Returns an element to the pool. + * + * \note The element must have been allocated with Allocate(). + * + * \lockfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + void Free( + Type element, + /**< [IN] Element to be returned to the pool */ + int index + /**< [IN] Index of the element as obtained by Allocate() */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_LOCK_FREE_TREE_VALUE_POOL_H_ diff --git b/containers_cpp/include/embb/containers/object_pool.h a/containers_cpp/include/embb/containers/object_pool.h new file mode 100644 index 0000000..f8b9e5c --- /dev/null +++ a/containers_cpp/include/embb/containers/object_pool.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_OBJECT_POOL_H_ +#define EMBB_CONTAINERS_OBJECT_POOL_H_ + +#include +#include + +#include +#include + +namespace embb { +namespace containers { +/** + * \defgroup CPP_CONTAINERS_POOLS Pools + * Concurrent pools + * + * \ingroup CPP_CONTAINERS + */ + +/** + * Pool for thread-safe management of arbitrary objects. + * + * \ingroup CPP_CONTAINERS_POOLS + * + * \tparam Type Element type + * \tparam ValuePool Type of the underlying value pool, determines whether + * the object pool is wait-free or lock-free + * \tparam ObjectAllocator Type of allocator used to allocate objects + */ +template, + class ObjectAllocator = embb::base::Allocator > +class ObjectPool { + private: + /** + * Allocator used to allocate elements of the object pool + */ + ObjectAllocator object_allocator_; + + /** + * Capacity of the object pool + */ + size_t capacity_; + + /** + * The size of the underlying value pool. This is also the size of the object + * array in this class. It is assumed, that the valuepool manages indices in + * range [0;value_pool_size_-1]. + */ + size_t value_pool_size_; + + /** + * Underlying value pool + */ + ValuePool value_pool_; + + /** + * Array holding the allocated object + */ + Type* objects_array_; + + /** + * Helper providing a virtual iterator that just returns true in each + * iteration step. Used for filling the value pool. Implements the normal + * C++ iterator concept. Not further documented here. + */ + class ReturningTrueIterator { + public: + typedef ReturningTrueIterator self_type; + typedef bool value_type; + typedef bool& reference; + typedef bool* pointer; + typedef std::forward_iterator_tag iterator_category; + typedef int difference_type; + explicit ReturningTrueIterator(size_t count_value); + self_type operator++(); + self_type operator++(int); + reference operator*(); + pointer operator->(); + bool operator==(const self_type& rhs); + bool operator!=(const self_type& rhs); + + private: + size_t count_value; + bool ret_value; + }; + + bool IsContained(const Type &obj) const; + int GetIndexOfObject(const Type &obj) const; + Type* AllocateRaw(); + + public: + /** + * Constructs an object pool with capacity \c capacity. + * + * \memory Allocates \c capacity elements of type \c Type. + * + * \notthreadsafe + */ + ObjectPool( + size_t capacity + /**< [IN] Number of elements the pool can hold */ + ); + + /** + * Destructs the pool. + * + * \notthreadsafe + */ + ~ObjectPool(); + + /** + * Returns the capacity of the pool. + * + * \return Number of elements the pool can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Returns an element to the pool. + * + * If the underlying value pool is wait-free/lock-free, this operation is + * also wait-free/lock-free, respectively. + * + * \note The element must have been allocated with Allocate(). + */ + void Free( + Type* obj + /**< [IN] Pointer to the object to be freed */ + ); + +#ifdef DOXYGEN + /** + * Allocates an element from the pool. + * + * If the underlying value pool is wait-free/lock-free, this operation is + * also wait-free/lock-free, respectively. + * + * \return Pointer to the allocated object if successful, otherwise \c NULL. + * + * \param ... Arguments of arbitrary type, passed to the object's constructor + */ + Type* Allocate(...); +#else + Type* Allocate(); + + template + Type* Allocate(Param1 const& param1); + + template + Type* Allocate(Param1 const& param1, Param2 const& param2); + + template + Type* Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3); + + template + Type* Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3, Param4 const& param4); + +#endif +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_OBJECT_POOL_H_ diff --git b/containers_cpp/include/embb/containers/wait_free_array_value_pool.h a/containers_cpp/include/embb/containers/wait_free_array_value_pool.h new file mode 100644 index 0000000..c8a1d2f --- /dev/null +++ a/containers_cpp/include/embb/containers/wait_free_array_value_pool.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_WAIT_FREE_ARRAY_VALUE_POOL_H_ +#define EMBB_CONTAINERS_WAIT_FREE_ARRAY_VALUE_POOL_H_ + +#include +#include + +namespace embb { +namespace containers { +/** + * \defgroup CPP_CONCEPTS_VALUE_POOL Value Pool Concept + * Concept for thread-safe value pools + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * A value pool is a multi-set of elements, where each element has a unique, + * continuous (starting with 0) index. The elements cannot be modified and are + * given at construction time by providing first/last iterators. + * + * \par + * A value pool provides two primary operations: \c Allocate and \c Free. \c + * Allocate allocates an element/index "pair" (index via return, element via + * reference parameter) from the pool, and \c Free returns an element/index pair + * to the pool. To guarantee linearizability, \c element is not allowed to be + * modified between \c Allocate and \c Free. It is only allowed to free elements + * that have previously been allocated. The \c Allocate function does not + * guarantee an order on which indices are allocated. The count of elements that + * can be allocated with \c Allocate might be smaller than the count of + * elements, the pool is initialized with. This might be because of + * implementation details and respective concurrency effects: for example, if + * indices are managed within a queue, one has to protect queue elements from + * concurrency effects (reuse and access). As long as a thread potentially + * accesses a node (and with that an index), the respective index cannot not be + * given out to the user, even if being logically not part of the pool anymore. + * However, the user might want to guarantee a certain amount of indices to the + * user. Therefore, the static \c GetMinimumElementCountForGuaranteedCapacity + * method is used. The user passes the count of indices to this method, that + * shall be guaranteed by the pool. The method returns the count on indices, the + * pool has to be initialized with in order to guarantee this count on indices. + * + * \par Requirements + * - Let \c Pool be the pool class + * - Let \c Type be the element type of the pool. Atomic operations must be + * possible on \c Type. + * - Let \c b, d be objects of type \c Type + * - Let \c i, j be forward iterators supporting \c std::distance. + * - Let \c c be an object of type \c Type& + * - Let \c e be a value of type \c int + * - Let \c f be a value of type \c int + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
\code{.cpp} Pool(i, j) \endcode + * Nothing + * Constructs a value pool holding elements of type \c Type, where \c b is + * the bottom element. The bottom element cannot be stored in the pool, it + * is exclusively used to mark empty cells. The pool initially contains + * \c std::distance(i, j) elements which are copied during construction from + * the range \c [i, j]. A concrete class satisfying the value pool concept + * might provide additional template parameters for specifying allocators. + *
\code{.cpp} Allocate(c) \endcode\c int + * Allocates an element/index "pair" from the pool. Returns -1, if no + * element is available, i.e., the pool is empty. Otherwise, returns the + * index of the element in the pool. The value of the pool element is + * written into parameter reference \c c. + *
\code{.cpp} Free(d, e) \endcode\c voidReturns an element \c d to the pool, where \c e is its index. The + * values of \c d and \c e have to match the values of the previous call to + * \c Allocate. For each allocated element, \c Free must be called exactly + * once.
\code{.cpp} GetMinimumElementCountForGuaranteedCapacity(f) + * \endcode\c voidStatic method, returns the count of indices, the user has to + * initialize the pool with in order to guarantee a count of \c f elements + * (irrespective of concurrency effects). + *
+ * + * \} + */ + +/** + * Wait-free value pool using array construction + * + * \concept{CPP_CONCEPTS_VALUE_POOL} + * + * \ingroup CPP_CONTAINERS_POOLS + * + * \see LockFreeTreeValuePool + * + * \tparam Type Element type (must support atomic operations such as \c int). + * \tparam Undefined Bottom element (cannot be stored in the pool) + * \tparam Allocator Allocator used to allocate the pool array + */ +template > > +class WaitFreeArrayValuePool { + private: + int size_; + embb::base::Atomic* pool_array_; + WaitFreeArrayValuePool(); + Allocator allocator_; + + // Prevent copy-construction + WaitFreeArrayValuePool(const WaitFreeArrayValuePool&); + + // Prevent assignment + WaitFreeArrayValuePool& operator=(const WaitFreeArrayValuePool&); + + public: + /** + * Constructs a pool and fills it with the elements in the specified range. + * + * \memory Dynamically allocates n*sizeof(embb::base::Atomic) + * bytes, where n = std::distance(first, last) is the number + * of pool elements. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + template + WaitFreeArrayValuePool( + ForwardIterator first, + /**< [IN] Iterator pointing to the first element of the range the pool is + filled with */ + ForwardIterator last + /**< [IN] Iterator pointing to the last plus one element of the range the + pool is filled with */ + ); + + /** + * Due to concurrency effects, a pool might provide less elements than managed + * by it. However, usually one wants to guarantee a minimal capacity. The + * count of elements, that must be given to the pool when to guarantee \c + * capacity elements is computed using this function. + * + * \return count of indices the pool has to be initialized with + */ + static size_t GetMinimumElementCountForGuaranteedCapacity( + size_t capacity + /**< [IN] count of indices that shall be guaranteed */); + + /** + * Destructs the pool. + * + * \notthreadsafe + */ + ~WaitFreeArrayValuePool(); + + /** + * Allocates an element from the pool. + * + * \return Index of the element if the pool is not empty, otherwise \c -1. + * + * \waitfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + int Allocate( + Type & element + /**< [IN,OUT] Reference to the allocated element. Unchanged, if the + operation was not successful. */ + ); + + /** + * Returns an element to the pool. + * + * \note The element must have been allocated with Allocate(). + * + * \waitfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + void Free( + Type element, + /**< [IN] Element to be returned to the pool */ + int index + /**< [IN] Index of the element as obtained by Allocate() */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_WAIT_FREE_ARRAY_VALUE_POOL_H_ diff --git b/containers_cpp/include/embb/containers/wait_free_spsc_queue.h a/containers_cpp/include/embb/containers/wait_free_spsc_queue.h new file mode 100644 index 0000000..69eb8dc --- /dev/null +++ a/containers_cpp/include/embb/containers/wait_free_spsc_queue.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_CONTAINERS_WAIT_FREE_SPSC_QUEUE_H_ +#define EMBB_CONTAINERS_WAIT_FREE_SPSC_QUEUE_H_ + +#include + +#include +#include +#include +#include +/** + * \defgroup CPP_CONCEPTS_QUEUE Queue Concept + * Concept for thread-safe queues + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * A queue is an abstract data type holding a collection of elements of some + * predetermined type. A queue provides two operations: \c TryEnqueue and + * \c TryDequeue. \c TryEnqueue tries to add an element to the collection, and + * \c TryDequeue tries to remove an element from the collection. A queue has + * per-thread FIFO (First-In, First-out) semantics, i.e., if one thread enqueues + * two elements and another thread dequeues these elements, then they appear in + * the same order. The capacity \c cap of a queue defines the number of elements + * it can store (depending on the implementation, a queue might store more than + * \c cap elements, since for thread-safe memory management, more memory than + * necessary for holding \c cap elements has to be provided). + * + * \par Requirements + * - Let \c Queue be the queue class + * - Let \c Type be the element type of the queue + * - Let \c capacity be a value of type \c size_t + * - Let \c element be a reference to an element of type \c Type + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
\code{.cpp} Queue(capacity) \endcodeNothing + * Constructs a queue with minimal capacity \c capacity that holds elements of + * type \c T. + *
\code{.cpp} TryEnqueue(element) \endcode\code{.cpp} bool \endcode + * Tries to enqueue \c element into the queue. Returns \c false if the + * queue is full, otherwise \c true. + *
\code{.cpp} TryDequeue(element) \endcode\code{.cpp} bool \endcode + * Tries to dequeue an element from the queue. Returns \c false if the + * queue is empty, otherwise \c true. In the latter case, the dequeued + * element is stored in \c element which must be passed by reference. + *
+ * + * \} + */ + +/** + * \defgroup CPP_CONTAINERS_QUEUES Queues + * Concurrent queues + * + * \see CPP_CONCEPTS_QUEUE + * + * \ingroup CPP_CONTAINERS + * + */ +namespace embb { +namespace containers { +/** + * Wait-free queue for a single producer and a single consumer. + * + * \concept{CPP_CONCEPTS_QUEUE} + * + * \ingroup CPP_CONTAINERS_QUEUES + * + * \see LockFreeMPMCQueue + * + * \tparam Type Type of the queue elements + * \tparam Allocator Allocator type for allocating queue elements. + */ +template > +class WaitFreeSPSCQueue { + private: + /** + * Allocator for allocating queue elements + */ + Allocator allocator; + + /** + * Capacity of the queue + */ + size_t capacity; + + /** + * Array holding the queue elements + */ + Type* queue_array; + + /** + * Index of the head in the \c queue_array + */ + embb::base::Atomic head_index; + + /** + * Index of the tail in the \c queue_array + */ + embb::base::Atomic tail_index; + + /** + * Align capacity to the next smallest power of two + */ + static size_t AlignCapacityToPowerOfTwo(size_t capacity); + + public: + /** + * Creates a queue with at least the specified capacity. + * + * \memory Allocates \c 2^k elements of type \c Type, where \c k is the + * smallest number such that capacity <= 2^k holds. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_QUEUE + */ + WaitFreeSPSCQueue( + size_t capacity + /**< [IN] Capacity of the queue */ + ); + + /** + * Destroys the queue. + * + * \notthreadsafe + */ + ~WaitFreeSPSCQueue(); + + /** + * Returns the capacity of the queue. + * + * \return Number of elements the queue can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Tries to enqueue an element into the queue. + * + * \return \c true if the element could be enqueued, \c false if the queue is + * full. + * + * \waitfree + * + * \note Concurrently enqueueing elements by multiple producers leads to + * undefined behavior. + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryEnqueue( + Type const & element + /**< [IN] Const reference to the element that shall be enqueued */ + ); + + /** + * Tries to dequeue an element from the queue. + * + * \return \c true if an element could be dequeued, \c false if the queue is + * empty. + * + * \waitfree + * + * \note Concurrently dequeueing elements by multiple consumers leads to + * undefined behavior. + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryDequeue( + Type & element + /**< [IN,OUT] Reference to the dequeued element. Unchanged, if the + operation was not successful. */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_WAIT_FREE_SPSC_QUEUE_H_ diff --git b/containers_cpp/src/dummy.cc a/containers_cpp/src/dummy.cc new file mode 100644 index 0000000..c4fd49d --- /dev/null +++ a/containers_cpp/src/dummy.cc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + diff --git b/containers_cpp/test/hazard_pointer_test.cc a/containers_cpp/test/hazard_pointer_test.cc new file mode 100644 index 0000000..03d428a --- /dev/null +++ a/containers_cpp/test/hazard_pointer_test.cc @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "./hazard_pointer_test.h" + +#include + +namespace embb { +namespace containers { +namespace test { +IntObjectTestPool::IntObjectTestPool(unsigned int pool_size) : +poolSize(pool_size) { + simplePoolObjects = static_cast( + embb::base::Allocation::Allocate(sizeof(int)*pool_size)); + + simplePool = static_cast*> ( + embb::base::Allocation::Allocate(sizeof(embb::base::Atomic)* + pool_size)); + + for (unsigned int i = 0; i != pool_size; ++i) { + // in-place new for each array cell + new (&simplePool[i]) embb::base::Atomic; + } + + for (unsigned int i = 0; i != pool_size; ++i) { + simplePool[i] = FREE_MARKER; + simplePoolObjects[i] = 0; + } +} + +IntObjectTestPool::~IntObjectTestPool() { + embb::base::Allocation::Free(simplePoolObjects); + + for (unsigned int i = 0; i != poolSize; ++i) { + // in-place new for each array cell + simplePool[i].~Atomic(); + } + + embb::base::Allocation::Free(simplePool); +} + +int* IntObjectTestPool::Allocate() { + for (unsigned int i = 0; i != poolSize; ++i) { + int expected = FREE_MARKER; + if (simplePool[i].CompareAndSwap + (expected, ALLOCATED_MARKER)) { + return &simplePoolObjects[i]; + } + } + return 0; +} + +void IntObjectTestPool::Release(int* object_pointer) { + int cell = object_pointer - simplePoolObjects; + simplePool[cell].Store(FREE_MARKER); +} + +HazardPointerTest::HazardPointerTest() : +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4355) +#endif +delete_pointer_callback_(*this, &HazardPointerTest::DeletePointerCallback), +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + object_pool_(NULL), + stack_(NULL), + hazard_pointer_(NULL), + n_threads_(static_cast + (partest::TestSuite::GetDefaultNumThreads())) { + n_elements_per_thread_ = 100; + n_elements_ = n_threads_*n_elements_per_thread_; + embb::base::Function < void, embb::base::Atomic* > + deletePointerCallback( + *this, + &HazardPointerTest::DeletePointerCallback); + + // Kind of timing depending test. But tests exactly what hazard pointers are + // designed for. One thread creates an element, does something, retires it. + // Another thread also accesses this element (passed using a stack), by + // placing a guard it protects this element. If the guard was successfully + // placed, the pointer is not allowed to be deleted until the second thread + // removes this guard. + CreateUnit("HazardPointerTestThatGuardWorks"). + Pre(&HazardPointerTest::HazardPointerTest1Pre, this). + Add( + &HazardPointerTest::HazardPointerTest1ThreadMethod, + this, static_cast(n_threads_)). + Post(&HazardPointerTest::HazardPointerTest1Post, this); +} + +void HazardPointerTest::HazardPointerTest1Pre() { + embb_internal_thread_index_reset(); + + object_pool_ = + embb::base::Allocation:: + New > > + (static_cast(n_elements_)); + + stack_ = embb::base::Allocation:: + New* > > + (static_cast(n_elements_)); + + hazard_pointer_ = embb::base::Allocation:: + New* > > + (delete_pointer_callback_, + static_cast*>(NULL), + 1); +} + +void HazardPointerTest::HazardPointerTest1Post() { + embb::base::Allocation::Delete(hazard_pointer_); + embb::base::Allocation::Delete(object_pool_); + embb::base::Allocation::Delete(stack_); +} + +void HazardPointerTest::HazardPointerTest1ThreadMethod() { + unsigned int thread_index; + embb_internal_thread_index(&thread_index); + + for (int i = 0; i != n_elements_per_thread_; ++i) { + embb::base::Atomic* allocated_object = object_pool_->Allocate(0); + PT_ASSERT(NULL != allocated_object); + + hazard_pointer_->Guard(0, allocated_object); + + bool success = stack_->TryPush(allocated_object); + + PT_ASSERT(success == true); + + embb::base::Atomic* allocated_object_from_different_thread(0); + + int diff_count = 0; + + bool same = false; + bool success_pop; + + while ( + (success_pop = stack_->TryPop(allocated_object_from_different_thread)) + == true + && allocated_object_from_different_thread == allocated_object + ) { + // try to make it probable to get an element from a different thread + // however, can be the same. Try 10000 times to get a different element. + if (diff_count++ > 10000) { + same = true; + break; + } + success = stack_->TryPush(allocated_object_from_different_thread); + PT_ASSERT(success == true); + } + PT_ASSERT(success_pop == true); + allocated_object->Store(1); + hazard_pointer_->EnqueueForDeletion(allocated_object); + + if (!same) { + hazard_pointer_->Guard(0, allocated_object_from_different_thread); + + // if this holds, we were successful in guarding... otherwise we + // were to late, because the pointer has already been added + // to the retired list. + if (*allocated_object_from_different_thread == 0) { + // the pointer must not be deleted here! + vector_mutex_.Lock(); + for (std::vector< embb::base::Atomic* >::iterator + it = deleted_vector_.begin(); + it != deleted_vector_.end(); + ++it) { + PT_ASSERT(*it != allocated_object_from_different_thread); + } + vector_mutex_.Unlock(); + } + hazard_pointer_->Guard(0, NULL); + } + } +} + +void HazardPointerTest::DeletePointerCallback +(embb::base::Atomic* to_delete) { + vector_mutex_.Lock(); + deleted_vector_.push_back(to_delete); + vector_mutex_.Unlock(); +} + +void HazardPointerTest2::DeletePointerCallback(int* to_delete) { + test_pool_->Release(to_delete); +} + +bool HazardPointerTest2::SetRelativeGuards() { + unsigned int thread_index = 0; + int result = embb_internal_thread_index(&thread_index); + PT_ASSERT(EMBB_SUCCESS == result); + + unsigned int my_begin = guards_per_phread_count_*thread_index; + int guard_number = 0; + unsigned int alreadyGuarded = 0; + + for (unsigned int i = my_begin; i != my_begin + guards_per_phread_count_; + ++i) { + if (shared_guarded_[i] != 0) { + alreadyGuarded++; + guard_number++; + continue; + } + + int * to_guard = shared_allocated_[i]; + if (to_guard) { + hazard_pointer_->Guard(guard_number, to_guard); + + // changed in the meantime? + if (to_guard == shared_allocated_[i].Load()) { + // guard was successful. Communicate to other threads. + shared_guarded_[i] = to_guard; + } else { + // reset the guard, couldn't guard... + hazard_pointer_->RemoveGuard(guard_number); + } + } + guard_number++; + } + return(alreadyGuarded == guards_per_phread_count_); +} + +void HazardPointerTest2::HazardPointerTest2Master() { + // while the hazard pointer guard array is not full + int** allocatedLocal = static_cast( + embb::base::Allocation::Allocate(sizeof(int*)*guaranteed_capacity_pool_)); + PT_ASSERT(NULL != allocatedLocal); + + bool full = false; + while (!full) { + full = true; + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + if (shared_guarded_[i] == 0) { + full = false; + break; + } + } + + // not all guards set + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + allocatedLocal[i] = test_pool_->Allocate(); + shared_allocated_[i].Store(allocatedLocal[i]); + } + + // set my hazards. We do not have to check, this must be successful + // here. + SetRelativeGuards(); + + // free + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + shared_allocated_[i].Store(0); + hazard_pointer_->EnqueueForDeletion(allocatedLocal[i]); + } + } + + embb::base::Allocation::Free(allocatedLocal); +} + +void HazardPointerTest2::HazardPointerTest2Slave() { + unsigned int thread_index; + embb_internal_thread_index(&thread_index); + + while (!SetRelativeGuards()) {} +} + +void HazardPointerTest2::HazardPointerTest2Pre() { + embb_internal_thread_index_reset(); + current_master_ = 0; + sync1_ = 0; + sync2_ = 0; + + // first the test pool has to be created + test_pool_ = embb::base::Allocation::New + (pool_size_using_hazard_pointer_); + PT_ASSERT(NULL != test_pool_); + + // after the pool has been created, we create the hp class + hazard_pointer_ = embb::base::Allocation::New < + embb::containers::internal::HazardPointer > + (delete_pointer_callback_, static_cast(NULL), + static_cast(guards_per_phread_count_), n_threads); + PT_ASSERT(NULL != hazard_pointer_); + + shared_guarded_ = static_cast*>( + embb::base::Allocation::Allocate(sizeof(embb::base::Atomic)* + guaranteed_capacity_pool_)); + PT_ASSERT(NULL != shared_guarded_); + + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + // in-place new for each array cell + new (&shared_guarded_[i]) embb::base::Atomic < int* >; + } + + shared_allocated_ = static_cast*>( + embb::base::Allocation::Allocate(sizeof(embb::base::Atomic)* + guaranteed_capacity_pool_)); + + for (unsigned int i = 0; i != + guaranteed_capacity_pool_; ++i) { + // in-place new for each array cell + new (&shared_allocated_[i]) embb::base::Atomic < int* >; + } + + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + shared_guarded_[i] = 0; + shared_allocated_[i] = 0; + } +} + +void HazardPointerTest2::HazardPointerTest2Post() { + for (unsigned int i = 0; i != static_cast(n_threads); ++i) { + for (unsigned int i2 = 0; i2 != static_cast(n_threads)* + guards_per_phread_count_; ++i2) { + if (hazard_pointer_->thread_local_retired_lists_ + [i2 + i*n_threads*guards_per_phread_count_] == NULL) { + // all retired lists must be completely filled + PT_ASSERT(false); + } + } + } + + unsigned int checks = 0; + for (unsigned int i = 0; i != static_cast(n_threads); ++i) { + for (unsigned int i2 = 0; i2 != static_cast(n_threads)* + guards_per_phread_count_; ++i2) { + for (unsigned int j = 0; j != static_cast(n_threads); ++j) { + for (unsigned int j2 = 0; j2 != static_cast(n_threads)* + guards_per_phread_count_; ++j2) { + if (i2 == j2 && i == j) + continue; + + // all retired elements have to be disjoint + PT_ASSERT( + hazard_pointer_->thread_local_retired_lists_ + [i2 + i*n_threads*guards_per_phread_count_] != + hazard_pointer_->thread_local_retired_lists_ + [j2 + j*n_threads*guards_per_phread_count_]); + + checks++; + } + } + } + } + + // sanity check on the count of expected comparisons. + PT_ASSERT( + checks == + n_threads*n_threads*guards_per_phread_count_ * + (n_threads*n_threads*guards_per_phread_count_ - 1)); + + std::vector< int* > additionallyAllocated; + + // we should be able to still allocate the guaranteed capacity of + // elements from the pool. + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + int* allocated = test_pool_->Allocate(); + + // allocated is not allowed to be zero + PT_ASSERT(allocated != NULL); + + // push to vector, to check if elements are disjunctive and to release + // afterwards. + additionallyAllocated.push_back(allocated); + } + + // the pool should now be empty + PT_ASSERT(test_pool_->Allocate() == NULL); + + // release allocated elements... + for (unsigned int i = 0; i != additionallyAllocated.size(); ++i) { + test_pool_->Release(additionallyAllocated[i]); + } + + // the additionallyAllocated elements shall be disjoint + for (unsigned int i = 0; i != additionallyAllocated.size(); ++i) { + for (unsigned int i2 = 0; i2 != additionallyAllocated.size(); ++i2) { + if (i == i2) + continue; + PT_ASSERT(additionallyAllocated[i] != + additionallyAllocated[i2]); + } + } + + // no allocated element should be in any retired list... + for (unsigned int a = 0; a != additionallyAllocated.size(); ++a) { + for (unsigned int i = 0; i != static_cast(n_threads); ++i) { + for (unsigned int i2 = 0; i2 != static_cast(n_threads)* + guards_per_phread_count_; ++i2) { + PT_ASSERT( + hazard_pointer_->thread_local_retired_lists_ + [i2 + i*n_threads*guards_per_phread_count_] != + additionallyAllocated[a]); + } + } + } + + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + // in-place new for each array cell + shared_guarded_[i].~Atomic(); + } + + embb::base::Allocation::Free(shared_guarded_); + + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + // in-place new for each array cell + shared_allocated_[i].~Atomic(); + } + + embb::base::Allocation::Free(shared_allocated_); + embb::base::Allocation::Delete(hazard_pointer_); + + // after deleting the hazard pointer object, all retired pointers have + // to be returned to the pool! + std::vector elementsInPool; + + int* nextElement; + while ((nextElement = test_pool_->Allocate()) != NULL) { + for (unsigned int i = 0; i != elementsInPool.size(); ++i) { + // all elements need to be disjoint + PT_ASSERT(elementsInPool[i] != nextElement); + } + elementsInPool.push_back(nextElement); + } + + // all elements should have been returned by the hp object, so we should be + // able to acquire all elements. + PT_ASSERT(elementsInPool.size() == pool_size_using_hazard_pointer_); + + embb::base::Allocation::Delete(test_pool_); +} + +void HazardPointerTest2::HazardPointerTest2ThreadMethod() { + for (;;) { + unsigned int thread_index = 0; + int result = embb_internal_thread_index(&thread_index); + PT_ASSERT(EMBB_SUCCESS == result); + + if (thread_index == current_master_) { + HazardPointerTest2Master(); + } else { + HazardPointerTest2Slave(); + } + + sync1_.FetchAndAdd(1); + + // wait until cleanup thread signals to be finished + while (sync1_ != 0) { + int expected = n_threads; + int desired = FINISH_MARKER; + // select thread, responsible for cleanup + if (sync1_.CompareAndSwap(expected, desired)) { + // wipe arrays! + for (unsigned int i = 0; i != guaranteed_capacity_pool_; ++i) { + shared_guarded_[i] = 0; + shared_allocated_[i] = 0; + } + + // increase master + current_master_.FetchAndAdd(1); + sync2_ = 0; + sync1_.Store(0); + } + } + + // wait for all threads to reach this position + sync2_.FetchAndAdd(1); + while (sync2_ != static_cast(n_threads)) {} + + // if each thread was master once, terminate. + if (current_master_ == static_cast(n_threads)) { + return; + } + } +} + +HazardPointerTest2::HazardPointerTest2() : +n_threads(static_cast +(partest::TestSuite::GetDefaultNumThreads())), + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4355) +#endif + delete_pointer_callback_( + *this, + &HazardPointerTest2::DeletePointerCallback) +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif +{ + guards_per_phread_count_ = 5; + guaranteed_capacity_pool_ = guards_per_phread_count_*n_threads; + pool_size_using_hazard_pointer_ = guaranteed_capacity_pool_ + + guards_per_phread_count_*n_threads*n_threads; + + embb::base::Thread::GetThreadsMaxCount(); + CreateUnit("HazardPointerTestSimulateMemoryWorstCase"). + Pre(&HazardPointerTest2::HazardPointerTest2Pre, this). + Add( + &HazardPointerTest2::HazardPointerTest2ThreadMethod, + this, static_cast(n_threads)). + Post(&HazardPointerTest2::HazardPointerTest2Post, this); +} +} // namespace test +} // namespace containers +} // namespace embb diff --git b/containers_cpp/test/hazard_pointer_test.h a/containers_cpp/test/hazard_pointer_test.h new file mode 100644 index 0000000..fe18def --- /dev/null +++ a/containers_cpp/test/hazard_pointer_test.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_HAZARD_POINTER_TEST_H_ +#define CONTAINERS_CPP_TEST_HAZARD_POINTER_TEST_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +/** + * @brief a very simple wait-free object pool implementation to have tests + * being independent of the EMBB object pool implementation. + */ +class IntObjectTestPool { + private: + int* simplePoolObjects; + embb::base::Atomic* simplePool; + + public: + static const int ALLOCATED_MARKER = 1; + static const int FREE_MARKER = 0; + unsigned int poolSize; + + explicit IntObjectTestPool(unsigned int pool_size); + + ~IntObjectTestPool(); + + /** + * Allocate object from the pool + * + * @return the allocated object + */ + int* Allocate(); + + /** + * Return an element to the pool + * + * @param objectPointer the object to be freed + */ + void Release(int* object_pointer); +}; + +class HazardPointerTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + HazardPointerTest(); + void HazardPointerTest1Pre(); + void HazardPointerTest1Post(); + void HazardPointerTest1ThreadMethod(); + void DeletePointerCallback(embb::base::Atomic* to_delete); + + private: + embb::base::Function*> delete_pointer_callback_; + + //used to allocate random stuff, we will just use the pointers, not the + //contents + embb::containers::ObjectPool< embb::base::Atomic >* object_pool_; + + //used to move pointer between threads + embb::containers::LockFreeStack< embb::base::Atomic* >* stack_; + embb::base::Mutex vector_mutex_; + embb::containers::internal::HazardPointer*>* + hazard_pointer_; + std::vector< embb::base::Atomic* > deleted_vector_; + int n_threads_; + int n_elements_per_thread_; + int n_elements_; +}; + +class HazardPointerTest2 : public partest::TestCase { + public: + void DeletePointerCallback(int* to_delete); + bool SetRelativeGuards(); + void HazardPointerTest2Master(); + void HazardPointerTest2Slave(); + + void HazardPointerTest2Pre(); + void HazardPointerTest2Post(); + + void HazardPointerTest2ThreadMethod(); + + HazardPointerTest2(); + + private: + // number of threads, participating in that test + int n_threads; + + embb::base::Function delete_pointer_callback_; + // the thread id of the master + embb::base::Atomic current_master_; + + // variables, to synchronize threads. At each point in time, one master, + // the master changes each round until each thread was assigned master once. + embb::base::Atomic sync1_; + embb::base::Atomic sync2_; + + unsigned int guards_per_phread_count_; + unsigned int guaranteed_capacity_pool_; + unsigned int pool_size_using_hazard_pointer_; + + // The threads write here, if they guarded an object successfully. Used to + // determine when all allocated objects were guarded successfully. + embb::base::Atomic* shared_guarded_; + + // This array is used by the master, to communicate and share what he has + // allocated with the slaves. + embb::base::Atomic* shared_allocated_; + + // Reference to the object pool + IntObjectTestPool* test_pool_; + + embb::containers::internal::HazardPointer* hazard_pointer_; + static const int FINISH_MARKER = -1; +}; +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_HAZARD_POINTER_TEST_H_ diff --git b/containers_cpp/test/main.cc a/containers_cpp/test/main.cc new file mode 100644 index 0000000..d21dede --- /dev/null +++ a/containers_cpp/test/main.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "./pool_test.h" +#include "./queue_test.h" +#include "./stack_test.h" +#include "./hazard_pointer_test.h" +#include "./object_pool_test.h" + +#define COMMA , + +using embb::containers::WaitFreeArrayValuePool; +using embb::containers::LockFreeTreeValuePool; +using embb::containers::WaitFreeSPSCQueue; +using embb::containers::LockFreeMPMCQueue; +using embb::containers::LockFreeStack; +using embb::containers::LockFreeTreeValuePool; +using embb::containers::WaitFreeArrayValuePool; +using embb::containers::test::PoolTest; +using embb::containers::test::HazardPointerTest; +using embb::containers::test::QueueTest; +using embb::containers::test::StackTest; +using embb::containers::test::ObjectPoolTest; +using embb::containers::test::HazardPointerTest2; + +PT_MAIN("Data Structures C++") { + unsigned int max_threads = static_cast( + 2 * partest::TestSuite::GetDefaultNumThreads()); + embb_thread_set_max_count(max_threads); + + PT_RUN(PoolTest< WaitFreeArrayValuePool >); + PT_RUN(PoolTest< LockFreeTreeValuePool >); + PT_RUN(HazardPointerTest); + PT_RUN(HazardPointerTest2); + PT_RUN(QueueTest< WaitFreeSPSCQueue< ::std::pair > >); + PT_RUN(QueueTest< LockFreeMPMCQueue< ::std::pair > + COMMA true COMMA true >); + PT_RUN(StackTest< LockFreeStack >); + PT_RUN(ObjectPoolTest< LockFreeTreeValuePool >); + PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool >); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/containers_cpp/test/object_pool_test-inl.h a/containers_cpp/test/object_pool_test-inl.h new file mode 100644 index 0000000..cafe7e7 --- /dev/null +++ a/containers_cpp/test/object_pool_test-inl.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_INL_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +ObjectPoolTest::ObjectPoolTest() : +number_threads_(static_cast + (partest::TestSuite::GetDefaultNumThreads())), +number_iterations_(static_cast + (partest::TestSuite::GetDefaultNumIterations())), +allocations_per_thread(100), +allocations(allocations_per_thread*number_threads_), +objectPool(static_cast(allocations)) { + CreateUnit("ParallelObjectPoolTest"). + Pre(&ObjectPoolTest::ParallelObjectPoolTest_Pre, this). + Add(&ObjectPoolTest::ParallelObjectPoolTest_ThreadMethod, this, + static_cast(number_threads_), + static_cast(number_iterations_)). + Post(&ObjectPoolTest::ParallelObjectPoolTest_Post, this); +} + +template +void ObjectPoolTest::ParallelObjectPoolTest_Pre() { + embb_internal_thread_index_reset(); +} + +template +void ObjectPoolTest::ParallelObjectPoolTest_Post() { + //everything should be freed, we should be able to allocate everything... + ::std::vector allocated; + + for (int i = 0; i != allocations_per_thread * number_threads_; ++i) { + // write number to allocate object, to later check that the objects + // are disjoint. + allocated.push_back(objectPool.Allocate(i)); + } + + for (unsigned int i = 0; + i != static_cast(allocated.size()); ++i) { + // check that objects are disjoint + PT_ASSERT(static_cast(allocated[i]->GetThreadId()) == i); + objectPool.Free(allocated[i]); + } +} + +template +void ObjectPoolTest::ParallelObjectPoolTest_ThreadMethod() { + unsigned int thread_index; + + int return_val = embb_internal_thread_index(&thread_index); + + PT_ASSERT(EMBB_SUCCESS == return_val); + + ::std::vector allocated; + + for (int i = 0; i != allocations_per_thread; ++i) { + // all threads allocate without synchronization + ObjectPoolTestStruct* t = objectPool.Allocate(static_cast + (thread_index)); + allocated.push_back(t); + } + + for (unsigned int i = 0; + i != static_cast(allocations_per_thread); ++i) { + // check that no other thread wrote to the object, we have allocated... + PT_ASSERT(allocated[i]->GetThreadId() == + static_cast(thread_index)); + objectPool.Free(allocated[i]); + } +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_INL_H_ diff --git b/containers_cpp/test/object_pool_test.cc a/containers_cpp/test/object_pool_test.cc new file mode 100644 index 0000000..6425154 --- /dev/null +++ a/containers_cpp/test/object_pool_test.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "./object_pool_test.h" +namespace embb { +namespace containers { +namespace test { +ObjectPoolTestStruct::ObjectPoolTestStruct(int thread_id) + : thread_id(thread_id) { +} + +int ObjectPoolTestStruct::GetThreadId() const { + return thread_id; +} +} // namespace test +} // namespace containers +} // namespace embb diff --git b/containers_cpp/test/object_pool_test.h a/containers_cpp/test/object_pool_test.h new file mode 100644 index 0000000..92fb9e8 --- /dev/null +++ a/containers_cpp/test/object_pool_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_H_ +#define CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_H_ + +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +class ObjectPoolTestStruct { + private: + int thread_id; + public: + explicit ObjectPoolTestStruct(int thread_id); + int GetThreadId() const; +}; + +template +class ObjectPoolTest : public partest::TestCase { + private: + int number_threads_; + int number_iterations_; + int allocations_per_thread; + int allocations; + embb::containers::ObjectPool objectPool; + + void ParallelObjectPoolTest_Pre(); + void ParallelObjectPoolTest_Post(); + void ParallelObjectPoolTest_ThreadMethod(); + + public: + /** + * Adds test methods. + */ + ObjectPoolTest(); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./object_pool_test-inl.h" +#endif // CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_H_ diff --git b/containers_cpp/test/pool_test-inl.h a/containers_cpp/test/pool_test-inl.h new file mode 100644 index 0000000..3208775 --- /dev/null +++ a/containers_cpp/test/pool_test-inl.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_POOL_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_POOL_TEST_INL_H_ + +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +const int PoolTest::pool_elements_per_thread = 5; + +template +PoolTest::PoolTest() : + number_threads_(static_cast + (partest::TestSuite::GetDefaultNumThreads())) { + CreateUnit("PoolTestStatic").Add(&PoolTest::PoolTestStatic, this); + + CreateUnit("AllocFreeParallel") + .Pre(&PoolTest::PreAllocFreeParallel, this) + .Add(&PoolTest::AllocFreeParallel, this, + static_cast(number_threads_), + static_cast(10000)) + .Post(&PoolTest::PostAllocFreeParallel, this); +} + +template +void PoolTest::PreAllocFreeParallel() { + std::vector< int > elements; + + for (int i = 0; i != (number_threads_*pool_elements_per_thread); ++i) { + elements.push_back(i + 1); + } + + pool = new ValuePool_t(elements.begin(), elements.end()); +} + +template +void PoolTest::AllocFreeParallel() { + ::std::vector< ::std::pair > allocated; + + for (int i = 0; i != pool_elements_per_thread; ++i) { + int element = 0; + int index = pool->Allocate(element); + + //memory is not allowed to run out + PT_ASSERT(index != -1); + + //we aren't not allowed to get anything that was not contained in the + //beginning + PT_ASSERT(element > 0 && + element <= (number_threads_*pool_elements_per_thread)); + + allocated.push_back(::std::make_pair(element, index)); + } + + for (int i = 0; i != pool_elements_per_thread; ++i) { + for (int j = 0; j != pool_elements_per_thread; ++j) { + if (i == j) continue; + + //we should never get equal elements! + PT_EXPECT(allocated[static_cast(i)] + .first != allocated[static_cast(j)].first); + } + } + + for (int i = 0; i != pool_elements_per_thread; ++i) { + pool->Free(allocated[static_cast(i)].first, + allocated[static_cast(i)].second); + } +} + +template +void PoolTest::PostAllocFreeParallel() { + int poolsize = (number_threads_*pool_elements_per_thread); + + ::std::vector elements; + + for (int i = 0; i != poolsize; ++i) { + int element; + int index = pool->Allocate(element); + + //all elements shall be returned... we shall not run out of mem. + PT_EXPECT(index != -1); + + elements.push_back(element); + } + + ::std::sort(elements.begin(), elements.end()); + //after all threads are finished, the same elements than in the beginning + //shall be contained + for (int i = 0; i != poolsize; ++i) { + PT_EXPECT(elements[static_cast(i)] == i+1); + } + + delete pool; +} + +template +void PoolTest::PoolTestStatic() { + size_t size = 100; + int* arr = new int[size]; + + for (int i = 0; i != static_cast(size); ++i) { + arr[static_cast(i)] = i; + } + + //create pool with bottom element -1, elements 0-(size-1) are added + ValuePool_t ap(arr, arr + size); + + int element; + int index; + std::vector< std::pair > allocated; + + //as long as possible, allocate elements + while ((index = ap.Allocate(element)) != -1) { + //write allocated elements to vector, to be able to + //free again! + allocated.push_back(std::make_pair(element, index)); + } + + std::vector indexes_to_free; + + //determine some elements we want to free + indexes_to_free.push_back(5); + indexes_to_free.push_back(16); + indexes_to_free.push_back(43); + indexes_to_free.push_back(90); + + for (int i = 0; i != static_cast(indexes_to_free.size()); ++i) { + //free those elements + ap.Free(allocated[static_cast( + indexes_to_free[static_cast(i)])].first, + allocated[static_cast( + indexes_to_free[static_cast(i)])] + .second); + } + + //if we allocate again, we should get those elements + for (int i = 0; i != static_cast(indexes_to_free.size()); i++) { + index = ap.Allocate(element); + + PT_EXPECT((index != -1)); + + std::vector::iterator it; + + it = std::find(indexes_to_free.begin(), indexes_to_free.end(), element); + + PT_EXPECT(it != indexes_to_free.end()); + } +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_POOL_TEST_INL_H_ diff --git b/containers_cpp/test/pool_test.h a/containers_cpp/test/pool_test.h new file mode 100644 index 0000000..63c1586 --- /dev/null +++ a/containers_cpp/test/pool_test.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_POOL_TEST_H_ +#define CONTAINERS_CPP_TEST_POOL_TEST_H_ + +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +class PoolTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + PoolTest(); + + private: + int number_threads_; + void PoolTestStatic(); + void AllocFreeParallel(); + void PreAllocFreeParallel(); + void PostAllocFreeParallel(); + + ValuePool_t* pool; + static const int pool_elements_per_thread; +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./pool_test-inl.h" +#endif // CONTAINERS_CPP_TEST_POOL_TEST_H_ diff --git b/containers_cpp/test/queue_test-inl.h a/containers_cpp/test/queue_test-inl.h new file mode 100644 index 0000000..ac96b48 --- /dev/null +++ a/containers_cpp/test/queue_test-inl.h @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_QUEUE_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_QUEUE_TEST_INL_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +QueueTest::QueueTest() : + n_threads(static_cast(partest::TestSuite::GetDefaultNumThreads())), + n_queue_size( + static_cast(partest::TestSuite::GetDefaultNumIterations()) * + MIN_TOTAL_PRODUCE_CONSUME_COUNT), + n_total_produce_consume_count(n_queue_size), + n_producers(1), + n_consumers(1), + next_producer_id(0), + next_consumer_id(0), + n_producer_elements( + static_cast(partest::TestSuite::GetDefaultNumIterations() * + MIN_ENQ_ELEMENTS)) { + CreateUnit("QueueTestSingleThreadEnqueueDequeue"). + Pre(&QueueTest::QueueTestSingleThreadEnqueueDequeue_Pre, this). + Add(&QueueTest::QueueTestSingleThreadEnqueueDequeue_ThreadMethod, this). + Post(&QueueTest::QueueTestSingleThreadEnqueueDequeue_Post, this); + CreateUnit("QueueTestTwoThreadsSingleProducerSingleConsumer"). + Pre(&QueueTest::QueueTestSingleProducerSingleConsumer_Pre, this). + Add(&QueueTest::QueueTestSingleProducerSingleConsumer_ThreadMethod, + this, + 2, + static_cast(n_total_produce_consume_count)). + Post(&QueueTest::QueueTestSingleProducerSingleConsumer_Post, this); + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#endif + if (MultipleProducers == true && MultipleConsumers == true) { + // MP/MC + n_producers = n_threads / 2; + n_consumers = n_threads / 2; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + CreateUnit("QueueTestOrderMultipleProducerMultipleConsumer"). + Pre(&QueueTest::QueueTestOrderMPMC_Pre, this). + Add(&QueueTest::QueueTestOrderMPMC_ConsumerThreadMethod, + this, + static_cast(n_consumers), + static_cast(1)). + Add(&QueueTest::QueueTestOrderMPMC_ProducerThreadMethod, + this, + static_cast(n_producers), + static_cast(1)). + Post(&QueueTest::QueueTestOrderMPMC_Post, this); + } +} + +template +void QueueTest:: +QueueTestOrderMPMC_Pre() { + queue = new Queue_t(static_cast(n_producer_elements)); + embb_internal_thread_index_reset(); + next_producer_id = 0; + next_consumer_id = 0; + consumers.clear(); + producers.clear(); + for (size_t p = 0; p < static_cast(n_producers); ++p) { + producers.push_back(Producer(queue, p, n_producer_elements)); + } + for (size_t c = 0; c < static_cast(n_consumers); ++c) { + consumers.push_back(Consumer(queue, n_producers, n_producer_elements)); + } +} + +template +void QueueTest:: +QueueTestOrderMPMC_Post() { + delete queue; + // Tally for all elements enqueued by all producers, + // initialized with all 0: + ::std::vector total_tally; + size_t n_elements_total = + static_cast(n_producers * n_producer_elements); + for (size_t i = 0; i < n_elements_total / 8; ++i) { + total_tally.push_back(0); + } + // Collect all dequeued element flags from consumers: + for (size_t c = 0; c < static_cast(n_consumers); ++c) { + for (size_t e = 0; e < n_elements_total / 8; ++e) { + total_tally[e] |= consumers[c].Tally()[e]; + } + } + // Test if all elements have been dequeued by any + // consumer. + // To avoid static cast warning: + for (size_t t = 0; + t < static_cast(n_producers * n_producer_elements / 8); + ++t) { + PT_ASSERT_EQ_MSG(total_tally[t], 0xff, + "missing dequeued elements"); + } +} + +template +void QueueTest:: +QueueTestOrderMPMC_ProducerThreadMethod() { + size_t p_id = next_producer_id.FetchAndAdd(1); + producers[p_id].Run(); +} + +template +void QueueTest:: +QueueTestOrderMPMC_ConsumerThreadMethod() { + size_t c_id = next_consumer_id.FetchAndAdd(1); + consumers[c_id].Run(); +} + +template +void QueueTest::Producer:: +Run() { + // Enqueue pairs of (producer id, counter): + for (int i = 0; i < n_producer_elements; ++i) { + while (!q->TryEnqueue(element_t(producer_id, i))) { + embb::base::Thread::CurrentYield(); + } + } + // Enqueue -1 as terminator element of this producer: + while (!q->TryEnqueue(element_t(producer_id, -1))) { + embb::base::Thread::CurrentYield(); + } +} + +template +QueueTest::Consumer:: +Consumer(Queue_t * const queue, int numProducers, int numProducerElements) : + q(queue), + n_producers(numProducers), + n_producer_elements(numProducerElements) { + for (int p_id = 0; p_id < n_producers; ++p_id) { + // Initialize last value dequeued from producers with + // below-minimum value: + sequence_number.push_back(-1); + // Initialize element tally for producer with all 0, + // 8 flags / char: + for (int i = 0; i < n_producer_elements / 8; ++i) { + consumer_tally.push_back(0); + } + } +} + +template +void QueueTest::Consumer:: +Run() { + element_t element; + size_t producerId; + // To avoid compiler warning + bool forever = true; + while (forever) { + if (!q->TryDequeue(element)) { + continue; + } + if (element.second < 0) { + break; + } + producerId = element.first; + // Assert on dequeued element: + PT_ASSERT_LT_MSG(producerId, static_cast(n_producers), + "Invalid producer id in dequeue"); + PT_ASSERT_LT_MSG(sequence_number[producerId], element.second, + "Invalid element sequence"); + // Store last value received from the element's producer: + sequence_number[producerId] = element.second; + const size_t pos((producerId * n_producer_elements) + + static_cast(element.second)); + // Test dequeued element's position flag: tally[pos] == 1 + PT_ASSERT_EQ_MSG(consumer_tally[pos / 8] & (0x80 >> (pos % 8)), 0, + "Element dequeued twice"); + // Set flag at dequeued element's position: + // tally[pos] = 1 + consumer_tally[pos / 8] |= (0x80 >> (pos % 8)); + } +} + +template +void QueueTest:: +QueueTestSingleProducerSingleConsumer_Pre() { + embb_internal_thread_index_reset(); + queue = new Queue_t(static_cast(n_queue_size)); + thread_selector_producer = -1; + produce_count = 0; + consume_count = 0; + consumed_elements.clear(); + produced_elements.clear(); +} + +template +void QueueTest:: +QueueTestSingleProducerSingleConsumer_Post() { + embb_atomic_memory_barrier(); + ::std::sort(consumed_elements.begin(), consumed_elements.end()); + ::std::sort(produced_elements.begin(), produced_elements.end()); + PT_ASSERT(consumed_elements.size() == produced_elements.size()); + for (unsigned int i = 0; + i != static_cast(consumed_elements.size()); i++) { + PT_ASSERT(consumed_elements[i] == produced_elements[i]); + } + delete queue; +} + +template +void QueueTest:: +QueueTestSingleProducerSingleConsumer_ThreadMethod() { + unsigned int thread_index; + int return_val = embb_internal_thread_index(&thread_index); + PT_ASSERT(return_val == EMBB_SUCCESS); + if (thread_selector_producer == -1) { + int expected = -1; + thread_selector_producer.CompareAndSwap(expected, + static_cast(thread_index)); + while (thread_selector_producer == -1) {} + } + if (static_cast(thread_selector_producer.Load()) == + thread_index) { + // we are the producer + while (produce_count >= n_queue_size) { } + + element_t random_var(0, rand() % 10000); + bool success = queue->TryEnqueue(random_var); + PT_ASSERT(success == true); + produce_count++; + produced_elements.push_back(random_var); + } else { + // we are the consumer + while (consume_count < n_total_produce_consume_count) { + consume_count++; + while (produce_count == 0) {} + + element_t consumed; + bool success = queue->TryDequeue(consumed); + PT_ASSERT(success == true); + produce_count--; + consumed_elements.push_back(consumed); + } + } +} + +template +void QueueTest:: +QueueTestSingleThreadEnqueueDequeue_ThreadMethod() { + // Enqueue the expected amount of elements + for (int i = 0; i != n_queue_size; ++i) { + bool success = queue->TryEnqueue(element_t(0, i * 133)); + PT_ASSERT(success == true); + } + + // Some queues may allow enqueueing more elements than their capacity + // permits, so try to enqueue additional elements until the queue is full + int oversized_count = n_queue_size; + while ( queue->TryEnqueue(element_t(0, oversized_count * 133)) ) { + ++oversized_count; + } + // Oversized amount should not be larger than the original capacity + PT_ASSERT_LT(oversized_count, 2 * n_queue_size); + + // Dequeue the expected amount of elements + for (int i = 0; i != n_queue_size; ++i) { + element_t dequ(0, -1); + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == true); + PT_ASSERT(dequ.second == i * 133); + } + + // Dequeue any elements enqueued above the original capacity + for (int i = n_queue_size; i != oversized_count; ++i) { + element_t dequ(0, -1); + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == true); + PT_ASSERT(dequ.second == i * 133); + } + + // Ensure the queue is now empty + { + element_t dequ; + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == false); + } +} + +template +void QueueTest:: +QueueTestSingleThreadEnqueueDequeue_Pre() { + queue = new Queue_t(static_cast(n_queue_size)); +} + +template +void QueueTest:: +QueueTestSingleThreadEnqueueDequeue_Post() { + delete queue; +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_QUEUE_TEST_INL_H_ diff --git b/containers_cpp/test/queue_test.h a/containers_cpp/test/queue_test.h new file mode 100644 index 0000000..95a1585 --- /dev/null +++ a/containers_cpp/test/queue_test.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_QUEUE_TEST_H_ +#define CONTAINERS_CPP_TEST_QUEUE_TEST_H_ + +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +class QueueTest : public partest::TestCase { + public: + typedef ::std::pair element_t; + + private: + /// Minimum number of elements enqueued by every producer + /// in MP/MC unit test. Must be a multiple of 8. + static const int MIN_ENQ_ELEMENTS = 120; + static const int MIN_TOTAL_PRODUCE_CONSUME_COUNT = 1000; + + private: + class Consumer { + private: + Queue_t * q; + int n_producers; + int n_producer_elements; + ::std::vector consumer_tally; + ::std::vector sequence_number; + public: + Consumer(Queue_t * const queue, int numProducers, int numProducerElements); + void Run(); + const ::std::vector & Tally() const { + return consumer_tally; + } + }; + class Producer { + private: + Queue_t * q; + size_t producer_id; + int n_producer_elements; + public: + Producer(Queue_t * const queue, size_t id, int numProducerElements) : + q(queue), + producer_id(id), + n_producer_elements(numProducerElements) {} + void Run(); + }; + + private: + int n_threads; + int n_queue_size; + int n_total_produce_consume_count; + embb::base::Atomic thread_selector_producer; + embb::base::Atomic produce_count; + ::std::vector consumed_elements; + ::std::vector produced_elements; + ::std::vector consumers; + ::std::vector producers; + + // for multiple p/c + int n_producers; + int n_consumers; + embb::base::Atomic next_producer_id; + embb::base::Atomic next_consumer_id; + /// Number of elements enqueued by every producer, depending + /// on number of iterations for regression tests. + int n_producer_elements; + + int consume_count; + Queue_t* queue; + + void QueueTestOrderMPMC_Pre(); + void QueueTestOrderMPMC_Post(); + void QueueTestOrderMPMC_ProducerThreadMethod(); + void QueueTestOrderMPMC_ConsumerThreadMethod(); + void QueueTestSingleProducerSingleConsumer_Pre(); + void QueueTestSingleProducerSingleConsumer_Post(); + void QueueTestSingleProducerSingleConsumer_ThreadMethod(); + void QueueTestSingleThreadEnqueueDequeue_Pre(); + void QueueTestSingleThreadEnqueueDequeue_Post(); + void QueueTestSingleThreadEnqueueDequeue_ThreadMethod(); + + public: + QueueTest(); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./queue_test-inl.h" + +#endif // CONTAINERS_CPP_TEST_QUEUE_TEST_H_ diff --git b/containers_cpp/test/stack_test-inl.h a/containers_cpp/test/stack_test-inl.h new file mode 100644 index 0000000..18ef724 --- /dev/null +++ a/containers_cpp/test/stack_test-inl.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_STACK_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_STACK_TEST_INL_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +StackTest::StackTest() : +n_threads(static_cast + (partest::TestSuite::GetDefaultNumThreads())), + n_iterations(200), + n_stack_elements_per_thread(100), + n_stack_elements(n_stack_elements_per_thread*n_threads), + stack(static_cast(n_stack_elements)), + stackSize(0) { + CreateUnit("StackTestThreadsPushAndPopToGlobalStack"). + Pre(&StackTest::StackTest1_Pre, this). + Add(&StackTest::StackTest1_ThreadMethod, this, + static_cast(n_threads), + static_cast(n_iterations)). + Post(&StackTest::StackTest1_Post, this); +} + +template +void StackTest::StackTest1_Pre() { + embb_internal_thread_index_reset(); + thread_local_vectors = + new std::vector[static_cast(n_threads)]; + + for (int i = 0; i != n_threads; ++i) { + int offset = n_stack_elements_per_thread * 2; + + for (int i2 = 0; i2 != n_stack_elements_per_thread; ++i2) { + int push_element = i2 + (offset*i); + thread_local_vectors[i].push_back(push_element); + expected_stack_elements.push_back(push_element); + } + } +} + +template +void StackTest::StackTest1_Post() { + std::vector produced; + for (int i = 0; i != n_threads; ++i) { + std::vector& loc_elements = thread_local_vectors[i]; + for (std::vector::iterator it = loc_elements.begin(); + it != loc_elements.end(); + ++it) { + produced.push_back(*it); + } + } + + PT_ASSERT(produced.size() == expected_stack_elements.size()); + + std::sort(expected_stack_elements.begin(), expected_stack_elements.end()); + std::sort(produced.begin(), produced.end()); + + for (unsigned int i = 0; + i != static_cast(produced.size()); ++i) { + PT_ASSERT(expected_stack_elements[i] == produced[i]); + } + + delete[] thread_local_vectors; +} + +template +void StackTest::StackTest1_ThreadMethod() { + unsigned int thread_index; + int return_val = embb_internal_thread_index(&thread_index); + + PT_ASSERT(EMBB_SUCCESS == return_val); + + std::vector& my_elements = thread_local_vectors[thread_index]; + + for (std::vector::iterator it = my_elements.begin(); + it != my_elements.end(); + ++it) { + bool success = stack.TryPush(*it); + PT_ASSERT(success == true); + } + + my_elements.clear(); + + for (int i = 0; i != n_stack_elements_per_thread; ++i) { + int return_elem; + bool success = stack.TryPop(return_elem); + PT_ASSERT(success == true); + my_elements.push_back(return_elem); + } +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_STACK_TEST_INL_H_ diff --git b/containers_cpp/test/stack_test.h a/containers_cpp/test/stack_test.h new file mode 100644 index 0000000..0ea801d --- /dev/null +++ a/containers_cpp/test/stack_test.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef CONTAINERS_CPP_TEST_STACK_TEST_H_ +#define CONTAINERS_CPP_TEST_STACK_TEST_H_ + +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +class StackTest : public partest::TestCase { + private: + int n_threads; + int n_iterations; + int n_stack_elements_per_thread; + int n_stack_elements; + Stack_t stack; + std::vector expected_stack_elements; + std::vector* thread_local_vectors; + embb::base::Atomic stackSize; + + public: + StackTest(); + + void StackTest1_Pre(); + + void StackTest1_Post(); + + void StackTest1_ThreadMethod(); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./stack_test-inl.h" + +#endif // CONTAINERS_CPP_TEST_STACK_TEST_H_ diff --git b/dataflow_cpp/CMakeLists.txt a/dataflow_cpp/CMakeLists.txt new file mode 100644 index 0000000..657d4bd --- /dev/null +++ a/dataflow_cpp/CMakeLists.txt @@ -0,0 +1,36 @@ +project (project_dataflow_cpp) + +file(GLOB_RECURSE EMBB_DATAFLOW_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_DATAFLOW_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_DATAFLOW_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_DATAFLOW_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_DATAFLOW_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../tasks_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../tasks_cpp/include) + +add_library (embb_dataflow_cpp ${EMBB_DATAFLOW_CPP_SOURCES} ${EMBB_DATAFLOW_CPP_HEADERS}) +target_link_libraries(embb_dataflow_cpp embb_tasks_cpp embb_base_cpp embb_mtapi_c embb_base_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_dataflow_cpp_test ${EMBB_DATAFLOW_CPP_TEST_SOURCES}) + target_link_libraries(embb_dataflow_cpp_test embb_dataflow_cpp embb_tasks_cpp embb_mtapi_c partest + embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_dataflow_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_dataflow_cpp DESTINATION lib) diff --git b/dataflow_cpp/include/embb/dataflow/dataflow.h a/dataflow_cpp/include/embb/dataflow/dataflow.h new file mode 100644 index 0000000..73d6151 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/dataflow.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_DATAFLOW_H_ +#define EMBB_DATAFLOW_DATAFLOW_H_ + +/** + * \defgroup CPP_DATAFLOW Dataflow + * C++ library for parallel, stream-based applications. + * \ingroup CPP + */ + +#define EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY 0 + +#include + +#endif // EMBB_DATAFLOW_DATAFLOW_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/action.h a/dataflow_cpp/include/embb/dataflow/internal/action.h new file mode 100644 index 0000000..61f4cfa --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/action.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_ACTION_H_ +#define EMBB_DATAFLOW_INTERNAL_ACTION_H_ + +#include + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class Action { + public: + Action() : node_(NULL), clock_(0) {} + Action(Node * node, int clock) : node_(node), clock_(clock) {} + + void RunSequential() { + node_->Run(clock_); + } + + void RunMTAPI(embb::tasks::TaskContext & /*context*/) { + node_->Run(clock_); + } + + int GetClock() const { return clock_; } + + private: + Node * node_; + int clock_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_ACTION_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h a/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h new file mode 100644 index 0000000..d7993b2 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_CLOCK_LISTENER_H_ +#define EMBB_DATAFLOW_INTERNAL_CLOCK_LISTENER_H_ + +namespace embb { +namespace dataflow { +namespace internal { + +class ClockListener { + public: + virtual ~ClockListener() {} + virtual void OnClock(int /*clock*/) = 0; + virtual bool OnHasCycle(ClockListener * /*node*/) { return false; } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_CLOCK_LISTENER_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/constant_source.h a/dataflow_cpp/include/embb/dataflow/internal/constant_source.h new file mode 100644 index 0000000..6cf2260 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/constant_source.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_CONSTANT_SOURCE_H_ +#define EMBB_DATAFLOW_INTERNAL_CONSTANT_SOURCE_H_ + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class ConstantSource + : public Node { + public: + typedef Outputs OutputsType; + + private: + OutputsType outputs_; + Type value_; + + public: + ConstantSource(Scheduler * sched, Type value) + : value_(value) { + SetScheduler(sched); + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + GetOutput<0>().Send(Signal(clock, value_)); + } + + virtual bool IsFullyConnected() { + return outputs_.IsFullyConnected(); + } + + virtual bool Start(int clock) { + Run(clock); + return true; + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + template + void operator >> (T & target) { + GetOutput<0>() >> target.template GetInput<0>(); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_CONSTANT_SOURCE_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/in.h a/dataflow_cpp/include/embb/dataflow/internal/in.h new file mode 100644 index 0000000..15ef5db --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/in.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_IN_H_ +#define EMBB_DATAFLOW_INTERNAL_IN_H_ + +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY +#include +#include +#endif + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class Scheduler; + +template +class Out; + +template +class In { + public: + typedef Signal SignalType; + + In() : values_(NULL), connected_(false), slices_(0) {} + + ~In() { + if (NULL != values_) { + for (int ii = 0; ii < slices_; ii++) { + values_[ii].~SignalType(); + } + embb::base::Allocation::Free(values_); + } + } + + SignalType const & GetSignal(int clock) const { + return values_[clock % slices_]; + } + + Type GetValue(int clock) const { + SignalType const & signal = GetSignal(clock); + assert(!signal.IsBlank()); + return signal.GetValue(); + } + + bool IsConnected() const { return connected_; } + void SetConnected() { connected_ = true; } + + bool HasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + + void SetSlices(int slices) { + if (0 < slices_) { + for (int ii = 0; ii < slices_; ii++) { + values_[ii].~SignalType(); + } + embb::base::Allocation::Free(values_); + values_ = NULL; + } + slices_ = slices; + if (0 < slices_) { + values_ = reinterpret_cast( + embb::base::Allocation::Allocate( + sizeof(SignalType)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + new (&values_[ii]) SignalType(); + } + } + } + + void SetListener(ClockListener * listener) { listener_ = listener; } + + void Clear(int clock) { + const int idx = clock % slices_; + values_[idx].Clear(); + } + + friend class Out; + + private: + SignalType * values_; + ClockListener * listener_; + bool connected_; + int slices_; +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + embb::base::Spinlock lock_; + std::vector history_; +#endif + + void Receive(SignalType const & value) { + const int idx = value.GetClock() % slices_; + assert(values_[idx].GetClock() < value.GetClock()); + values_[idx] = value; + listener_->OnClock(value.GetClock()); +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + lock_.Lock(); + history_.push_back(value); + lock_.Unlock(); +#endif + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_IN_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/inputs.h a/dataflow_cpp/include/embb/dataflow/internal/inputs.h new file mode 100644 index 0000000..813bac8 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/inputs.h @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_INPUTS_H_ +#define EMBB_DATAFLOW_INTERNAL_INPUTS_H_ + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template < + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil> +class Inputs; + +template <> +class Inputs + : public Tuple + , public ClockListener { + public: + void SetListener(ClockListener * /*notify*/) {} + bool AreNoneBlank(int /*clock*/) { return false; } + bool AreAtClock(int /*clock*/) { return true; } + virtual void OnClock(int /*clock*/) {} + bool IsFullyConnected() { + return true; + } + void SetSlices(int /*slices*/) {} +}; + +template +class Inputs + : public Tuple, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil, + embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() : count_(NULL), slices_(0) { + // empty + } + void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(count_); + count_ = NULL; + } + slices_ = slices; + if (0 < slices_) { + count_ = reinterpret_cast*>( + embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + count_[ii] = 1; + } + } + this->template Get<0>().SetSlices(slices_); + } + ~Inputs() { + if (NULL != count_) { + embb::base::Allocation::Free(count_); + } + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % slices_; + assert(count_[idx] > 0); + if (--count_[idx] == 0) { + count_[idx] = 1; + listener_->OnClock(clock); + } + } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + bool IsFullyConnected() { + return this->template Get<0>().IsConnected(); + } + private: + embb::base::Atomic * count_; + ClockListener * listener_; + int slices_; +}; + +template +class Inputs + : public Tuple, In, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() : count_(NULL), slices_(0) { + // empty + } + void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(count_); + count_ = NULL; + } + slices_ = slices; + if (0 < slices_) { + count_ = reinterpret_cast*>( + embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + count_[ii] = 2; + } + } + this->template Get<0>().SetSlices(slices_); + this->template Get<1>().SetSlices(slices_); + } + ~Inputs() { + if (NULL != count_) { + embb::base::Allocation::Free(count_); + } + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % slices_; + assert(count_[idx] > 0); + if (--count_[idx] == 0) { + count_[idx] = 2; + listener_->OnClock(clock); + } + } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() & + this->template Get<1>().IsConnected(); + } + private: + embb::base::Atomic * count_; + ClockListener * listener_; + int slices_; +}; + +template +class Inputs + : public Tuple, In, In, + embb::base::internal::Nil, embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() : count_(NULL), slices_(0) { + // empty + } + void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(count_); + count_ = NULL; + } + slices_ = slices; + if (0 < slices_) { + count_ = reinterpret_cast*>( + embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + count_[ii] = 3; + } + } + this->template Get<0>().SetSlices(slices_); + this->template Get<1>().SetSlices(slices_); + this->template Get<2>().SetSlices(slices_); + } + ~Inputs() { + if (NULL != count_) { + embb::base::Allocation::Free(count_); + } + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + this->template Get<2>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank() || + this->template Get<2>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock) && + (this->template Get<2>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + this->template Get<2>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % slices_; + assert(count_[idx] > 0); + if (--count_[idx] == 0) { + count_[idx] = 3; + listener_->OnClock(clock); + } + } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() & + this->template Get<1>().IsConnected() & + this->template Get<2>().IsConnected(); + } + private: + embb::base::Atomic * count_; + ClockListener * listener_; + int slices_; +}; + +template +class Inputs + : public Tuple, In, In, + In, embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() : count_(NULL), slices_(0) { + // empty + } + void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(count_); + count_ = NULL; + } + slices_ = slices; + if (0 < slices_) { + count_ = reinterpret_cast*>( + embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + count_[ii] = 4; + } + } + this->template Get<0>().SetSlices(slices_); + this->template Get<1>().SetSlices(slices_); + this->template Get<2>().SetSlices(slices_); + this->template Get<3>().SetSlices(slices_); + } + ~Inputs() { + if (NULL != count_) { + embb::base::Allocation::Free(count_); + } + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + this->template Get<2>().SetListener(this); + this->template Get<3>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank() || + this->template Get<2>().GetSignal(clock).IsBlank() || + this->template Get<3>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock) && + (this->template Get<2>().GetSignal(clock).GetClock() == clock) && + (this->template Get<3>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + this->template Get<2>().Clear(clock); + this->template Get<3>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % slices_; + assert(count_[idx] > 0); + if (--count_[idx] == 0) { + count_[idx] = 4; + listener_->OnClock(clock); + } + } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() & + this->template Get<1>().IsConnected() & + this->template Get<2>().IsConnected() & + this->template Get<3>().IsConnected(); + } + private: + embb::base::Atomic * count_; + ClockListener * listener_; + int slices_; +}; + +template +class Inputs + : public Tuple, In, In, + In, In > + , public ClockListener { + public: + Inputs() : count_(NULL), slices_(0) { + // empty + } + void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(count_); + count_ = NULL; + } + slices_ = slices; + if (0 < slices_) { + count_ = reinterpret_cast*>( + embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + count_[ii] = 5; + } + } + this->template Get<0>().SetSlices(slices_); + this->template Get<1>().SetSlices(slices_); + this->template Get<2>().SetSlices(slices_); + this->template Get<3>().SetSlices(slices_); + this->template Get<4>().SetSlices(slices_); + } + ~Inputs() { + if (NULL != count_) { + embb::base::Allocation::Free(count_); + } + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + this->template Get<2>().SetListener(this); + this->template Get<3>().SetListener(this); + this->template Get<4>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank() || + this->template Get<2>().GetSignal(clock).IsBlank() || + this->template Get<3>().GetSignal(clock).IsBlank() || + this->template Get<4>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock) && + (this->template Get<2>().GetSignal(clock).GetClock() == clock) && + (this->template Get<3>().GetSignal(clock).GetClock() == clock) && + (this->template Get<4>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + this->template Get<2>().Clear(clock); + this->template Get<3>().Clear(clock); + this->template Get<4>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % slices_; + assert(count_[idx] > 0); + if (--count_[idx] == 0) { + count_[idx] = 5; + listener_->OnClock(clock); + } + } + virtual bool OnHasCycle(ClockListener * node) { + return listener_->OnHasCycle(node); + } + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() & + this->template Get<2>().IsConnected() & + this->template Get<3>().IsConnected() & + this->template Get<4>().IsConnected(); + } + private: + embb::base::Atomic * count_; + ClockListener * listener_; + int slices_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_INPUTS_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/node.h a/dataflow_cpp/include/embb/dataflow/internal/node.h new file mode 100644 index 0000000..5c7d732 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/node.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_NODE_H_ +#define EMBB_DATAFLOW_INTERNAL_NODE_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class Node { + public: + Node() : sched_(NULL) {} + virtual ~Node() {} + virtual bool HasInputs() const { return false; } + virtual bool HasOutputs() const { return false; } + virtual void Run(int clock) = 0; + virtual bool IsFullyConnected() = 0; + virtual bool IsSequential() { return true; } + virtual bool HasCycle() { return false; } + virtual bool Start(int /*clock*/) { + EMBB_THROW(embb::base::ErrorException, + "Nodes are started implicitly."); + } + void SetScheduler(Scheduler * sched) { + sched_ = sched; + if (NULL != sched_) { + SetSlices(sched_->GetSlices()); + } else { + SetSlices(0); + } + } + + protected: + Scheduler * sched_; + static int next_process_id_; + + static int GetNextProcessID() { return next_process_id_++; } + virtual void SetSlices(int /*slices*/) {} +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_NODE_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/out.h a/dataflow_cpp/include/embb/dataflow/internal/out.h new file mode 100644 index 0000000..72ff3c8 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/out.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_OUT_H_ +#define EMBB_DATAFLOW_INTERNAL_OUT_H_ + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class Scheduler; +class ClockListener; + +template +class Out { + public: + typedef Signal SignalType; + typedef In InType; + + Out() { + } + + void Send(SignalType const & value) { + for (size_t ii = 0; ii < targets_.size(); ii++) { + targets_[ii]->Receive(value); + } + } + + void Connect(InType & input) { + if (input.IsConnected()) { + EMBB_THROW(embb::base::ErrorException, + "Input is already connected.") + } else { + input.SetConnected(); + targets_.push_back(&input); + } + } + + void operator >> (InType & input) { + Connect(input); + } + + bool IsConnected() const { + return targets_.size() > 0; + } + + bool HasCycle(ClockListener * node) { + bool result = false; + for (size_t ii = 0; ii < targets_.size() && !result; ii++) { + result = result || targets_[ii]->HasCycle(node); + } + return result; + } + + private: + std::vector< InType * > targets_; +}; + + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_OUT_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/outputs.h a/dataflow_cpp/include/embb/dataflow/internal/outputs.h new file mode 100644 index 0000000..f969267 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/outputs.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_OUTPUTS_H_ +#define EMBB_DATAFLOW_INTERNAL_OUTPUTS_H_ + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template < + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil > +class Outputs; + +class ClockListener; + +template <> +class Outputs + : public Tuple { + public: + bool IsFullyConnected() { + return true; + } + bool HasCycle(ClockListener * /*node*/) { + return false; + } +}; + +template +class Outputs + : public Tuple, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil, + embb::base::internal::Nil> { + public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node); + } +}; + +template +class Outputs + : public Tuple, Out, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil> { + public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node); + } +}; + +template +class Outputs + : public Tuple, Out, Out, + embb::base::internal::Nil, embb::base::internal::Nil> { + public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() && + this->template Get<2>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node) || + this->template Get<2>().HasCycle(node); + } +}; + +template +class Outputs + : public Tuple, Out, Out, + Out, embb::base::internal::Nil>{ + public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() && + this->template Get<2>().IsConnected() && + this->template Get<3>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node) || + this->template Get<2>().HasCycle(node) || + this->template Get<3>().HasCycle(node); + } +}; + +template +class Outputs + : public Tuple, Out, Out, + Out, Out > { + public: + bool IsFullyConnected() { + return this->template Get<0>().IsConnected() && + this->template Get<1>().IsConnected() && + this->template Get<2>().IsConnected() && + this->template Get<3>().IsConnected() && + this->template Get<4>().IsConnected(); + } + bool HasCycle(ClockListener * node) { + return this->template Get<0>().HasCycle(node) || + this->template Get<1>().HasCycle(node) || + this->template Get<2>().HasCycle(node) || + this->template Get<3>().HasCycle(node) || + this->template Get<4>().HasCycle(node); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_OUTPUTS_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/process.h a/dataflow_cpp/include/embb/dataflow/internal/process.h new file mode 100644 index 0000000..7f0f538 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/process.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_PROCESS_H_ +#define EMBB_DATAFLOW_INTERNAL_PROCESS_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template class Process; + +template < + bool Serial, + typename I1, typename I2, typename I3, typename I4, typename I5, + typename O1, typename O2, typename O3, typename O4, typename O5> +class Process< Serial, Inputs, + Outputs > + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef Outputs OutputsType; + typedef ProcessExecutor< InputsType, OutputsType > ExecutorType; + typedef typename ExecutorType::FunctionType FunctionType; + + Process(Scheduler * sched, FunctionType function) + : inputs_() + , executor_(function) + , action_(NULL) + , slices_(0) { + next_clock_ = 0; + queued_clock_ = 0; + bool ordered = Serial; + if (ordered) { + queue_id_ = GetNextProcessID(); + } else { + queue_id_ = 0; + } + inputs_.SetListener(this); + SetScheduler(sched); + } + + ~Process() { + if (NULL != action_) { + embb::base::Allocation::Free(action_); + } + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + executor_.Execute(clock, inputs_, outputs_); + } + + virtual bool IsFullyConnected() { + return inputs_.IsFullyConnected() && outputs_.IsFullyConnected(); + } + + virtual bool IsSequential() { + return Serial; + } + + virtual bool HasCycle() { + return outputs_.HasCycle(this); + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + template + T & operator >> (T & target) { + GetOutput<0>() >> target.template GetInput<0>(); + return target; + } + + virtual void OnClock(int clock) { + assert(inputs_.AreAtClock(clock)); + + bool ordered = Serial; + if (ordered) { + bool retry = true; + while (retry) { + int clk = next_clock_; + int clk_end = clk + slices_; + int clk_res = clk; + for (int ii = clk; ii < clk_end; ii++) { + if (!inputs_.AreAtClock(ii)) { + break; + } + clk_res++; + } + if (clk_res > clk) { + if (next_clock_.CompareAndSwap(clk, clk_res)) { + while (queued_clock_.Load() < clk) continue; + for (int ii = clk; ii < clk_res; ii++) { + const int idx = ii % slices_; + action_[idx] = Action(this, ii); + sched_->Enqueue(queue_id_, action_[idx]); + } + queued_clock_.Store(clk_res); + retry = false; + } + } else { + retry = false; + } + } + } else { + const int idx = clock % slices_; + action_[idx] = Action(this, clock); + sched_->Spawn(action_[idx]); + } + } + + virtual bool OnHasCycle(ClockListener * node) { + ClockListener * this_node = this; + if (this_node == node) { + return true; + } else { + return outputs_.HasCycle(node); + } + } + + private: + InputsType inputs_; + OutputsType outputs_; + ExecutorType executor_; + Action * action_; + embb::base::Atomic next_clock_; + embb::base::Atomic queued_clock_; + int queue_id_; + int slices_; + + virtual void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(action_); + action_ = NULL; + } + slices_ = slices; + inputs_.SetSlices(slices); + if (0 < slices_) { + action_ = reinterpret_cast( + embb::base::Allocation::Allocate( + sizeof(Action)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + action_[ii] = Action(); + } + } + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_PROCESS_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/process_executor.h a/dataflow_cpp/include/embb/dataflow/internal/process_executor.h new file mode 100644 index 0000000..5b82634 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/process_executor.h @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_PROCESS_EXECUTOR_H_ +#define EMBB_DATAFLOW_INTERNAL_PROCESS_EXECUTOR_H_ + +#include + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class ProcessExecutor; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + function_( + inputs.template Get<0>().GetValue(clock), + o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + O3 o3; + function_( + inputs.template Get<0>().GetValue(clock), + o1, o2, o3); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + outputs.template Get<2>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + O3 o3; + O4 o4; + function_( + inputs.template Get<0>().GetValue(clock), + o1, o2, o3, o4); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + outputs.template Get<3>().Send(Signal(clock, o4)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + outputs.template Get<2>().Send(Signal(clock)); + outputs.template Get<3>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + O3 o3; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + o1, o2, o3); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + outputs.template Get<2>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + inputs.template Get<3>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_PROCESS_EXECUTOR_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/scheduler.h a/dataflow_cpp/include/embb/dataflow/internal/scheduler.h new file mode 100644 index 0000000..dbf5556 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/scheduler.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SCHEDULER_H_ +#define EMBB_DATAFLOW_INTERNAL_SCHEDULER_H_ + +namespace embb { +namespace dataflow { +namespace internal { + +class Action; + +class Scheduler { + public: + Scheduler() {} + virtual ~Scheduler() {} + virtual void Spawn(Action & action) = 0; + virtual void Enqueue(int process_id, Action & action) = 0; + virtual void WaitForSlice(int slice) = 0; + virtual int GetSlices() = 0; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SCHEDULER_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h a/dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h new file mode 100644 index 0000000..4870767 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SCHEDULER_MTAPI_H_ +#define EMBB_DATAFLOW_INTERNAL_SCHEDULER_MTAPI_H_ + +#include +#include +#include +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class SchedulerMTAPI : public Scheduler { + public: + explicit SchedulerMTAPI(int slices) + : slices_(slices) { + embb::tasks::Node & node = embb::tasks::Node::GetInstance(); + + int tl = std::min( + static_cast(node.GetTaskLimit()), + static_cast(node.GetGroupCount())); + if (tl < slices_) { + slices_ = tl; + } + + group_ = reinterpret_cast( + embb::base::Allocation::Allocate( + sizeof(embb::tasks::Group*)*slices_)); + + for (int ii = 0; ii < slices_; ii++) { + embb::tasks::Group & group = node.CreateGroup(); + group_[ii] = &group; + } + + queue_count_ = std::min( + static_cast(node.GetQueueCount()), + static_cast(node.GetWorkerThreadCount()) ); + queue_ = reinterpret_cast( + embb::base::Allocation::Allocate( + sizeof(embb::tasks::Queue*)*queue_count_)); + + for (int ii = 0; ii < queue_count_; ii++) { + embb::tasks::Queue & queue = node.CreateQueue(0, true); + queue_[ii] = &queue; + } + } + virtual ~SchedulerMTAPI() { + if (embb::tasks::Node::IsInitialized()) { + // only destroy groups and queues if there still is an instance + embb::tasks::Node & node = embb::tasks::Node::GetInstance(); + for (int ii = 0; ii < slices_; ii++) { + group_[ii]->WaitAll(MTAPI_INFINITE); + node.DestroyGroup(*group_[ii]); + } + for (int ii = 0; ii < queue_count_; ii++) { + node.DestroyQueue(*queue_[ii]); + } + } + embb::base::Allocation::Free(group_); + embb::base::Allocation::Free(queue_); + } + virtual void Spawn(Action & action) { + const int idx = action.GetClock() % slices_; + group_[idx]->Spawn(embb::base::MakeFunction(action, &Action::RunMTAPI)); + } + virtual void Enqueue(int process_id, Action & action) { + const int idx = action.GetClock() % slices_; + const int queue_id = process_id % queue_count_; + queue_[queue_id]->Spawn(group_[idx], + embb::base::MakeFunction(action, &Action::RunMTAPI)); + } + virtual void WaitForSlice(int slice) { + group_[slice]->WaitAll(MTAPI_INFINITE); + } + virtual int GetSlices() { return slices_; } + + private: + embb::tasks::Group ** group_; + embb::tasks::Queue ** queue_; + int queue_count_; + int slices_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SCHEDULER_MTAPI_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h a/dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h new file mode 100644 index 0000000..c9c4b9d --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SCHEDULER_SEQUENTIAL_H_ +#define EMBB_DATAFLOW_INTERNAL_SCHEDULER_SEQUENTIAL_H_ + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class SchedulerSequential : public Scheduler { + public: + SchedulerSequential() {} + virtual ~SchedulerSequential() {} + virtual void Spawn(Action & action) { + action.RunSequential(); + } + virtual void Enqueue(int, Action & action) { + action.RunSequential(); + } + virtual void WaitForSlice(int /*slice*/) {} + virtual int GetSlices() { return 1; } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SCHEDULER_SEQUENTIAL_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/select.h a/dataflow_cpp/include/embb/dataflow/internal/select.h new file mode 100644 index 0000000..b9f3485 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/select.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SELECT_H_ +#define EMBB_DATAFLOW_INTERNAL_SELECT_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Select + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef Outputs OutputsType; + + explicit Select(Scheduler * sched) : inputs_(), slices_(0) { + inputs_.SetListener(this); + SetScheduler(sched); + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + if (GetInput<0>().GetSignal(clock).IsBlank()) { + GetOutput<0>().Send(Signal(clock)); + } else { + bool pred = GetInput<0>().GetValue(clock); + Type val; + if (pred) { + if (GetInput<1>().GetSignal(clock).IsBlank()) { + GetOutput<0>().Send(Signal(clock)); + } else { + val = GetInput<1>().GetValue(clock); + GetOutput<0>().Send(Signal(clock, val)); + } + } else { + if (GetInput<2>().GetSignal(clock).IsBlank()) { + GetOutput<0>().Send(Signal(clock)); + } else { + val = GetInput<2>().GetValue(clock); + GetOutput<0>().Send(Signal(clock, val)); + } + } + } + } + + virtual bool IsFullyConnected() { + return inputs_.IsFullyConnected() && outputs_.IsFullyConnected(); + } + + virtual bool OnHasCycle(ClockListener * node) { + ClockListener * this_node = this; + if (this_node == node) { + return true; + } else { + return outputs_.HasCycle(node); + } + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + template + T & operator >> (T & target) { + GetOutput<0>() >> target.template GetInput<0>(); + return target; + } + + virtual void OnClock(int clock) { + //const int idx = clock % Slices; + assert(inputs_.AreAtClock(clock)); + Run(clock); + } + + private: + InputsType inputs_; + OutputsType outputs_; + int slices_; + + virtual void SetSlices(int slices) { + slices_ = slices; + inputs_.SetSlices(slices); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SELECT_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/signal.h a/dataflow_cpp/include/embb/dataflow/internal/signal.h new file mode 100644 index 0000000..4931681 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/signal.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SIGNAL_H_ +#define EMBB_DATAFLOW_INTERNAL_SIGNAL_H_ + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Signal { + public: + Signal() : blank_(true), value_(), clock_(-1) {} + Signal(int clock, Type value) : blank_(false), value_(value), clock_(clock) {} + explicit Signal(int clock) : blank_(true), value_(), clock_(clock) {} + Signal(Signal const & other) + : blank_(other.blank_), value_(other.value_), clock_(other.clock_) {} + void operator = (Signal const & rhs) { + blank_ = rhs.blank_; + value_ = rhs.value_; + clock_ = rhs.clock_; + embb_atomic_memory_barrier(); + } + int GetClock() const { return clock_; } + bool IsBlank() const { return blank_; } + Type const & GetValue() const { return value_; } + void Clear() { + blank_ = true; + clock_ = -1; + } + + private: + bool blank_; + Type value_; + int clock_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SIGNAL_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/sink.h a/dataflow_cpp/include/embb/dataflow/internal/sink.h new file mode 100644 index 0000000..e690a99 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/sink.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SINK_H_ +#define EMBB_DATAFLOW_INTERNAL_SINK_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template class Sink; + +template < + typename I1, typename I2, typename I3, typename I4, typename I5> +class Sink< Inputs > + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef SinkExecutor< InputsType > ExecutorType; + typedef typename ExecutorType::FunctionType FunctionType; + + Sink(Scheduler * sched, ClockListener * listener, + FunctionType function) + : inputs_() + , executor_(function) + , action_(NULL) + , slices_(0) { + next_clock_ = 0; + queued_clock_ = 0; + queue_id_ = GetNextProcessID(); + inputs_.SetListener(this); + listener_ = listener; + SetScheduler(sched); + } + + ~Sink() { + if (NULL != action_) { + embb::base::Allocation::Free(action_); + } + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual void Run(int clock) { + if (inputs_.AreNoneBlank(clock)) { + executor_.Execute(clock, inputs_); + } + listener_->OnClock(clock); + } + + virtual bool IsFullyConnected() { + return inputs_.IsFullyConnected(); + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + virtual void OnClock(int clock) { + EMBB_UNUSED_IN_RELEASE(clock); + assert(inputs_.AreAtClock(clock)); + + bool retry = true; + while (retry) { + int clk = next_clock_; + int clk_end = clk + slices_; + int clk_res = clk; + for (int ii = clk; ii < clk_end; ii++) { + if (!inputs_.AreAtClock(ii)) { + break; + } + clk_res++; + } + if (clk_res > clk) { + if (next_clock_.CompareAndSwap(clk, clk_res)) { + while (queued_clock_.Load() < clk) continue; + for (int ii = clk; ii < clk_res; ii++) { + const int idx = ii % slices_; + action_[idx] = Action(this, ii); + sched_->Enqueue(queue_id_, action_[idx]); + } + queued_clock_.Store(clk_res); + retry = false; + } + } else { + retry = false; + } + } + } + + private: + InputsType inputs_; + ExecutorType executor_; + Action * action_; + ClockListener * listener_; + embb::base::Atomic next_clock_; + embb::base::Atomic queued_clock_; + int queue_id_; + int slices_; + + virtual void SetSlices(int slices) { + if (0 < slices_) { + embb::base::Allocation::Free(action_); + action_ = NULL; + } + slices_ = slices; + inputs_.SetSlices(slices); + if (0 < slices_) { + action_ = reinterpret_cast( + embb::base::Allocation::Allocate( + sizeof(Action)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + action_[ii] = Action(); + } + } + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SINK_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/sink_executor.h a/dataflow_cpp/include/embb/dataflow/internal/sink_executor.h new file mode 100644 index 0000000..619a32d --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/sink_executor.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SINK_EXECUTOR_H_ +#define EMBB_DATAFLOW_INTERNAL_SINK_EXECUTOR_H_ + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class SinkExecutor; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + inputs.template Get<3>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + inputs.template Get<3>().GetValue(clock), + inputs.template Get<4>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SINK_EXECUTOR_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/source.h a/dataflow_cpp/include/embb/dataflow/internal/source.h new file mode 100644 index 0000000..26d3734 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/source.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SOURCE_H_ +#define EMBB_DATAFLOW_INTERNAL_SOURCE_H_ + +#include + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template class Source; + +template < + typename O1, typename O2, typename O3, typename O4, typename O5> +class Source< Outputs > + : public Node { + public: + typedef Outputs OutputsType; + typedef SourceExecutor< OutputsType > ExecutorType; + typedef typename ExecutorType::FunctionType FunctionType; + + Source(Scheduler * sched, FunctionType function) + : executor_(function), not_done_(true) { + SetScheduler(sched); + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + not_done_ = executor_.Execute(clock, outputs_); + } + + virtual bool IsFullyConnected() { + return outputs_.IsFullyConnected(); + } + + virtual bool Start(int clock) { + if (not_done_) { + Run(clock); + } + return not_done_; + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + template + T & operator >> (T & target) { + GetOutput<0>() >> target.template GetInput<0>(); + return target; + } + + private: + OutputsType outputs_; + ExecutorType executor_; + embb::base::Atomic not_done_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SOURCE_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/source_executor.h a/dataflow_cpp/include/embb/dataflow/internal/source_executor.h new file mode 100644 index 0000000..309b9c9 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/source_executor.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SOURCE_EXECUTOR_H_ +#define EMBB_DATAFLOW_INTERNAL_SOURCE_EXECUTOR_H_ + +#include + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class SourceExecutor; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + bool Execute( + int clock, + Outputs & outputs) { + O1 o1; + bool result = function_(o1); + if (result) { + outputs.template Get<0>().Send(Signal(clock, o1)); + } + return result; + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + bool Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + bool result = function_(o1, o2); + if (result) { + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } + return result; + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + bool Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + O3 o3; + bool result = function_(o1, o2, o3); + if (result) { + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + } + return result; + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + bool Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + O3 o3; + O4 o4; + bool result = function_(o1, o2, o3, o4); + if (result) { + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + outputs.template Get<3>().Send(Signal(clock, o4)); + } + return result; + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + bool Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + O3 o3; + O4 o4; + O5 o5; + bool result = function_(o1, o2, o3, o4, o5); + if (result) { + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + outputs.template Get<3>().Send(Signal(clock, o4)); + outputs.template Get<4>().Send(Signal(clock, o5)); + } + return result; + } + + private: + FunctionType function_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SOURCE_EXECUTOR_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/switch.h a/dataflow_cpp/include/embb/dataflow/internal/switch.h new file mode 100644 index 0000000..e595742 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/switch.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_SWITCH_H_ +#define EMBB_DATAFLOW_INTERNAL_SWITCH_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Switch + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef Outputs OutputsType; + + explicit Switch(Scheduler * sched) : inputs_() { + inputs_.SetListener(this); + SetScheduler(sched); + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + if (inputs_.AreNoneBlank(clock)) { + bool pred = GetInput<0>().GetValue(clock); + Type val = GetInput<1>().GetValue(clock); + if (pred) { + // signal with value + GetOutput<0>().Send(Signal(clock, val)); + // blank signal + GetOutput<1>().Send(Signal(clock)); + } else { + // blank signal + GetOutput<0>().Send(Signal(clock)); + // signal with value + GetOutput<1>().Send(Signal(clock, val)); + } + } else { + GetOutput<0>().Send(Signal(clock)); + GetOutput<1>().Send(Signal(clock)); + } + } + + virtual bool IsFullyConnected() { + return inputs_.IsFullyConnected() && outputs_.IsFullyConnected(); + } + + virtual bool OnHasCycle(ClockListener * node) { + ClockListener * this_node = this; + if (this_node == node) { + return true; + } else { + return outputs_.HasCycle(node); + } + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + template + T & operator >> (T & target) { + GetOutput<0>() >> target.template GetInput<0>(); + return target; + } + + virtual void OnClock(int clock) { + //const int idx = clock % Slices; + assert(inputs_.AreAtClock(clock)); + Run(clock); + } + + private: + InputsType inputs_; + OutputsType outputs_; + + virtual void SetSlices(int slices) { + inputs_.SetSlices(slices); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SWITCH_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/tuple.h a/dataflow_cpp/include/embb/dataflow/internal/tuple.h new file mode 100644 index 0000000..c5f7981 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/tuple.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_TUPLE_H_ +#define EMBB_DATAFLOW_INTERNAL_TUPLE_H_ + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class TupleLeaf { + public: + typedef T Type; + + enum { index = Index }; + + enum { count = 1 }; + + TupleLeaf() : value_() {} + + TupleLeaf(TupleLeaf const & other) : value_(other.value_) {} + + void operator = (TupleLeaf const & other) { + value_ = other.value_; + } + + T const & Get() const { + return value_; + } + + T & Get() { + return value_; + } + + private: + T value_; +}; + +template +class TupleLeaf { + public: + typedef embb::base::internal::Nil Type; + + enum { index = Index }; + + enum { count = 0 }; + + embb::base::internal::Nil const & Get() const { + static embb::base::internal::Nil vv; + return vv; + } + + embb::base::internal::Nil & Get() { + static embb::base::internal::Nil vv; + return vv; + } +}; + +template < + typename T1 = embb::base::internal::Nil, + typename T2 = embb::base::internal::Nil, + typename T3 = embb::base::internal::Nil, + typename T4 = embb::base::internal::Nil, + typename T5 = embb::base::internal::Nil> +class Tuple : + public TupleLeaf<0, T1>, + public TupleLeaf<1, T2>, + public TupleLeaf<2, T3>, + public TupleLeaf<3, T4>, + public TupleLeaf<4, T5> { + public: + typedef typename MakeTypeList::Result Types; + typedef typename MakeTypeList< + TupleLeaf<0, T1>, + TupleLeaf<1, T2>, + TupleLeaf<2, T3>, + TupleLeaf<3, T4>, + TupleLeaf<4, T5> >::Result LeafTypes; + + size_t Size() const { + return + TupleLeaf<0, T1>::count + + TupleLeaf<1, T2>::count + + TupleLeaf<2, T3>::count + + TupleLeaf<3, T4>::count + + TupleLeaf<4, T5>::count; + } + + template + typename TypeAt< Types, Index >::Result & Get() { + typename TypeAt< LeafTypes, Index >::Result * leaf = + static_cast::Result *>(this); + return leaf->Get(); + } + + template + typename TypeAt< Types, Index >::Result const & Get() const { + typename TypeAt< LeafTypes, Index >::Result * leaf = + static_cast::Result *>(this); + return leaf->Get(); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_TUPLE_H_ diff --git b/dataflow_cpp/include/embb/dataflow/internal/typelist.h a/dataflow_cpp/include/embb/dataflow/internal/typelist.h new file mode 100644 index 0000000..a193909 --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/internal/typelist.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_INTERNAL_TYPELIST_H_ +#define EMBB_DATAFLOW_INTERNAL_TYPELIST_H_ + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +struct TypeList { + typedef T Head; + typedef U Tail; +}; + +template < + typename T1 = embb::base::internal::Nil, + typename T2 = embb::base::internal::Nil, + typename T3 = embb::base::internal::Nil, + typename T4 = embb::base::internal::Nil, + typename T5 = embb::base::internal::Nil> +struct MakeTypeList { + private: + typedef typename MakeTypeList::Result TailResult; + + public: + typedef TypeList Result; +}; + +template <> +struct MakeTypeList<> { + typedef embb::base::internal::Nil Result; +}; + +template struct TypeListLength; + +template <> struct TypeListLength { + enum { value = 0 }; +}; + +template +struct TypeListLength > { + enum { value = 1 + TypeListLength::value }; +}; + +template struct TypeAt; + +template +struct TypeAt< TypeList, 0> { + typedef Head Result; +}; + +template +struct TypeAt< TypeList, Index> { + typedef typename TypeAt::Result Result; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_TYPELIST_H_ diff --git b/dataflow_cpp/include/embb/dataflow/network.h a/dataflow_cpp/include/embb/dataflow/network.h new file mode 100644 index 0000000..4658bbb --- /dev/null +++ a/dataflow_cpp/include/embb/dataflow/network.h @@ -0,0 +1,968 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_DATAFLOW_NETWORK_H_ +#define EMBB_DATAFLOW_NETWORK_H_ + +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace embb { +namespace dataflow { + +#ifdef DOXYGEN + +/** + * Represents a set of processes, that are connected by communication channels. + * + * \ingroup CPP_DATAFLOW + */ +class Network { + public: + /** + * Constructs an empty network. + * \note The number of concurrent tokens will automatically be derived from + * the structure of the network on the first call to operator(), and the + * corresponding resources will be allocated then. + */ + Network() {} + + /** + * Constructs an empty network. + * \param slices Number of concurrent tokens allowed in the network. + */ + explicit Network(int slices) {} + + /** + * Input port class. + */ + template + class In { + }; + + /** + * Output port class. + */ + template + class Out { + public: + /** + * Input port class that can be connected to this output port. + */ + typedef In InType; + + /** + * Connects this output port to the input port \c input. + * If the input port already was connected to a different + * output an ErrorException is thrown. + * \param input The input port to connect to. + */ + void Connect(InType & input); + + /** + * Connects this output port to the input port \c input. + * If the input port already was connected to a different + * output an ErrorException is thrown. + * \param input The input port to connect to. + */ + void operator >> (InType & input); + }; + + /** + * Provides the input port types for a process. + * \tparam T1 Type of first port. + * \tparam T2 Optional type of second port. + * \tparam T3 Optional type of third port. + * \tparam T4 Optional type of fourth port. + * \tparam T5 Optional type of fifth port. + */ + template + struct Inputs { + /** + * Type list used to derive input port types from Index. + * \tparam Index The index of the input port type to query. + */ + template + struct Types { + /** + * Result of an input port type query. + * T_Index is T1 if Index is 0, T2 if Index is 1 and so on. + */ + typedef In Result; + }; + + /** + * \returns Reference to input port at Index. + */ + template + typename Types::Result & Get(); + }; + + /** + * Provides the output port types for a process. + * \tparam T1 Type of first port. + * \tparam T2 Optional type of second port. + * \tparam T3 Optional type of third port. + * \tparam T4 Optional type of fourth port. + * \tparam T5 Optional type of fifth port. + */ + template + struct Outputs { + /** + * Type list used to derive output port types from Index. + * \tparam Index The index of the output port type to query. + */ + template + struct Types { + /** + * Result of an output port type query. + * T_Index is T1 if Index is 0, T2 if Index is 1 and so on. + */ + typedef Out Result; + }; + + /** + * \returns Reference to output port at Index. + */ + template + typename Types::Result & Get(); + }; + + /** + * Generic serial process template. + * + * Implements a generic serial process in the network that may have one to + * four input ports and one to four output ports but no more that five total + * ports. + * Tokens are processed in order. + * + * \see Source, ParallelProcess, Sink, Switch, Select + * + * \tparam Inputs Inputs of the process. + * \tparam Outputs Outputs of the process. + */ + template + class SerialProcess { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function + FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a SerialProcess with a user specified processing function. + * \param network The network this node is going to be part of. + * \param function The Function to call to process a token. + */ + explicit SerialProcess(Network & network, FunctionType function); + + /** + * \returns \c true if the SerialProcess has any inputs, \c false + * otherwise. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns \c true if the SerialProcess has any outputs, \c false + * otherwise. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + + /** + * Connects output port 0 to input port 0 of \c target. + * \param target Process to connect to. + * \tparam T Type of target process. + */ + template + void operator >> (T & target); + }; + + /** + * Generic parallel process template. + * + * Implements a generic parallel process in the network that may have one to + * four input ports and one to four output ports but no more that five total + * ports. + * Tokens are processed as soon as all inputs for that token are complete. + * + * \see Source, SerialProcess, Sink, Switch, Select + * + * \tparam Inputs Inputs of the process. + * \tparam Outputs Outputs of the process. + */ + template + class ParallelProcess { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function + FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a ParallelProcess with a user specified processing function. + * \param network The network this node is going to be part of. + * \param function The Function to call to process a token. + */ + explicit ParallelProcess(Network & network, FunctionType function); + + /** + * \returns \c true if the ParallelProcess has any inputs, \c false + * otherwise. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns \c true if the ParallelProcess has any outputs, \c false + * otherwise. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + + /** + * Connects output port 0 to input port 0 of \c target. + * \param target Process to connect to. + * \tparam T Type of target process. + */ + template + void operator >> (T & target); + }; + + /** + * Switch process template. + * + * A switch has 2 inputs and 2 outputs. Input port 0 is of type boolean and + * selects to which output port the value of input port 1 of type \c Type + * is sent. If input port 0 is set to true the value goes to output port 0 + * and to output port 1 otherwise. + * Tokens are processed as soon as all inputs for that token are complete. + * + * \see Select + * + * \tparam Type The type of input port 1 and output port 0 and 1. + */ + template + class Switch { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a Switch process. + * \param network The network this node is going to be part of. + */ + explicit Select(Network & network); + + /** + * \returns Always \c true. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + + /** + * Connects output port 0 to input port 0 of \c target. + * \param target Process to connect to. + * \tparam T Type of target process. + */ + template + void operator >> (T & target); + }; + + /** + * Select process template. + * + * A select has 3 inputs and 1 output. Input port 0 is of type boolean and + * selects which of input port 1 or 2 (of type \c Type) is sent to output + * port 0 (of type \c Type). If input port 0 is set to true the value of + * input port 1 is selected, otherwise the value of input port 2 is taken. + * Tokens are processed as soon as all inputs for that token are complete. + * + * \see Switch + * + * \tparam Type The type of input port 1 and 2 and output port 0. + */ + template + class Select { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a Select process. + * \param network The network this node is going to be part of. + */ + explicit Select(Network & network); + + /** + * \returns Always \c true. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + + /** + * Connects output port 0 to input port 0 of \c target. + * \param target Process to connect to. + * \tparam T Type of target process. + */ + template + void operator >> (T & target); + }; + + /** + * Sink process template. + * + * A sink marks the end of a particular processing chain. It can have one to + * five input ports and no output ports. + * Tokens are processed in order by the sink, regardless in which order they + * arrive at the input ports. + * + * \see Source, SerialProcess, ParallelProcess + * + * \tparam I1 Type of first input port. + * \tparam I2 Optional type of second input port. + * \tparam I3 Optional type of third input port. + * \tparam I4 Optional type of fourth input port. + * \tparam I5 Optional type of fifth input port. + */ + template + class Sink { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Constructs a Sink with a user specified processing function. + * \param network The network this node is going to be part of. + * \param function The Function to call to process a token. + */ + explicit Sink(Network & network, FunctionType function); + + /** + * \returns Always \c true. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns Always \c false. + */ + virtual bool HasOutputs() const; + }; + + /** + * Source process template. + * + * A source marks the start of a processing chain. It can have one to five + * output ports and no input ports. + * Tokens are emitted in order by the source. + * + * \see SerialProcess, ParallelProcess, Sink + * + * \tparam O1 Type of first output port. + * \tparam O2 Optional type of second output port. + * \tparam O3 Optional type of third output port. + * \tparam O4 Optional type of fourth output port. + * \tparam O5 Optional type of fifth output port. + */ + template + class Source { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a Source with a user specified processing function. + * \param network The network this node is going to be part of. + * \param function The Function to call to emit a token. + */ + explicit Source(Network & network, FunctionType function); + + /** + * \returns Always \c false. + */ + virtual bool HasInputs() const; + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at INDEX. + */ + template + typename OutputsType::Types::Result & GetOutput(); + + /** + * Connects output port 0 to input port 0 of \c target. + * \param target Process to connect to. + * \tparam T Type of target process. + */ + template + void operator >> (T & target); + }; + + /** + * Constant source process template. + * + * A constant source has one output port and emits a constant value given + * at construction time for each token. + * + * \tparam Type The type of output port 0. + */ + template + class ConstantSource { + public: + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a ConstantSource with a value to emit on each token. + * \param network The network this node is going to be part of. + * \param value The value to emit. + */ + explicit ConstantSource(Network & network, Type value); + + /** + * \returns Always \c false. + */ + virtual bool HasInputs() const; + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + + /** + * Connects output port 0 to input port 0 of \c target. + * \param target Process to connect to. + * \tparam T Type of target process. + */ + template + void operator >> (T & target); + }; + + /** + * Checks whether the network is completely connected and free of cycles. + * \returns \c true if everything is in order, \c false if not. + * \note Executing an invalid network results in an exception. For this + * reason, it is recommended to first check the network using IsValid(). + */ + bool IsValid(); + + /** + * Executes the network until one of the the sources returns \c false. + * \note If the network was default constructed, the number of concurrent + * tokens will automatically be derived from the structure of the network + * on the first call of the operator, and the corresponding resources will + * be allocated then. + * \note Executing an invalid network results in an exception. For this + * reason, it is recommended to first check the network using IsValid(). + */ + void operator () (); +}; + +#else + +class Network : public internal::ClockListener { + public: + Network() + : sink_counter_(NULL), sink_count_(0), slices_(0), sched_(NULL) { + // empty + } + + explicit Network(int slices) + : sink_counter_(NULL), sink_count_(0), slices_(slices), sched_(NULL) { + PrepareSlices(); + } + + ~Network() { + if (NULL != sched_) { + embb::base::Allocation::Delete(sched_); + sched_ = NULL; + } + if (NULL != sink_counter_) { + embb::base::Allocation::Free(sink_counter_); + sink_counter_ = NULL; + } + } + + template + class Inputs { + // empty + }; + + template + class Outputs { + // empty + }; + + template class SerialProcess; + + template < + typename I1, typename I2, typename I3, typename I4, typename I5, + typename O1, typename O2, typename O3, typename O4, typename O5> + class SerialProcess< Inputs, + Outputs > + : public internal::Process< true, + internal::Inputs, + internal::Outputs > { + public: + typedef typename internal::Process< true, + internal::Inputs, + internal::Outputs >::FunctionType + FunctionType; + explicit SerialProcess(Network & network, FunctionType function) + : internal::Process< true, + internal::Inputs, + internal::Outputs >( + network.sched_, function) { + network.processes_.push_back(this); + } + }; + + template class ParallelProcess; + + template < + typename I1, typename I2, typename I3, typename I4, typename I5, + typename O1, typename O2, typename O3, typename O4, typename O5> + class ParallelProcess< Inputs, + Outputs > + : public internal::Process< false, + internal::Inputs, + internal::Outputs >{ + public: + typedef typename internal::Process< false, + internal::Inputs, + internal::Outputs >::FunctionType + FunctionType; + explicit ParallelProcess(Network & network, FunctionType function) + : internal::Process< false, + internal::Inputs, + internal::Outputs >( + network.sched_, function) { + network.processes_.push_back(this); + } + }; + + template + class Switch : public internal::Switch { + public: + explicit Switch(Network & network) + : internal::Switch(network.sched_) { + network.processes_.push_back(this); + } + }; + + template + class Select : public internal::Select { + public: + explicit Select(Network & network) + : internal::Select(network.sched_) { + network.processes_.push_back(this); + } + }; + + template + class Sink : public internal::Sink< + internal::Inputs > { + public: + typedef typename internal::Sink< + internal::Inputs >::FunctionType FunctionType; + + explicit Sink(Network & network, FunctionType function) + : internal::Sink< + internal::Inputs >( + network.sched_, &network, function) { + network.sinks_.push_back(this); + network.sink_count_++; + } + }; + + template + class Source : public internal::Source< + internal::Outputs > { + public: + typedef typename internal::Source< + internal::Outputs >::FunctionType + FunctionType; + + explicit Source(Network & network, FunctionType function) + : internal::Source< + internal::Outputs >(network.sched_, function) { + network.sources_.push_back(this); + } + }; + + template + class ConstantSource : public internal::ConstantSource { + public: + explicit ConstantSource(Network & network, Type value) + : internal::ConstantSource(network.sched_, value) { + network.sources_.push_back(this); + } + }; + + bool IsValid() { + bool valid = true; + // check connectivity + for (size_t ii = 0; ii < sources_.size() && valid; ii++) { + valid = valid && sources_[ii]->IsFullyConnected(); + } + for (size_t ii = 0; ii < processes_.size() && valid; ii++) { + valid = valid && processes_[ii]->IsFullyConnected(); + } + for (size_t ii = 0; ii < sinks_.size() && valid; ii++) { + valid = valid && sinks_[ii]->IsFullyConnected(); + } + // check for cycles + for (size_t ii = 0; ii < processes_.size() && valid; ii++) { + valid = valid && !processes_[ii]->HasCycle(); + } + return valid; + } + + void operator () () { + if (0 >= slices_) { + slices_ = static_cast( + sources_.size() + + sinks_.size()); + for (size_t ii = 0; ii < processes_.size(); ii++) { + int tt = processes_[ii]->IsSequential() ? 1 : + static_cast(embb_core_count_available()); + slices_ += tt; + } + PrepareSlices(); + for (size_t ii = 0; ii < sources_.size(); ii++) { + sources_[ii]->SetScheduler(sched_); + } + for (size_t ii = 0; ii < processes_.size(); ii++) { + processes_[ii]->SetScheduler(sched_); + } + for (size_t ii = 0; ii < sinks_.size(); ii++) { + sinks_[ii]->SetScheduler(sched_); + } + } + + int clock = 0; + while (clock >= 0) { + const int idx = clock % slices_; + while (sink_counter_[idx] > 0) embb::base::Thread::CurrentYield(); + sched_->WaitForSlice(idx); + if (!SpawnClock(clock)) + break; + clock++; + } + + int ii = clock - slices_ + 1; + if (ii < 0) ii = 0; + for (; ii < clock; ii++) { + const int idx = ii % slices_; + while (sink_counter_[idx] > 0) embb::base::Thread::CurrentYield(); + sched_->WaitForSlice(idx); + } + } + + /** + * Internal. + * \internal + * Gets called when a token has reached all sinks and frees up the + * corresponding slot, thus allowing a new token to be emitted. + */ + virtual void OnClock(int clock) { + const int idx = clock % slices_; + assert(sink_counter_[idx] > 0); + --sink_counter_[idx]; + } + + private: + std::vector processes_; + std::vector sources_; + std::vector sinks_; + embb::base::Atomic * sink_counter_; + int sink_count_; + int slices_; + internal::Scheduler * sched_; + +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + std::vector spawn_history_[Slices]; +#endif + + bool SpawnClock(int clock) { + const int idx = clock % slices_; + bool result = true; +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + spawn_history_[idx].push_back(clock); +#endif + sink_counter_[idx] = sink_count_; + for (size_t kk = 0; kk < sources_.size(); kk++) { + result &= sources_[kk]->Start(clock); + } + return result; + } + + void PrepareSlices() { + sched_ = embb::base::Allocation::New(slices_); + if (sched_->GetSlices() != slices_) { + slices_ = sched_->GetSlices(); + } + sink_counter_ = reinterpret_cast*>( + embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*slices_)); + for (int ii = 0; ii < slices_; ii++) { + sink_counter_[ii] = 0; + } + } +}; + +#endif // DOXYGEN + +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_NETWORK_H_ diff --git b/dataflow_cpp/src/node.cc a/dataflow_cpp/src/node.cc new file mode 100644 index 0000000..cc5e031 --- /dev/null +++ a/dataflow_cpp/src/node.cc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +int embb::dataflow::internal::Node::next_process_id_ = 0; diff --git b/dataflow_cpp/test/dataflow_cpp_test_simple.cc a/dataflow_cpp/test/dataflow_cpp_test_simple.cc new file mode 100644 index 0000000..c73a0f1 --- /dev/null +++ a/dataflow_cpp/test/dataflow_cpp_test_simple.cc @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include +#include + +#include + +#define NUM_SLICES 8 +#define TEST_COUNT 12 + +typedef embb::dataflow::Network MyNetwork; +typedef MyNetwork::ConstantSource< int > MyConstantSource; +typedef MyNetwork::Source< int > MySource; +typedef MyNetwork::SerialProcess< MyNetwork::Inputs, + MyNetwork::Outputs > MyPred; +typedef MyNetwork::ParallelProcess< MyNetwork::Inputs, + MyNetwork::Outputs > MyFilter; +typedef MyNetwork::ParallelProcess< MyNetwork::Inputs, + MyNetwork::Outputs > MyMult; +typedef MyNetwork::Sink< int > MySink; +typedef MyNetwork::Switch< int > MySwitch; +typedef MyNetwork::Select< int > MySelect; + +embb::base::Atomic source_counter; +int source_array[TEST_COUNT]; + +bool sourceFunc(int & out) { + if (source_counter < TEST_COUNT) { + out = source_counter; + + source_array[source_counter] = out; + source_counter++; + + return true; + } else { + return false; + } +} + +embb::base::Atomic pred_counter; +bool pred_array[TEST_COUNT]; + +void predFunc(int const & in, bool & out) { + out = (0 == (in % 2)); + + pred_array[pred_counter] = out; + pred_counter++; +} + +embb::base::Atomic filter_counter; +int filter_array[TEST_COUNT]; + +void filterFunc(int const &in, int & out) { + out = in + 1; + + filter_array[filter_counter] = out; + filter_counter++; +} + +embb::base::Atomic mult_counter; +int mult_array[TEST_COUNT]; + +void multFunc(int const & in_a, int const & in_b, int & out) { + out = in_a * in_b; + + mult_array[mult_counter] = out; + mult_counter++; +} + +template +class ArraySink { + private: + int values_[SIZE]; + int pos_; + + public: + ArraySink() { + Init(); + } + + void Print() const { + std::cout << values_[0]; + for (int ii = 1; ii < SIZE; ii++) { + std::cout << ", " << values_[ii]; + } + std::cout << std::endl; + } + + void Init() { + for (int ii = 0; ii < SIZE; ii++) { + values_[ii] = -1; + } + pos_ = 0; + } + + bool Check() const { + for (int ii = 0; ii < SIZE; ii++) { + int expected; + if (0 == (ii % 2)) + expected = ii + 1; + else + expected = ii * 4; + if (values_[ii] != expected) + return false; + } + return true; + } + + void Run(int const & in) { + values_[pos_] = in; + pos_++; + } +}; + +SimpleTest::SimpleTest() { + CreateUnit("dataflow_cpp simple test").Add(&SimpleTest::TestBasic, this); +} + +#define MTAPI_DOMAIN_ID 1 +#define MTAPI_NODE_ID 1 + +void SimpleTest::TestBasic() { + // All available cores + embb::base::CoreSet core_set(true); + embb::tasks::Node::Initialize( + MTAPI_DOMAIN_ID, + MTAPI_NODE_ID, + core_set, + 1024, // max tasks (default: 1024) + 128, // max groups (default: 128) + 2, // max queues (default: 16) + 1024, // queue capacity (default: 1024) + 4); // num priorities (default: 4) + + for (int ii = 0; ii < 10000; ii++) { + ArraySink asink; + MyNetwork network(NUM_SLICES); + MyConstantSource constant(network, 4); + MySource source(network, embb::base::MakeFunction(sourceFunc)); + MyFilter filter(network, embb::base::MakeFunction(filterFunc)); + MyMult mult(network, embb::base::MakeFunction(multFunc)); + MySink sink(network, + embb::base::MakeFunction(asink, &ArraySink::Run)); + MyPred pred(network, embb::base::MakeFunction(predFunc)); + MySwitch sw(network); + MySelect sel(network); + + for (int kk = 0; kk < TEST_COUNT; kk++) { + source_array[kk] = -1; + pred_array[kk] = false; + filter_array[kk] = -1; + mult_array[kk] = -1; + } + + source_counter = 0; + pred_counter = 0; + mult_counter = 0; + filter_counter = 0; + + filter.HasInputs(); + filter.HasOutputs(); + + source.GetOutput<0>() >> sw.GetInput<1>(); + + // connection chain representing the commented single connections below + source >> pred >> sw >> filter; + + //source.GetOutput<0>() >> pred.GetInput<0>(); + //pred.GetOutput<0>() >> sw.GetInput<0>(); + pred.GetOutput<0>() >> sel.GetInput<0>(); + + //sw.GetOutput<0>() >> filter.GetInput<0>(); + filter.GetOutput<0>() >> sel.GetInput<1>(); + + constant.GetOutput<0>() >> mult.GetInput<0>(); + sw.GetOutput<1>() >> mult.GetInput<1>(); + mult.GetOutput<0>() >> sel.GetInput<2>(); + + sel.GetOutput<0>() >> sink.GetInput<0>(); + + try { + if (!network.IsValid()) { + EMBB_THROW(embb::base::ErrorException, "network is invalid"); + } + network(); + } catch (embb::base::ErrorException & e) { + PT_ASSERT_MSG(false, e.What()); + } + + PT_EXPECT(asink.Check()); + } + + embb::tasks::Node::Finalize(); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} + diff --git b/dataflow_cpp/test/dataflow_cpp_test_simple.h a/dataflow_cpp/test/dataflow_cpp_test_simple.h new file mode 100644 index 0000000..d4fc9c1 --- /dev/null +++ a/dataflow_cpp/test/dataflow_cpp_test_simple.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_SIMPLE_H_ +#define DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_SIMPLE_H_ + +#include + +class SimpleTest : public partest::TestCase { + public: + SimpleTest(); + + private: + void TestBasic(); +}; + +#endif // DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_SIMPLE_H_ diff --git b/dataflow_cpp/test/dataflow_cpp_test_tuple.cc a/dataflow_cpp/test/dataflow_cpp_test_tuple.cc new file mode 100644 index 0000000..640b37f --- /dev/null +++ a/dataflow_cpp/test/dataflow_cpp_test_tuple.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +TupleTest::TupleTest() { + CreateUnit("dataflow_cpp tuple test").Add(&TupleTest::TestBasic, this); +} + +void TupleTest::TestBasic() { + embb::dataflow::internal::Tuple test; + test.Get<0>() = 1; + test.Get<1>() = "test"; +} diff --git b/dataflow_cpp/test/dataflow_cpp_test_tuple.h a/dataflow_cpp/test/dataflow_cpp_test_tuple.h new file mode 100644 index 0000000..3013d50 --- /dev/null +++ a/dataflow_cpp/test/dataflow_cpp_test_tuple.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_TUPLE_H_ +#define DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_TUPLE_H_ + +#include + +class TupleTest : public partest::TestCase { + public: + TupleTest(); + + private: + void TestBasic(); +}; + +#endif // DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_TUPLE_H_ diff --git b/dataflow_cpp/test/main.cc a/dataflow_cpp/test/main.cc new file mode 100644 index 0000000..7e09ac1 --- /dev/null +++ a/dataflow_cpp/test/main.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include + +PT_MAIN("Dataflow C++") { + PT_RUN(SimpleTest); + PT_RUN(TupleTest); +} diff --git b/doc/examples/CMakeLists.txt a/doc/examples/CMakeLists.txt new file mode 100644 index 0000000..31c07d2 --- /dev/null +++ a/doc/examples/CMakeLists.txt @@ -0,0 +1,53 @@ +project (project_embb_tutorials) + +file(GLOB_RECURSE EXAMPLES_SOURCES "*.cc" "*.h") + + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/ + ${CMAKE_CURRENT_BINARY_DIR}/ + ${CMAKE_CURRENT_SOURCE_DIR}/../../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/src + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_plugins_c/mtapi_network_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../tasks_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../../tasks_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../containers_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../algorithms_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../dataflow_cpp/include + ) + +if(BUILD_OPENCL_PLUGIN STREQUAL ON) + # used in source code, to include opencl code + add_definitions(-DEMBB_WITH_OPENCL) + # add opencl includes + include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_plugins_c/mtapi_opencl_c/include + ) + # later used, to link opencl to target... + set (EMBB_MTAPI_OPENCL_C_CONDITIONAL "embb_mtapi_opencl_c") +else() + # remove opencl examples from sources (should not be build) + file(GLOB_RECURSE EXAMPLES_SOURCES_OPENCL_TO_REMOVE "*opencl*" ) + list(REMOVE_ITEM EXAMPLES_SOURCES ${EXAMPLES_SOURCES_OPENCL_TO_REMOVE}) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "-std=c++11") + set (EXTRA_LIBS dl) +endif() + +IF(MSVC) + set (EXTRA_LIBS ws2_32) +ENDIF() + +add_executable(examples ${EXAMPLES_SOURCES}) +target_link_libraries(examples embb_dataflow_cpp embb_algorithms_cpp embb_tasks_cpp embb_mtapi_cpp + embb_mtapi_network_c ${EMBB_MTAPI_OPENCL_C_CONDITIONAL} embb_mtapi_c + embb_base_cpp embb_base_c embb_containers_cpp + ${EXTRA_LIBS} ${compiler_libs}) +CopyBin(BIN examples DEST ${local_install_dir}) diff --git b/doc/examples/algorithms/counting/count-snippet.h a/doc/examples/algorithms/counting/count-snippet.h new file mode 100644 index 0000000..9b3191d --- /dev/null +++ a/doc/examples/algorithms/counting/count-snippet.h @@ -0,0 +1,3 @@ +std::iterator_traits::difference_type count; +using embb::algorithms::Count; +count = Count(range, range + 8, 1); diff --git b/doc/examples/algorithms/counting/count_if-snippet.h a/doc/examples/algorithms/counting/count_if-snippet.h new file mode 100644 index 0000000..68806e8 --- /dev/null +++ a/doc/examples/algorithms/counting/count_if-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::CountIf; +count = CountIf(range, range + 8, + [](const int& value) -> bool { return value > 0; }); diff --git b/doc/examples/algorithms/counting/counting-fragmented.cc a/doc/examples/algorithms/counting/counting-fragmented.cc new file mode 100644 index 0000000..63a4846 --- /dev/null +++ a/doc/examples/algorithms/counting/counting-fragmented.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +/** + * Example using embb::algorithms::Count and CountIf. + * + * Counting elements of a range that fulfill certain properties. + */ +void RunCounting() { + #include "algorithms/counting/setup-snippet.h" + + #include "algorithms/counting/count-snippet.h" + assert(count == 2); + + #include "algorithms/counting/count_if-snippet.h" + assert(count == 6); +} diff --git b/doc/examples/algorithms/counting/setup-snippet.h a/doc/examples/algorithms/counting/setup-snippet.h new file mode 100644 index 0000000..f488cbd --- /dev/null +++ a/doc/examples/algorithms/counting/setup-snippet.h @@ -0,0 +1 @@ +int range[] = {0, 3, 2, 0, 1, 1, 3, 2}; diff --git b/doc/examples/algorithms/for_each/doubling-snippet.h a/doc/examples/algorithms/for_each/doubling-snippet.h new file mode 100644 index 0000000..9d16397 --- /dev/null +++ a/doc/examples/algorithms/for_each/doubling-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::ForEach; +ForEach(range.begin(), range.end(), + [] (int& to_double) { to_double *= 2; }); diff --git b/doc/examples/algorithms/for_each/doubling_zip-snippet.h a/doc/examples/algorithms/for_each/doubling_zip-snippet.h new file mode 100644 index 0000000..0e5ccc4 --- /dev/null +++ a/doc/examples/algorithms/for_each/doubling_zip-snippet.h @@ -0,0 +1,7 @@ +using embb::algorithms::Zip; +using embb::algorithms::ZipPair; +ForEach(Zip(input_range.begin(), output_range.begin()), + Zip(input_range.end(), output_range.end()), + [] (ZipPair pair) { + pair.Second() = pair.First() * 2; + }); diff --git b/doc/examples/algorithms/for_each/for_each-fragmented.cc a/doc/examples/algorithms/for_each/for_each-fragmented.cc new file mode 100644 index 0000000..99aeea4 --- /dev/null +++ a/doc/examples/algorithms/for_each/for_each-fragmented.cc @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +static void CheckResults(const std::vector& range) { + int i = 1; + for (const int& value : range) { + assert(value == i * 2); + EMBB_UNUSED_IN_RELEASE(value); + i++; + } +} + +/** + * Example using embb::algorithms::ForEach. + * + * Traversing and modifying a sequence with a for-each loop. + */ +void RunForEach() { + #include "stl_for_each/setup-snippet.h" + #include "algorithms/for_each/doubling-snippet.h" + CheckResults(range); + + #include "algorithms/for_each/setup_zip-snippet.h" + #include "algorithms/for_each/doubling_zip-snippet.h" + CheckResults(output_range); +} diff --git b/doc/examples/algorithms/for_each/setup_zip-snippet.h a/doc/examples/algorithms/for_each/setup_zip-snippet.h new file mode 100644 index 0000000..311fb4b --- /dev/null +++ a/doc/examples/algorithms/for_each/setup_zip-snippet.h @@ -0,0 +1,5 @@ +std::vector input_range(5); +for (size_t i=0; i < input_range.size(); i++) { + input_range[i] = static_cast(i) + 1; +} +std::vector output_range(5); diff --git b/doc/examples/algorithms/invoke/invocation-snippet.h a/doc/examples/algorithms/invoke/invocation-snippet.h new file mode 100644 index 0000000..2b27930 --- /dev/null +++ a/doc/examples/algorithms/invoke/invocation-snippet.h @@ -0,0 +1,2 @@ +using embb::algorithms::Invoke; +Invoke(WorkPackageA, WorkPackageB, WorkPackageC); diff --git b/doc/examples/algorithms/invoke/invoke-fragmented.cc a/doc/examples/algorithms/invoke/invoke-fragmented.cc new file mode 100644 index 0000000..925dbaa --- /dev/null +++ a/doc/examples/algorithms/invoke/invoke-fragmented.cc @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "algorithms/invoke/packages-snippet.h" + +static int a = 0, b = 0, c = 0; + +void WorkPackageA() { + a++; +} + +void WorkPackageB() { + b++; +} + +void WorkPackageC() { + c++; +} + +int* Partition(int* first, int* last) { + int* pivot = last - 1; + while (first != last) { + while (*first < *pivot) { + ++first; + if (first == last) return first; + } + do { + --last; + if (first == last) return first; + } while (*pivot < *last); + std::swap(*first, *last); + if(pivot == first) { + pivot = last; + } else if (pivot == last) { + pivot = first; + } + ++first; + } + return first; +} + +#include "algorithms/invoke/quick_sort-snippet.h" + +#include "algorithms/invoke/parallel_quick_sort-snippet.h" + +/** + * Example using embb::algorithms::ParallelInvoke() to execute work packages in + * parallel. + */ +void RunInvoke() { + #include "algorithms/invoke/invocation-snippet.h" + assert(a == 1); + assert(b == 1); + assert(c == 1); + + { + int range[] = {2, 5, 3, 1, 4}; + QuickSort(range, range + 5); + for (size_t i = 0; i < 5; i++) { + assert(range[i] == static_cast(i) + 1); + } + } + { + int range[] = {2, 5, 3, 1, 4}; + ParallelQuickSort(range, range + 5); + for (size_t i = 0; i < 5; i++) { + assert(range[i] == static_cast(i) + 1); + } + } +} + + + diff --git b/doc/examples/algorithms/invoke/packages-snippet.h a/doc/examples/algorithms/invoke/packages-snippet.h new file mode 100644 index 0000000..743db1d --- /dev/null +++ a/doc/examples/algorithms/invoke/packages-snippet.h @@ -0,0 +1,3 @@ +void WorkPackageA(); +void WorkPackageB(); +void WorkPackageC(); diff --git b/doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h a/doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h new file mode 100644 index 0000000..f518853 --- /dev/null +++ a/doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h @@ -0,0 +1,7 @@ +void ParallelQuickSort(int* first, int* last) { + if (last - first <= 1) return; + int* mid = Partition(first, last); + using embb::algorithms::Invoke; + Invoke([=](){ParallelQuickSort(first, mid);}, + [=](){ParallelQuickSort(mid, last);}); +} diff --git b/doc/examples/algorithms/invoke/quick_sort-snippet.h a/doc/examples/algorithms/invoke/quick_sort-snippet.h new file mode 100644 index 0000000..9d33c89 --- /dev/null +++ a/doc/examples/algorithms/invoke/quick_sort-snippet.h @@ -0,0 +1,6 @@ +void QuickSort(int* first, int* last) { + if (last - first <= 1) return; + int* mid = Partition(first, last); + QuickSort(first, mid); + QuickSort(mid, last); +} diff --git b/doc/examples/algorithms/reduce/dot_product-snippet.h a/doc/examples/algorithms/reduce/dot_product-snippet.h new file mode 100644 index 0000000..8d6bbf2 --- /dev/null +++ a/doc/examples/algorithms/reduce/dot_product-snippet.h @@ -0,0 +1,9 @@ +using embb::algorithms::Zip; +using embb::algorithms::ZipPair; +int dot_product = Reduce(Zip(range.begin(), second_range.begin()), + Zip(range.end(), second_range.end()), + 0, + std::plus(), + [](const ZipPair& pair) { + return pair.First() * pair.Second(); + }); diff --git b/doc/examples/algorithms/reduce/parallel-snippet.h a/doc/examples/algorithms/reduce/parallel-snippet.h new file mode 100644 index 0000000..b28ad55 --- /dev/null +++ a/doc/examples/algorithms/reduce/parallel-snippet.h @@ -0,0 +1,2 @@ +using embb::algorithms::Reduce; +sum = Reduce(range.begin(), range.end(), 0, std::plus()); diff --git b/doc/examples/algorithms/reduce/range_init-snippet.h a/doc/examples/algorithms/reduce/range_init-snippet.h new file mode 100644 index 0000000..e3a3ee3 --- /dev/null +++ a/doc/examples/algorithms/reduce/range_init-snippet.h @@ -0,0 +1,4 @@ +std::vector range(5); +for (size_t i = 0; i < range.size(); i++) { + range[i] = static_cast(i) + 1; +} diff --git b/doc/examples/algorithms/reduce/reduce-fragmented.cc a/doc/examples/algorithms/reduce/reduce-fragmented.cc new file mode 100644 index 0000000..5fad70c --- /dev/null +++ a/doc/examples/algorithms/reduce/reduce-fragmented.cc @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/** + * Example using embb::algorithms::Reduce. + * + * Summing up a range of values and more. + */ +void RunReduce() { + #include "algorithms/reduce/range_init-snippet.h" + + #include "algorithms/reduce/sequential-snippet.h" + assert(sum == 1 + 2 + 3 + 4 + 5); + + #include "algorithms/reduce/parallel-snippet.h" + assert(sum == 1 + 2 + 3 + 4 + 5); + + #include "algorithms/reduce/second_range_init-snippet.h" + #include "algorithms/reduce/dot_product-snippet.h" + assert(dot_product == 1*5 + 2*6 + 3*7 + 4*8 + 5*9); + EMBB_UNUSED_IN_RELEASE(dot_product); +} diff --git b/doc/examples/algorithms/reduce/second_range_init-snippet.h a/doc/examples/algorithms/reduce/second_range_init-snippet.h new file mode 100644 index 0000000..0c1f44e --- /dev/null +++ a/doc/examples/algorithms/reduce/second_range_init-snippet.h @@ -0,0 +1,4 @@ +std::vector second_range(5); +for (size_t i = 0; i < range.size(); i++) { + second_range[i] = static_cast(i) + 5; +} diff --git b/doc/examples/algorithms/reduce/sequential-snippet.h a/doc/examples/algorithms/reduce/sequential-snippet.h new file mode 100644 index 0000000..c5cca02 --- /dev/null +++ a/doc/examples/algorithms/reduce/sequential-snippet.h @@ -0,0 +1,4 @@ +int sum = 0; +for (size_t i = 0; i < range.size(); i++) { + sum += range[i]; +} diff --git b/doc/examples/algorithms/scan/prefix_sum-snippet.h a/doc/examples/algorithms/scan/prefix_sum-snippet.h new file mode 100644 index 0000000..38fae7d --- /dev/null +++ a/doc/examples/algorithms/scan/prefix_sum-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::Scan; +Scan(input_range.begin(), input_range.end(), output_range.begin(), + 0, std::plus()); diff --git b/doc/examples/algorithms/scan/scan-fragmented.cc a/doc/examples/algorithms/scan/scan-fragmented.cc new file mode 100644 index 0000000..cf1e84f --- /dev/null +++ a/doc/examples/algorithms/scan/scan-fragmented.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +void CheckResults(const std::vector& results) { + assert(results.size() == 5); + assert(results[0] == 1); + assert(results[1] == 3); + assert(results[2] == 6); + assert(results[3] == 10); + assert(results[4] == 15); + EMBB_UNUSED_IN_RELEASE(results); +} + +/** + * Example using embb::algorithms::Scan. + * + * + */ +void RunScan() { + #include "algorithms/scan/setup-snippet.h" + + #include "algorithms/scan/sequential_prefix_sum-snippet.h" + CheckResults(output_range); + + for (size_t i = 0; i < output_range.size(); i++) output_range[i] = 0; + #include "algorithms/scan/prefix_sum-snippet.h" + CheckResults(output_range); +} diff --git b/doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h a/doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h new file mode 100644 index 0000000..4f42a55 --- /dev/null +++ a/doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h @@ -0,0 +1,5 @@ +std::vector output_range(input_range.size()); +output_range[0] = input_range[0]; +for(size_t i = 1; i < input_range.size(); i++) { + output_range[i] = output_range[i-1] + input_range[i]; +} diff --git b/doc/examples/algorithms/scan/setup-snippet.h a/doc/examples/algorithms/scan/setup-snippet.h new file mode 100644 index 0000000..56c9517 --- /dev/null +++ a/doc/examples/algorithms/scan/setup-snippet.h @@ -0,0 +1,5 @@ +std::vector input_range(5); +for (size_t i = 0; i < input_range.size(); i++) { + input_range[i] = static_cast(i) + 1; +} + diff --git b/doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h a/doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h new file mode 100644 index 0000000..7f12c05 --- /dev/null +++ a/doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::MergeSort; +std::vector temporary_range(range.size()); +MergeSort(range.begin(), range.end(), temporary_range.begin()); diff --git b/doc/examples/algorithms/sorting/quick_sort-snippet.h a/doc/examples/algorithms/sorting/quick_sort-snippet.h new file mode 100644 index 0000000..a8eeb9b --- /dev/null +++ a/doc/examples/algorithms/sorting/quick_sort-snippet.h @@ -0,0 +1,2 @@ +using embb::algorithms::QuickSort; +QuickSort(range.begin(), range.end()); diff --git b/doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h a/doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h new file mode 100644 index 0000000..d33307d --- /dev/null +++ a/doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h @@ -0,0 +1 @@ +QuickSort(range.begin(), range.end(), std::greater()); diff --git b/doc/examples/algorithms/sorting/range_define-snippet.h a/doc/examples/algorithms/sorting/range_define-snippet.h new file mode 100644 index 0000000..f9eb29e --- /dev/null +++ a/doc/examples/algorithms/sorting/range_define-snippet.h @@ -0,0 +1 @@ +std::vector range; diff --git b/doc/examples/algorithms/sorting/sorting-fragmented.cc a/doc/examples/algorithms/sorting/sorting-fragmented.cc new file mode 100644 index 0000000..5872b63 --- /dev/null +++ a/doc/examples/algorithms/sorting/sorting-fragmented.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +/** + * Example using embb::algorithms::QuickSort and MergeSort. + * + * Sorting a range of values. + */ +void RunSorting() { + #include "algorithms/sorting/range_define-snippet.h" + range.push_back(4); + range.push_back(2); + range.push_back(3); + range.push_back(5); + range.push_back(1); + + #include "algorithms/sorting/quick_sort-snippet.h" + for (size_t i = 0; i < range.size(); i++) { + assert(range[i] == static_cast(i) + 1); + } + + #include "algorithms/sorting/quick_sort_custom_compare-snippet.h" + for (size_t i = 0; i < range.size(); i++) { + assert(range[i] == static_cast(range.size() - i)); + } + + #include "algorithms/sorting/merge_sort_preallocated-snippet.h" + for (size_t i = 0; i < range.size(); i++) { + assert(range[i] == static_cast(i) + 1); + } +} + + + diff --git b/doc/examples/containers/object_pool-fragmented.cc a/doc/examples/containers/object_pool-fragmented.cc new file mode 100644 index 0000000..2be5dd5 --- /dev/null +++ a/doc/examples/containers/object_pool-fragmented.cc @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +void RunObjectPoolExample1() +{ + #include "containers/object_pool-snippet.h" +} + +void RunObjectPoolExample2() +{ + #include "containers/object_pool_2-snippet.h" +} + +void RunObjectPoolExamples() +{ + RunObjectPoolExample1(); + RunObjectPoolExample2(); + +} diff --git b/doc/examples/containers/object_pool-snippet.h a/doc/examples/containers/object_pool-snippet.h new file mode 100644 index 0000000..15fe2b0 --- /dev/null +++ a/doc/examples/containers/object_pool-snippet.h @@ -0,0 +1,11 @@ +embb::containers::ObjectPool objPool(5); //@\label{lst:object_pool_lst1:line_create}@ + +int* alloc[5]; + +for (int i = 0; i != 5; ++i) { + alloc[i] = objPool.Allocate(); //@\label{lst:object_pool_lst1:line_allocate}@ +} + +for (int i = 0; i != 5; ++i) { + objPool.Free(alloc[i]); //@\label{lst:object_pool_lst1:line_free}@ +} \ No newline at end of file diff --git b/doc/examples/containers/object_pool_2-snippet.h a/doc/examples/containers/object_pool_2-snippet.h new file mode 100644 index 0000000..db87c42 --- /dev/null +++ a/doc/examples/containers/object_pool_2-snippet.h @@ -0,0 +1,2 @@ +embb::containers::ObjectPool> objPool(5); //@\label{lst:object_pool_lst2:line_create}@ diff --git b/doc/examples/containers/queues-fragmented.cc a/doc/examples/containers/queues-fragmented.cc new file mode 100644 index 0000000..89f3a73 --- /dev/null +++ a/doc/examples/containers/queues-fragmented.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +void RunQueueExample1() +{ + #include "containers/queues-snippet.h" +} + + +void RunQueueExamples() +{ + RunQueueExample1(); +} diff --git b/doc/examples/containers/queues-snippet.h a/doc/examples/containers/queues-snippet.h new file mode 100644 index 0000000..4e3a24b --- /dev/null +++ a/doc/examples/containers/queues-snippet.h @@ -0,0 +1,15 @@ +embb::containers::LockFreeMPMCQueue queue(10); //@\label{lst:queue_lst1:line_create}@ + +int i, j; +bool result = queue.TryDequeue(i); //@\label{lst:queue_lst1:fail_pop}@ +assert(result == false); + +for (i = 0; i <= 4; ++i) { //@\label{lst:queue_lst1:loop1}@ + result = queue.TryEnqueue(i); //@\label{lst:queue_lst1:push}@ + assert(result == true); +} + +for (i = 0; i <= 4; ++i) { //@\label{lst:queue_lst1:loop2}@ + result = queue.TryDequeue(j); //@\label{lst:queue_lst1:pop}@ + assert(result == true && i == j); //@\label{lst:queue_lst1:assert}@ +} \ No newline at end of file diff --git b/doc/examples/containers/stack-fragmented.cc a/doc/examples/containers/stack-fragmented.cc new file mode 100644 index 0000000..5da21d4 --- /dev/null +++ a/doc/examples/containers/stack-fragmented.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +void RunStackExample1() +{ + #include "containers/stack-snippet.h" +} + +void RunStackExamples() +{ + RunStackExample1(); +} diff --git b/doc/examples/containers/stack-snippet.h a/doc/examples/containers/stack-snippet.h new file mode 100644 index 0000000..057ebe5 --- /dev/null +++ a/doc/examples/containers/stack-snippet.h @@ -0,0 +1,15 @@ +embb::containers::LockFreeStack stack(10); //@\label{lst:stack_lst1:line_create}@ + +int i, j; +bool result = stack.TryPop(i); //@\label{lst:stack_lst1:fail_pop}@ +assert(result == false); + +for (i = 0; i <= 4; ++i) {//@\label{lst:stack_lst1:loop1}@ + result = stack.TryPush(i); //@\label{lst:stack_lst1:push}@ + assert(result == true); +} + +for (i = 4; i >= 0; --i) { //@\label{lst:stack_lst1:loop2}@ + result = stack.TryPop(j); //@\label{lst:stack_lst1:pop}@ + assert(result == true && i == j); //@\label{lst:stack_lst1:assert}@ +} \ No newline at end of file diff --git b/doc/examples/dataflow/dataflow_comparator-snippet.h a/doc/examples/dataflow/dataflow_comparator-snippet.h new file mode 100644 index 0000000..3630f18 --- /dev/null +++ a/doc/examples/dataflow/dataflow_comparator-snippet.h @@ -0,0 +1,8 @@ +template +class Comparator { +public: + void Run(const T& a, const T& b, T& x, T& y) { + x = std::min(a,b); + y = std::max(a,b); + } +}; diff --git b/doc/examples/dataflow/dataflow_connect-snippet.h a/doc/examples/dataflow/dataflow_connect-snippet.h new file mode 100644 index 0000000..7c7537e --- /dev/null +++ a/doc/examples/dataflow/dataflow_connect-snippet.h @@ -0,0 +1 @@ + read >> replace >> write; diff --git b/doc/examples/dataflow/dataflow_consumer-snippet.h a/doc/examples/dataflow/dataflow_consumer-snippet.h new file mode 100644 index 0000000..eae4054 --- /dev/null +++ a/doc/examples/dataflow/dataflow_consumer-snippet.h @@ -0,0 +1,9 @@ +template +class Consumer { +public: + void Run(const T& x1, const T& x2, const T& x3, const T& x4) { + if (x1 <= x2 && x2 <= x3 && x3 <= x4) { + // consume values + } + } +}; diff --git b/doc/examples/dataflow/dataflow_declare_add_sources-snippet.h a/doc/examples/dataflow/dataflow_declare_add_sources-snippet.h new file mode 100644 index 0000000..dd94cc0 --- /dev/null +++ a/doc/examples/dataflow/dataflow_declare_add_sources-snippet.h @@ -0,0 +1,19 @@ + Producer + producer1(1), + producer2(2), + producer3(3), + producer4(4); + + Network::Source + source1( + network, + embb::base::MakeFunction(producer1, &Producer::Run) ), + source2( + network, + embb::base::MakeFunction(producer2, &Producer::Run) ), + source3( + network, + embb::base::MakeFunction(producer3, &Producer::Run) ), + source4( + network, + embb::base::MakeFunction(producer4, &Producer::Run) ); diff --git b/doc/examples/dataflow/dataflow_declare_replace-snippet.h a/doc/examples/dataflow/dataflow_declare_replace-snippet.h new file mode 100644 index 0000000..4276ecb --- /dev/null +++ a/doc/examples/dataflow/dataflow_declare_replace-snippet.h @@ -0,0 +1,5 @@ + Network::ParallelProcess< + Network::Inputs, + Network::Outputs > replace( + network, embb::base::MakeFunction(ReplaceFunction) + ); diff --git b/doc/examples/dataflow/dataflow_declare_sink-snippet.h a/doc/examples/dataflow/dataflow_declare_sink-snippet.h new file mode 100644 index 0000000..3bf8a2d --- /dev/null +++ a/doc/examples/dataflow/dataflow_declare_sink-snippet.h @@ -0,0 +1,3 @@ + Network::Sink write( + network, embb::base::MakeFunction(SinkFunction) + ); diff --git b/doc/examples/dataflow/dataflow_declare_source-snippet.h a/doc/examples/dataflow/dataflow_declare_source-snippet.h new file mode 100644 index 0000000..4d028a8 --- /dev/null +++ a/doc/examples/dataflow/dataflow_declare_source-snippet.h @@ -0,0 +1,3 @@ + Network::Source read( + network, embb::base::MakeFunction(SourceFunction) + ); diff --git b/doc/examples/dataflow/dataflow_include-snippet.h a/doc/examples/dataflow/dataflow_include-snippet.h new file mode 100644 index 0000000..ca47769 --- /dev/null +++ a/doc/examples/dataflow/dataflow_include-snippet.h @@ -0,0 +1 @@ +#include diff --git b/doc/examples/dataflow/dataflow_linear-fragmented.cc a/doc/examples/dataflow/dataflow_linear-fragmented.cc new file mode 100644 index 0000000..a3d7ea4 --- /dev/null +++ a/doc/examples/dataflow/dataflow_linear-fragmented.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +// replace all occurrences of 'what' in 'str' with 'with' +void repl(std::string& str, const std::string &what, + const std::string& with) { + std::string::size_type pos = 0; + while ((pos = str.find(what, pos)) != std::string::npos) { + str.replace(pos, what.length(), with); + pos += with.length(); + } +} + +#include "dataflow/dataflow_include-snippet.h" + +std::stringstream file("hi world!\nhi there!\nhi hi!?\nhi!\n"); +std::string what("hi"); +std::string with("hello"); + +#include "dataflow/dataflow_network-snippet.h" + +#include "dataflow/dataflow_source_function-snippet.h" +#include "dataflow/dataflow_replace_function-snippet.h" +#include "dataflow/dataflow_sink_function-snippet.h" + +void RunDataflowLinear() { +#include "dataflow/dataflow_make-snippet.h" +#include "dataflow/dataflow_declare_source-snippet.h" +#include "dataflow/dataflow_declare_replace-snippet.h" +#include "dataflow/dataflow_declare_sink-snippet.h" +#include "dataflow/dataflow_connect-snippet.h" +#include "dataflow/dataflow_run-snippet.h" +} diff --git b/doc/examples/dataflow/dataflow_make-snippet.h a/doc/examples/dataflow/dataflow_make-snippet.h new file mode 100644 index 0000000..8e3ce7f --- /dev/null +++ a/doc/examples/dataflow/dataflow_make-snippet.h @@ -0,0 +1 @@ + Network network(4); diff --git b/doc/examples/dataflow/dataflow_network-snippet.h a/doc/examples/dataflow/dataflow_network-snippet.h new file mode 100644 index 0000000..de219d9 --- /dev/null +++ a/doc/examples/dataflow/dataflow_network-snippet.h @@ -0,0 +1 @@ +typedef embb::dataflow::Network Network; diff --git b/doc/examples/dataflow/dataflow_nonlinear-fragmented.cc a/doc/examples/dataflow/dataflow_nonlinear-fragmented.cc new file mode 100644 index 0000000..ed30ed0 --- /dev/null +++ a/doc/examples/dataflow/dataflow_nonlinear-fragmented.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef NDEBUG +#undef assert +#define assert(x) if(!x) std::cout << "assert failed." << std::endl +#endif + +static int SimpleRand(int & seed) { + const int a = 16807; + const int m = 2147483647; + seed = (a * seed) % m; + return seed % 100; +} + +#include "dataflow/dataflow_include-snippet.h" + +#include "dataflow/dataflow_producer-snippet.h" +#include "dataflow/dataflow_comparator-snippet.h" +#include "dataflow/dataflow_consumer-snippet.h" + +#include "dataflow/dataflow_network-snippet.h" + +void RunDataflowNonLinear() { +#include "dataflow/dataflow_make-snippet.h" +#include "dataflow/dataflow_declare_add_sources-snippet.h" + + Comparator comparator; + + Network::ParallelProcess< + Network::Inputs, Network::Outputs > + process1(network, + embb::base::MakeFunction(comparator, &Comparator::Run)), + process2(network, + embb::base::MakeFunction(comparator, &Comparator::Run)), + process3(network, + embb::base::MakeFunction(comparator, &Comparator::Run)), + process4(network, + embb::base::MakeFunction(comparator, &Comparator::Run)), + process5(network, + embb::base::MakeFunction(comparator, &Comparator::Run)); + + Consumer consumer; + + Network::Sink + sink1(network, embb::base::MakeFunction(consumer, &Consumer::Run)); + + source1.GetOutput<0>() >> process1.GetInput<0>(); + source2.GetOutput<0>() >> process2.GetInput<0>(); + source3.GetOutput<0>() >> process1.GetInput<1>(); + source4.GetOutput<0>() >> process2.GetInput<1>(); + + process1.GetOutput<0>() >> process3.GetInput<0>(); + process2.GetOutput<0>() >> process3.GetInput<1>(); + process1.GetOutput<1>() >> process4.GetInput<0>(); + process2.GetOutput<1>() >> process4.GetInput<1>(); + + process3.GetOutput<1>() >> process5.GetInput<0>(); + process4.GetOutput<0>() >> process5.GetInput<1>(); + + process3.GetOutput<0>() >> sink1.GetInput<0>(); + process5.GetOutput<0>() >> sink1.GetInput<1>(); + process5.GetOutput<1>() >> sink1.GetInput<2>(); + process4.GetOutput<1>() >> sink1.GetInput<3>(); + +#include "dataflow/dataflow_run-snippet.h" +} diff --git b/doc/examples/dataflow/dataflow_producer-snippet.h a/doc/examples/dataflow/dataflow_producer-snippet.h new file mode 100644 index 0000000..f76065c --- /dev/null +++ a/doc/examples/dataflow/dataflow_producer-snippet.h @@ -0,0 +1,19 @@ +template +class Producer { + public: + explicit Producer(int seed) : seed_(seed), count_(4) {} + bool Run(T& x) { + if (count_ >= 0) { + // produce a new value x + x = SimpleRand(seed_); + count_--; + return true; + } else { + return false; + } + } + + private: + int seed_; + int count_; +}; diff --git b/doc/examples/dataflow/dataflow_replace_function-snippet.h a/doc/examples/dataflow/dataflow_replace_function-snippet.h new file mode 100644 index 0000000..21e71d9 --- /dev/null +++ a/doc/examples/dataflow/dataflow_replace_function-snippet.h @@ -0,0 +1,4 @@ +void ReplaceFunction(std::string const & istr, std::string & ostr) { + ostr = istr; + repl(ostr, what, with); +} diff --git b/doc/examples/dataflow/dataflow_run-snippet.h a/doc/examples/dataflow/dataflow_run-snippet.h new file mode 100644 index 0000000..a0c3bfe --- /dev/null +++ a/doc/examples/dataflow/dataflow_run-snippet.h @@ -0,0 +1 @@ + network(); diff --git b/doc/examples/dataflow/dataflow_sink_function-snippet.h a/doc/examples/dataflow/dataflow_sink_function-snippet.h new file mode 100644 index 0000000..66896ea --- /dev/null +++ a/doc/examples/dataflow/dataflow_sink_function-snippet.h @@ -0,0 +1,3 @@ +void SinkFunction(std::string const & str) { + std::cout << str << std::endl; +} diff --git b/doc/examples/dataflow/dataflow_source_function-snippet.h a/doc/examples/dataflow/dataflow_source_function-snippet.h new file mode 100644 index 0000000..1afc660 --- /dev/null +++ a/doc/examples/dataflow/dataflow_source_function-snippet.h @@ -0,0 +1,8 @@ +bool SourceFunction(std::string & str) { + if (!file.eof()) { + std::getline(file, str); + return true; + } else { + return false; + } +} diff --git b/doc/examples/insert_snippets.py a/doc/examples/insert_snippets.py new file mode 100644 index 0000000..40d70bc --- /dev/null +++ a/doc/examples/insert_snippets.py @@ -0,0 +1,51 @@ +import os +import glob +import shutil +import sys +import string + +filenames = os.listdir(".") +# Work through all files and directories +for filename in filenames: + # If it is a directory, just add content to filenames + if os.path.isdir(filename): + nestedfiles = os.listdir(filename + "/") + for i in range(len(nestedfiles)): + nestedfiles[i] = filename + "/" + nestedfiles[i] + filenames.extend(nestedfiles) + # Otherwise look into file + else: + #sys.stdout.write("Looking at " + filename) + is_fragmented = string.find(filename, "-fragmented.cc") != -1 + if is_fragmented: + in_file = open(filename, 'r+') + out_filename = string.replace(filename, "-fragmented", "") + sys.stdout.write(filename + " -> " + out_filename + "\n") + out_file = open(out_filename, 'w') + for line in in_file: + stripped_line = string.lstrip(line) + # Check if first characters after whitespaces are include + is_include = string.find(stripped_line, "#include") == 0 + # Check if snippet keyword is contained in include + includes_snippet = string.find(line, "-snippet.") != -1 + if is_include and includes_snippet: + # Get number of whitespaces for indentation of snippet + num_whitespaces = len(line) - len(stripped_line) + # Create Whitespace-string for indentation + whitespaces = "" + for i in range(num_whitespaces): + whitespaces += " " + # Get include filename + first_quotes_pos = string.find(line, '"') + last_quotes_pos = string.rfind(line, '"') + snippet_filename = line[first_quotes_pos + 1 : last_quotes_pos] + sys.stdout.write(" + " + snippet_filename + "\n") + snippet_file = open(snippet_filename, 'r') + for snippet_line in snippet_file: + out_file.write(whitespaces + snippet_line) + snippet_file.close() + #out_file.write("\n") + else: + out_file.write(line) + in_file.close() + out_file.close() \ No newline at end of file diff --git b/doc/examples/main.cc a/doc/examples/main.cc new file mode 100644 index 0000000..80b9c3c --- /dev/null +++ a/doc/examples/main.cc @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +void RunMTAPI_C(); +void RunMTAPI_C_Plugin(); +void RunMTAPI_C_Network(); +#ifdef EMBB_WITH_OPENCL +void RunMTAPI_C_OpenCL(); +#endif +void RunMTAPI_CPP(); +void RunTasks(); +void RunDataflowLinear(); +void RunDataflowNonLinear(); +void RunSTLForEach(); +void RunForEach(); +void RunInvoke(); +void RunSorting(); +void RunReduce(); +void RunCounting(); +void RunScan(); +void RunObjectPoolExamples(); +void RunStackExamples(); +void RunQueueExamples(); + +/** + * Runs all examples and tests their correctness. + */ +int main() { + embb_thread_set_max_count(1024); + + std::cout << "Running examples ..." << std::endl; + + std::cout << "RunMTAPI_C() ..." << std::endl; + RunMTAPI_C(); + std::cout << "RunMTAPI_C() ... done" << std::endl; + + std::cout << "RunMTAPI_C_Plugin() ..." << std::endl; + RunMTAPI_C_Plugin(); + std::cout << "RunMTAPI_C_Plugin() ... done" << std::endl; + + std::cout << "RunMTAPI_C_Network() ..." << std::endl; + RunMTAPI_C_Network(); + std::cout << "RunMTAPI_C_Network() ... done" << std::endl; + +#ifdef EMBB_WITH_OPENCL + std::cout << "RunMTAPI_C_OpenCL() ..." << std::endl; + RunMTAPI_C_OpenCL(); + std::cout << "RunMTAPI_C_OpenCL() ... done" << std::endl; +#endif + + std::cout << "RunMTAPI_CPP() ..." << std::endl; + RunMTAPI_CPP(); + std::cout << "RunMTAPI_CPP() ... done" << std::endl; + + std::cout << "RunTasks() ..." << std::endl; + RunTasks(); + std::cout << "RunTasks() ... done" << std::endl; + + std::cout << "RunDataflowLinear() ..." << std::endl; + RunDataflowLinear(); + std::cout << "RunDataflowLinear() ... done" << std::endl; + + std::cout << "RunDataflowNonLinear() ..." << std::endl; + RunDataflowNonLinear(); + std::cout << "RunDataflowNonLinear() ... done" << std::endl; + + std::cout << "RunSTLForEach() ..." << std::endl; + RunSTLForEach(); + std::cout << "RunSTLForEach() ... done" << std::endl; + + std::cout << "RunForEach() ..." << std::endl; + RunForEach(); + std::cout << "RunForEach() ... done" << std::endl; + + std::cout << "RunInvoke() ..." << std::endl; + RunInvoke(); + std::cout << "RunInvoke() ... done" << std::endl; + + std::cout << "RunSorting() ... " << std::endl; + RunSorting(); + std::cout << "RunSorting() ... done" << std::endl; + + std::cout << "RunReduce() ... " << std::endl; + RunReduce(); + std::cout << "RunReduce() ... done" << std::endl; + + std::cout << "RunCounting() ..." << std::endl; + RunCounting(); + std::cout << "RunCounting() ... done" << std::endl; + /* + std::cout << "RunScan() ..." << std::endl; + RunScan(); + std::cout << "RunScan() ... done" << std::endl; + */ + std::cout << "RunObjectPoolExamples() ..." << std::endl; + RunObjectPoolExamples(); + std::cout << "RunObjectPoolExamples() ... done" << std::endl; + + std::cout << "RunStackExamples() ..." << std::endl; + RunStackExamples(); + std::cout << "RunStackExamples() ... done" << std::endl; + + std::cout << "RunQueueExamples() ..." << std::endl; + RunQueueExamples(); + std::cout << "RunQueueExamples() ... done" << std::endl; + + std::cout << "Running examples ... done" << std::endl; +} diff --git b/doc/examples/mtapi/mtapi_c-fragmented.cc a/doc/examples/mtapi/mtapi_c-fragmented.cc new file mode 100644 index 0000000..735d30a --- /dev/null +++ a/doc/examples/mtapi/mtapi_c-fragmented.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "mtapi/mtapi_check_status-snippet.h" +#include "mtapi/mtapi_c_domain_node_id-snippet.h" + +static +#include "mtapi/mtapi_c_action_signature-snippet.h" +#include "mtapi/mtapi_c_validate_arguments-snippet.h" +#include "mtapi/mtapi_c_validate_result_buffer-snippet.h" + /* calculate */ +#include "mtapi/mtapi_terminating_condition-snippet.h" + /* first recursive call spawned as task (x = fib(n - 1);) */ +#include "mtapi/mtapi_c_calc_task-snippet.h" + /* second recursive call can be called directly (y = fib(n - 2);) */ +#include "mtapi/mtapi_c_calc_direct-snippet.h" + /* wait for completion */ +#include "mtapi/mtapi_c_wait_task-snippet.h" + /* add the two preceeding numbers */ +#include "mtapi/mtapi_write_back-snippet.h" + +static int fibonacci(int n) { +#include "mtapi/mtapi_c_initialize-snippet.h" +#include "mtapi/mtapi_c_register_action-snippet.h" +#include "mtapi/mtapi_c_start_task-snippet.h" + /* wait for task completion */ + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); +#include "mtapi/mtapi_c_finalize-snippet.h" + return result; +} + +void RunMTAPI_C() { + /* run calculation */ + int result; + result = fibonacci(6); + + /* print result */ + printf("result: %i\n", result); +} diff --git b/doc/examples/mtapi/mtapi_c_action_signature-snippet.h a/doc/examples/mtapi/mtapi_c_action_signature-snippet.h new file mode 100644 index 0000000..80b8a4f --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_action_signature-snippet.h @@ -0,0 +1,9 @@ +void fibonacciActionFunction( + const void* args, + mtapi_size_t arg_size, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context + ) { diff --git b/doc/examples/mtapi/mtapi_c_calc_direct-snippet.h a/doc/examples/mtapi/mtapi_c_calc_direct-snippet.h new file mode 100644 index 0000000..525f7be --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_calc_direct-snippet.h @@ -0,0 +1,7 @@ + int b = n - 2; + int y; + fibonacciActionFunction( + &b, sizeof(int), + &y, sizeof(int), + MTAPI_NULL, 0, + task_context); diff --git b/doc/examples/mtapi/mtapi_c_calc_task-snippet.h a/doc/examples/mtapi/mtapi_c_calc_task-snippet.h new file mode 100644 index 0000000..62b352a --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_calc_task-snippet.h @@ -0,0 +1,15 @@ + int a = n - 1; + int x; + mtapi_task_hndl_t task = mtapi_task_start( + MTAPI_TASK_ID_NONE, /* optional task ID */ + fibonacciJob, /* job */ + (void*)&a, /* arguments passed to action + functions */ + sizeof(int), /* size of arguments */ + (void*)&x, /* result buffer */ + sizeof(int), /* size of result buffer */ + MTAPI_DEFAULT_TASK_ATTRIBUTES, /* task attributes */ + MTAPI_GROUP_NONE, /* optional task group */ + &status /* status out - parameter */ + ); + MTAPI_CHECK_STATUS(status); diff --git b/doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h a/doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h new file mode 100644 index 0000000..b1850d0 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h @@ -0,0 +1,5 @@ +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#define FIBONACCI_JOB 1 +static mtapi_job_hndl_t fibonacciJob; diff --git b/doc/examples/mtapi/mtapi_c_finalize-snippet.h a/doc/examples/mtapi/mtapi_c_finalize-snippet.h new file mode 100644 index 0000000..21c5bfa --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_finalize-snippet.h @@ -0,0 +1,7 @@ + /* delete action */ + mtapi_action_delete(fibonacciAction, 100, &status); + MTAPI_CHECK_STATUS(status); + + /* finalize the node */ + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); diff --git b/doc/examples/mtapi/mtapi_c_initialize-snippet.h a/doc/examples/mtapi/mtapi_c_initialize-snippet.h new file mode 100644 index 0000000..1269342 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_initialize-snippet.h @@ -0,0 +1,25 @@ + mtapi_status_t status; + + /* initialize node attributes to default values */ + mtapi_node_attributes_t node_attr; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + /* set node type to SMP */ + mtapi_nodeattr_set( + &node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + /* initialize the node */ + mtapi_info_t info; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); diff --git b/doc/examples/mtapi/mtapi_c_network-fragmented.cc a/doc/examples/mtapi/mtapi_c_network-fragmented.cc new file mode 100644 index 0000000..0cc5026 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_network-fragmented.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "mtapi/mtapi_network_c_header-snippet.h" +#include + +#include "mtapi/mtapi_check_status-snippet.h" + +#define NETWORK_DOMAIN 1 +#define NETWORK_LOCAL_NODE 3 +#define NETWORK_LOCAL_JOB 3 +#define NETWORK_REMOTE_NODE 3 +#define NETWORK_REMOTE_JOB 4 + +#include "mtapi/mtapi_network_c_action_function-snippet.h" + +void RunMTAPI_C_Network() { + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_action_hndl_t network_action, local_action; + + const int kElements = 64; + float arguments[kElements * 2]; + float results[kElements]; + + for (int ii = 0; ii < kElements; ii++) { + arguments[ii] = static_cast(ii); + arguments[ii + kElements] = static_cast(ii); + } + + mtapi_initialize( + NETWORK_DOMAIN, + NETWORK_LOCAL_NODE, + MTAPI_DEFAULT_NODE_ATTRIBUTES, + MTAPI_NULL, + &status); + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_network_c_plugin_initialize-snippet.h" + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_network_c_remote_action_create-snippet.h" + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_network_c_local_action_create-snippet.h" + MTAPI_CHECK_STATUS(status); + + job = mtapi_job_get(NETWORK_LOCAL_JOB, NETWORK_DOMAIN, &status); + MTAPI_CHECK_STATUS(status); + + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + arguments, kElements * 2 * sizeof(float), + results, kElements*sizeof(float), + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + for (int ii = 0; ii < kElements; ii++) { + if (results[ii] != ii * 2 + 1) { + printf("result %d was expected to be %d but was %f.\n", + ii, ii * 2 + 1, results[ii]); + exit(MTAPI_ERR_UNKNOWN); + } + } + +#include "mtapi/mtapi_network_c_plugin_finalize-snippet.h" + MTAPI_CHECK_STATUS(status); + + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); +} diff --git b/doc/examples/mtapi/mtapi_c_opencl-fragmented.cc a/doc/examples/mtapi/mtapi_c_opencl-fragmented.cc new file mode 100644 index 0000000..ea0f67b --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_opencl-fragmented.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "mtapi/mtapi_opencl_c_header-snippet.h" + +#include "mtapi/mtapi_check_status-snippet.h" + +#define OPENCL_DOMAIN 1 +#define OPENCL_NODE 2 +#define OPENCL_JOB 2 + +// OpenCL Kernel Function for element by element vector addition +#include "mtapi/mtapi_opencl_c_kernel-snippet.h" + +void RunMTAPI_C_OpenCL() { + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_action_hndl_t action; + + const int kElements = 64; + float arguments[kElements * 2]; + float results[kElements]; + + for (int ii = 0; ii < kElements; ii++) { + arguments[ii] = static_cast(ii); + arguments[ii + kElements] = static_cast(ii); + } + + mtapi_initialize( + OPENCL_DOMAIN, + OPENCL_NODE, + MTAPI_DEFAULT_NODE_ATTRIBUTES, + MTAPI_NULL, + &status); + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_opencl_c_plugin_initialize-snippet.h" + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_opencl_c_action_create-snippet.h" + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(OPENCL_JOB, OPENCL_DOMAIN, &status); + MTAPI_CHECK_STATUS(status); + + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + arguments, kElements * 2 * sizeof(float), + results, kElements*sizeof(float), + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + for (int ii = 0; ii < kElements; ii++) { + if (results[ii] != ii * 2 + 1) { + printf("result %d was expected to be %d but was %f.\n", + ii, ii * 2 + 1, results[ii]); + exit(MTAPI_ERR_UNKNOWN); + } + } + +#include "mtapi/mtapi_opencl_c_plugin_finalize-snippet.h" + MTAPI_CHECK_STATUS(status); + + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); +} diff --git b/doc/examples/mtapi/mtapi_c_plugin-fragmented.cc a/doc/examples/mtapi/mtapi_c_plugin-fragmented.cc new file mode 100644 index 0000000..bbbcc70 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin-fragmented.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define PLUGIN_DOMAIN_ID 1 +#define PLUGIN_NODE_ID 1 +#define PLUGIN_JOB_ID 1 + +#include "mtapi/mtapi_check_status-snippet.h" + +#include "mtapi/mtapi_c_plugin_task_schedule-snippet.h" + +#include "mtapi/mtapi_c_plugin_task_start_cb-snippet.h" + +#include "mtapi/mtapi_c_plugin_task_cancel_cb-snippet.h" + +#include "mtapi/mtapi_c_plugin_action_finalize_cb-snippet.h" + +void RunMTAPI_C_Plugin() { + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_action_hndl_t action; + mtapi_task_hndl_t task; + + mtapi_initialize( + PLUGIN_DOMAIN_ID, + PLUGIN_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, + MTAPI_NULL, + &status); + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_c_plugin_action_create-snippet.h" + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_c_plugin_get_job-snippet.h" + MTAPI_CHECK_STATUS(status); + +#include "mtapi/mtapi_c_plugin_task_start-snippet.h" + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); +} diff --git b/doc/examples/mtapi/mtapi_c_plugin_action_create-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_action_create-snippet.h new file mode 100644 index 0000000..2ec43dd --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_action_create-snippet.h @@ -0,0 +1,10 @@ +action = mtapi_ext_plugin_action_create( + PLUGIN_JOB_ID, + plugin_task_start, + plugin_task_cancel, + plugin_action_finalize, + MTAPI_NULL, + MTAPI_NULL, + 0, + MTAPI_DEFAULT_ACTION_ATTRIBUTES, + &status); diff --git b/doc/examples/mtapi/mtapi_c_plugin_action_finalize_cb-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_action_finalize_cb-snippet.h new file mode 100644 index 0000000..7c44e30 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_action_finalize_cb-snippet.h @@ -0,0 +1,8 @@ +void plugin_action_finalize( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status + ) { + EMBB_UNUSED(action); + // nothing to do for tearing down the plugin action + mtapi_status_set(status, MTAPI_SUCCESS); +} diff --git b/doc/examples/mtapi/mtapi_c_plugin_get_job-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_get_job-snippet.h new file mode 100644 index 0000000..9e97299 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_get_job-snippet.h @@ -0,0 +1,4 @@ + job = mtapi_job_get( + PLUGIN_JOB_ID, + PLUGIN_DOMAIN_ID, + &status); diff --git b/doc/examples/mtapi/mtapi_c_plugin_task_cancel_cb-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_task_cancel_cb-snippet.h new file mode 100644 index 0000000..2a62c5d --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_task_cancel_cb-snippet.h @@ -0,0 +1,8 @@ +void plugin_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status + ) { + EMBB_UNUSED(task); + // nothing to cancel in this simple example + mtapi_status_set(status, MTAPI_SUCCESS); +} diff --git b/doc/examples/mtapi/mtapi_c_plugin_task_schedule-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_task_schedule-snippet.h new file mode 100644 index 0000000..a180edc --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_task_schedule-snippet.h @@ -0,0 +1,11 @@ +void plugin_task_schedule(embb_mtapi_task_t* local_task) { + // here the task might be dispatched to some hardware or separate thread + + // mark the task as running + embb_mtapi_task_set_state(local_task, MTAPI_TASK_RUNNING); + + // nothing to do to execute the no-op task + + // just mark the task as done + embb_mtapi_task_set_state(local_task, MTAPI_TASK_COMPLETED); +} diff --git b/doc/examples/mtapi/mtapi_c_plugin_task_start-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_task_start-snippet.h new file mode 100644 index 0000000..344330a --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_task_start-snippet.h @@ -0,0 +1,8 @@ + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + MTAPI_NULL, 0, + MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); diff --git b/doc/examples/mtapi/mtapi_c_plugin_task_start_cb-snippet.h a/doc/examples/mtapi/mtapi_c_plugin_task_start_cb-snippet.h new file mode 100644 index 0000000..8e4d063 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_plugin_task_start_cb-snippet.h @@ -0,0 +1,31 @@ +void plugin_task_start( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + // do we have a node? + if (embb_mtapi_node_is_initialized()) { + // get the node instance + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + // is this a valid task? + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + // get the tasks storage + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + // dispatch the task + plugin_task_schedule(local_task); + + local_status = MTAPI_SUCCESS; + } + else { + local_status = MTAPI_ERR_TASK_INVALID; + } + } + else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git b/doc/examples/mtapi/mtapi_c_register_action-snippet.h a/doc/examples/mtapi/mtapi_c_register_action-snippet.h new file mode 100644 index 0000000..a05aaa7 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_register_action-snippet.h @@ -0,0 +1,17 @@ + /* create action */ + mtapi_action_hndl_t fibonacciAction; + fibonacciAction = mtapi_action_create( + FIBONACCI_JOB, /* action ID, defined by the + application */ + (fibonacciActionFunction), /* action function */ + MTAPI_NULL, /* no shared data */ + 0, /* length of shared data */ + MTAPI_DEFAULT_ACTION_ATTRIBUTES, /* action attributes */ + &status /* status out - parameter */ + ); + MTAPI_CHECK_STATUS(status); + + /* get job */ + mtapi_task_hndl_t task; + fibonacciJob = mtapi_job_get(FIBONACCI_JOB, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); diff --git b/doc/examples/mtapi/mtapi_c_start_task-snippet.h a/doc/examples/mtapi/mtapi_c_start_task-snippet.h new file mode 100644 index 0000000..79d005e --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_start_task-snippet.h @@ -0,0 +1,15 @@ + /* start task */ + int result; + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, /* optional task ID */ + fibonacciJob, /* job */ + (void*)&n, /* arguments passed to action + functions */ + sizeof(int), /* size of arguments */ + (void*)&result, /* result buffer */ + sizeof(int), /* size of result buffer */ + MTAPI_DEFAULT_TASK_ATTRIBUTES, /* task attributes */ + MTAPI_GROUP_NONE, /* optional task group */ + &status /* status out - parameter */ + ); + MTAPI_CHECK_STATUS(status); diff --git b/doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h a/doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h new file mode 100644 index 0000000..44246ab --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h @@ -0,0 +1,12 @@ + /* check size of arguments (in this case we only expect one int + value)*/ + mtapi_status_t status; + if (arg_size != sizeof(int)) { + printf("wrong size of arguments\n"); + mtapi_context_status_set(task_context, MTAPI_ERR_ARG_SIZE, &status); + MTAPI_CHECK_STATUS(status); + return; + } + + /* cast arguments to the desired type */ + int n = *(int*)args; diff --git b/doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h a/doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h new file mode 100644 index 0000000..24e7630 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h @@ -0,0 +1,21 @@ + /* if the caller is not interested in results, result_buffer may be + MTAPI_NULL. Of course, this depends on the application */ + int* result = MTAPI_NULL; + if (result_buffer == MTAPI_NULL) { + mtapi_context_status_set( + task_context, MTAPI_ERR_RESULT_SIZE, &status); + MTAPI_CHECK_STATUS(status); + } else { + /* if results are expected by the caller, check result buffer + size... */ + if (result_buffer_size == sizeof(int)) { + /* ... and cast the result buffer */ + result = (int*)result_buffer; + } else { + printf("wrong size of result buffer\n"); + mtapi_context_status_set( + task_context, MTAPI_ERR_RESULT_SIZE, &status); + MTAPI_CHECK_STATUS(status); + return; + } + } diff --git b/doc/examples/mtapi/mtapi_c_wait_task-snippet.h a/doc/examples/mtapi/mtapi_c_wait_task-snippet.h new file mode 100644 index 0000000..8c3e348 --- /dev/null +++ a/doc/examples/mtapi/mtapi_c_wait_task-snippet.h @@ -0,0 +1,2 @@ + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); diff --git b/doc/examples/mtapi/mtapi_check_status-snippet.h a/doc/examples/mtapi/mtapi_check_status-snippet.h new file mode 100644 index 0000000..bf1d1d3 --- /dev/null +++ a/doc/examples/mtapi/mtapi_check_status-snippet.h @@ -0,0 +1,8 @@ +#include +#include +#include +#define MTAPI_CHECK_STATUS(status) \ +if (MTAPI_SUCCESS != status) { \ + printf("...error %d\n\n", status); \ + exit(status); \ +} diff --git b/doc/examples/mtapi/mtapi_cpp-fragmented.cc a/doc/examples/mtapi/mtapi_cpp-fragmented.cc new file mode 100644 index 0000000..427039e --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp-fragmented.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "mtapi/mtapi_check_status-snippet.h" +#include "mtapi/mtapi_cpp_domain_node_id-snippet.h" + +static +#include "mtapi/mtapi_c_action_signature-snippet.h" +#include "mtapi/mtapi_c_validate_arguments-snippet.h" +#include "mtapi/mtapi_c_validate_result_buffer-snippet.h" + /* get the node instance */ +#include "mtapi/mtapi_cpp_get_node-snippet.h" + /* calculate */ +#include "mtapi/mtapi_terminating_condition-snippet.h" + /* first recursive call spawned as task (x = fib(n - 1);) */ +#include "mtapi/mtapi_cpp_calc_task-snippet.h" + /* second recursive call can be called directly (y = fib(n - 2);) */ +#include "mtapi/mtapi_c_calc_direct-snippet.h" + /* wait for completion */ +#include "mtapi/mtapi_cpp_wait_task-snippet.h" + /* add the two preceding numbers */ +#include "mtapi/mtapi_write_back-snippet.h" + +static +int fibonacci(int n) { +#include "mtapi/mtapi_cpp_initialize-snippet.h" +#include "mtapi/mtapi_cpp_register_action-snippet.h" +#include "mtapi/mtapi_cpp_get_node-snippet.h" + /* start calculation */ +#include "mtapi/mtapi_cpp_start_task-snippet.h" + /* wait for task completion */ + mtapi_status_t status = task.Wait(MTAPI_INFINITE); + MTAPI_CHECK_STATUS(status); +#include "mtapi/mtapi_cpp_finalize-snippet.h" + return result; +} + +void RunMTAPI_CPP() { +#include "mtapi/mtapi_cpp_main-snippet.h" +} diff --git b/doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h a/doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h new file mode 100644 index 0000000..aad4e1e --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h @@ -0,0 +1,3 @@ + int a = n - 1; + int x; + embb::mtapi::Task task = node.Start(fibonacciJob, &a, &x); diff --git b/doc/examples/mtapi/mtapi_cpp_domain_node_id-snippet.h a/doc/examples/mtapi/mtapi_cpp_domain_node_id-snippet.h new file mode 100644 index 0000000..747609d --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_domain_node_id-snippet.h @@ -0,0 +1,5 @@ +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#define FIBONACCI_JOB 1 +static embb::mtapi::Job fibonacciJob; diff --git b/doc/examples/mtapi/mtapi_cpp_finalize-snippet.h a/doc/examples/mtapi/mtapi_cpp_finalize-snippet.h new file mode 100644 index 0000000..5061b36 --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_finalize-snippet.h @@ -0,0 +1,2 @@ + /* finalize the node */ + embb::mtapi::Node::Finalize(); diff --git b/doc/examples/mtapi/mtapi_cpp_get_node-snippet.h a/doc/examples/mtapi/mtapi_cpp_get_node-snippet.h new file mode 100644 index 0000000..b91d187 --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_get_node-snippet.h @@ -0,0 +1 @@ + embb::mtapi::Node& node = embb::mtapi::Node::GetInstance(); diff --git b/doc/examples/mtapi/mtapi_cpp_initialize-snippet.h a/doc/examples/mtapi/mtapi_cpp_initialize-snippet.h new file mode 100644 index 0000000..859b55a --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_initialize-snippet.h @@ -0,0 +1,2 @@ + /* initialize the node with default attributes */ + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); diff --git b/doc/examples/mtapi/mtapi_cpp_main-snippet.h a/doc/examples/mtapi/mtapi_cpp_main-snippet.h new file mode 100644 index 0000000..582850a --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_main-snippet.h @@ -0,0 +1,6 @@ + EMBB_TRY { + int result = fibonacci(6); + std::cout << "result: " << result << std::endl; + } EMBB_CATCH(embb::mtapi::StatusException &) { + std::cout << "MTAPI error occured." << std::endl; + } diff --git b/doc/examples/mtapi/mtapi_cpp_register_action-snippet.h a/doc/examples/mtapi/mtapi_cpp_register_action-snippet.h new file mode 100644 index 0000000..beeea5a --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_register_action-snippet.h @@ -0,0 +1,9 @@ + /* create action */ + embb::mtapi::Action fibonacciAction( + FIBONACCI_JOB, /* action ID, defined by the + application */ + (fibonacciActionFunction) /* action function */ + ); + + /* get job */ + fibonacciJob = embb::mtapi::Job(FIBONACCI_JOB, THIS_DOMAIN_ID); diff --git b/doc/examples/mtapi/mtapi_cpp_start_task-snippet.h a/doc/examples/mtapi/mtapi_cpp_start_task-snippet.h new file mode 100644 index 0000000..cf1f908 --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_start_task-snippet.h @@ -0,0 +1,2 @@ + int result; + embb::mtapi::Task task = node.Start(fibonacciJob, &n, &result); diff --git b/doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h a/doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h new file mode 100644 index 0000000..9f1f9d7 --- /dev/null +++ a/doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h @@ -0,0 +1,5 @@ + mtapi_status_t status = task.Wait(MTAPI_INFINITE); + if (status != MTAPI_SUCCESS) { + printf("task failed with error: %d\n\n", status); + exit(status); + } diff --git b/doc/examples/mtapi/mtapi_network_c_action_function-snippet.h a/doc/examples/mtapi/mtapi_network_c_action_function-snippet.h new file mode 100644 index 0000000..9f56102 --- /dev/null +++ a/doc/examples/mtapi/mtapi_network_c_action_function-snippet.h @@ -0,0 +1,20 @@ +static void AddVectorAction( + void const * arguments, + mtapi_size_t arguments_size, + void * result_buffer, + mtapi_size_t result_buffer_size, + void const * node_local_data, + mtapi_size_t node_local_data_size, + mtapi_task_context_t * context) { + EMBB_UNUSED(context); + EMBB_UNUSED(result_buffer_size); + EMBB_UNUSED(node_local_data_size); + int elements = static_cast(arguments_size / sizeof(float) / 2); + float const * a = reinterpret_cast(arguments); + float const * b = reinterpret_cast(arguments)+elements; + float * c = reinterpret_cast(result_buffer); + float const * d = reinterpret_cast(node_local_data); + for (int ii = 0; ii < elements; ii++) { + c[ii] = a[ii] + b[ii] + d[0]; + } +} diff --git b/doc/examples/mtapi/mtapi_network_c_header-snippet.h a/doc/examples/mtapi/mtapi_network_c_header-snippet.h new file mode 100644 index 0000000..c052390 --- /dev/null +++ a/doc/examples/mtapi/mtapi_network_c_header-snippet.h @@ -0,0 +1 @@ +#include diff --git b/doc/examples/mtapi/mtapi_network_c_local_action_create-snippet.h a/doc/examples/mtapi/mtapi_network_c_local_action_create-snippet.h new file mode 100644 index 0000000..c057151 --- /dev/null +++ a/doc/examples/mtapi/mtapi_network_c_local_action_create-snippet.h @@ -0,0 +1,6 @@ + network_action = mtapi_network_action_create( + NETWORK_DOMAIN, + NETWORK_LOCAL_JOB, + NETWORK_REMOTE_JOB, + "127.0.0.1", 12345, + &status); diff --git b/doc/examples/mtapi/mtapi_network_c_plugin_finalize-snippet.h a/doc/examples/mtapi/mtapi_network_c_plugin_finalize-snippet.h new file mode 100644 index 0000000..74d49f3 --- /dev/null +++ a/doc/examples/mtapi/mtapi_network_c_plugin_finalize-snippet.h @@ -0,0 +1 @@ + mtapi_network_plugin_finalize(&status); diff --git b/doc/examples/mtapi/mtapi_network_c_plugin_initialize-snippet.h a/doc/examples/mtapi/mtapi_network_c_plugin_initialize-snippet.h new file mode 100644 index 0000000..b7cc83b --- /dev/null +++ a/doc/examples/mtapi/mtapi_network_c_plugin_initialize-snippet.h @@ -0,0 +1,2 @@ + mtapi_network_plugin_initialize("127.0.0.1", 12345, 5, + kElements * 4 * 3 + 32, &status); diff --git b/doc/examples/mtapi/mtapi_network_c_remote_action_create-snippet.h a/doc/examples/mtapi/mtapi_network_c_remote_action_create-snippet.h new file mode 100644 index 0000000..31fa6ac --- /dev/null +++ a/doc/examples/mtapi/mtapi_network_c_remote_action_create-snippet.h @@ -0,0 +1,7 @@ + float node_remote = 1.0f; + local_action = mtapi_action_create( + NETWORK_REMOTE_JOB, + AddVectorAction, + &node_remote, sizeof(float), + MTAPI_DEFAULT_ACTION_ATTRIBUTES, + &status); diff --git b/doc/examples/mtapi/mtapi_opencl_c_action_create-snippet.h a/doc/examples/mtapi/mtapi_opencl_c_action_create-snippet.h new file mode 100644 index 0000000..c3927a4 --- /dev/null +++ a/doc/examples/mtapi/mtapi_opencl_c_action_create-snippet.h @@ -0,0 +1,6 @@ + float node_local = 1.0f; + action = mtapi_opencl_action_create( + OPENCL_JOB, + kernel, "AddVector", 32, 4, + &node_local, sizeof(float), + &status); diff --git b/doc/examples/mtapi/mtapi_opencl_c_header-snippet.h a/doc/examples/mtapi/mtapi_opencl_c_header-snippet.h new file mode 100644 index 0000000..1d3a916 --- /dev/null +++ a/doc/examples/mtapi/mtapi_opencl_c_header-snippet.h @@ -0,0 +1 @@ +#include diff --git b/doc/examples/mtapi/mtapi_opencl_c_kernel-snippet.h a/doc/examples/mtapi/mtapi_opencl_c_kernel-snippet.h new file mode 100644 index 0000000..cfd3e99 --- /dev/null +++ a/doc/examples/mtapi/mtapi_opencl_c_kernel-snippet.h @@ -0,0 +1,18 @@ +const char * kernel = +"__kernel void AddVector(\n" +" __global void* arguments,\n" +" int arguments_size,\n" +" __global void* result_buffer,\n" +" int result_buffer_size,\n" +" __global void* node_local_data,\n" +" int node_local_data_size) {\n" +" int ii = get_global_id(0);\n" +" int elements = arguments_size / sizeof(float) / 2;\n" +" if (ii >= elements)" +" return;" +" __global float* a = (__global float*)arguments;\n" +" __global float* b = ((__global float*)arguments) + elements;\n" +" __global float* c = (__global float*)result_buffer;\n" +" __global float* d = (__global float*)node_local_data;\n" +" c[ii] = a[ii] + b[ii] + d[0];\n" +"}\n"; diff --git b/doc/examples/mtapi/mtapi_opencl_c_plugin_finalize-snippet.h a/doc/examples/mtapi/mtapi_opencl_c_plugin_finalize-snippet.h new file mode 100644 index 0000000..8689937 --- /dev/null +++ a/doc/examples/mtapi/mtapi_opencl_c_plugin_finalize-snippet.h @@ -0,0 +1 @@ + mtapi_opencl_plugin_finalize(&status); diff --git b/doc/examples/mtapi/mtapi_opencl_c_plugin_initialize-snippet.h a/doc/examples/mtapi/mtapi_opencl_c_plugin_initialize-snippet.h new file mode 100644 index 0000000..c8106fc --- /dev/null +++ a/doc/examples/mtapi/mtapi_opencl_c_plugin_initialize-snippet.h @@ -0,0 +1 @@ + mtapi_opencl_plugin_initialize(&status); diff --git b/doc/examples/mtapi/mtapi_terminating_condition-snippet.h a/doc/examples/mtapi/mtapi_terminating_condition-snippet.h new file mode 100644 index 0000000..7112b48 --- /dev/null +++ a/doc/examples/mtapi/mtapi_terminating_condition-snippet.h @@ -0,0 +1,3 @@ + if (n < 2) { + *result = n; + } else { diff --git b/doc/examples/mtapi/mtapi_write_back-snippet.h a/doc/examples/mtapi/mtapi_write_back-snippet.h new file mode 100644 index 0000000..bca08c7 --- /dev/null +++ a/doc/examples/mtapi/mtapi_write_back-snippet.h @@ -0,0 +1,3 @@ + *result = x + y; + } +} diff --git b/doc/examples/stl_for_each/function-snippet.h a/doc/examples/stl_for_each/function-snippet.h new file mode 100644 index 0000000..d870d31 --- /dev/null +++ a/doc/examples/stl_for_each/function-snippet.h @@ -0,0 +1 @@ +std::for_each(range.begin(), range.end(), &DoubleFunction); diff --git b/doc/examples/stl_for_each/function_define-snippet.h a/doc/examples/stl_for_each/function_define-snippet.h new file mode 100644 index 0000000..0bb8d2b --- /dev/null +++ a/doc/examples/stl_for_each/function_define-snippet.h @@ -0,0 +1,3 @@ +void DoubleFunction(int& to_double) { + to_double *= 2; +} diff --git b/doc/examples/stl_for_each/functor-snippet.h a/doc/examples/stl_for_each/functor-snippet.h new file mode 100644 index 0000000..e282b10 --- /dev/null +++ a/doc/examples/stl_for_each/functor-snippet.h @@ -0,0 +1 @@ +std::for_each(range.begin(), range.end(), DoubleFunctor()); diff --git b/doc/examples/stl_for_each/functor_define-snippet.h a/doc/examples/stl_for_each/functor_define-snippet.h new file mode 100644 index 0000000..d5e34d1 --- /dev/null +++ a/doc/examples/stl_for_each/functor_define-snippet.h @@ -0,0 +1,5 @@ +struct DoubleFunctor { + void operator()(int& to_double) { + to_double *= 2; + } +}; diff --git b/doc/examples/stl_for_each/lambda-snippet.h a/doc/examples/stl_for_each/lambda-snippet.h new file mode 100644 index 0000000..e8c545a --- /dev/null +++ a/doc/examples/stl_for_each/lambda-snippet.h @@ -0,0 +1,2 @@ +std::for_each(range.begin(), range.end(), + [] (int& to_double) { to_double *= 2; }); diff --git b/doc/examples/stl_for_each/manual-snippet.h a/doc/examples/stl_for_each/manual-snippet.h new file mode 100644 index 0000000..22b72cc --- /dev/null +++ a/doc/examples/stl_for_each/manual-snippet.h @@ -0,0 +1,3 @@ +for (size_t i=0; i < range.size(); i++) { + range[i] *= 2; +} diff --git b/doc/examples/stl_for_each/setup-snippet.h a/doc/examples/stl_for_each/setup-snippet.h new file mode 100644 index 0000000..0e78cc8 --- /dev/null +++ a/doc/examples/stl_for_each/setup-snippet.h @@ -0,0 +1,4 @@ +std::vector range(5); +for (size_t i=0; i < range.size(); i++) { + range[i] = static_cast(i) + 1; +} diff --git b/doc/examples/stl_for_each/stl_for_each-fragmented.cc a/doc/examples/stl_for_each/stl_for_each-fragmented.cc new file mode 100644 index 0000000..bd5aa21 --- /dev/null +++ a/doc/examples/stl_for_each/stl_for_each-fragmented.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "stl_for_each/function_define-snippet.h" + +#include "stl_for_each/functor_define-snippet.h" + +static void CheckResults(const std::vector& range) { + int i = 1; + for (const int& value : range) { + assert(value == i * 2); + EMBB_UNUSED_IN_RELEASE(value); + i++; + } +} + +/** + * Example using embb::patterns::ForEach. + * + * Traversing and modifying a sequence with a for-each loop. + */ + +void RunSTLForEach() { + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/manual-snippet.h" + CheckResults(range); + } + + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/function-snippet.h" + CheckResults(range); + } + + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/functor-snippet.h" + CheckResults(range); + } + + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/lambda-snippet.h" + CheckResults(range); + } +} diff --git b/doc/examples/tasks/tasks_cpp-fragmented.cc a/doc/examples/tasks/tasks_cpp-fragmented.cc new file mode 100644 index 0000000..5443760 --- /dev/null +++ a/doc/examples/tasks/tasks_cpp-fragmented.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "mtapi/mtapi_check_status-snippet.h" + +static +#include "tasks/tasks_cpp_action_signature-snippet.h" + /* get the node instance */ +#include "tasks/tasks_cpp_get_node-snippet.h" + /* calculate */ +#include "mtapi/mtapi_terminating_condition-snippet.h" + /* first recursive call spawned as task (x = fib(n - 1);) */ +#include "tasks/tasks_cpp_calc_task-snippet.h" + /* second recursive call can be called directly (y = fib(n - 2);) */ +#include "tasks/tasks_cpp_calc_direct-snippet.h" + /* wait for completion */ +#include "tasks/tasks_cpp_wait_task-snippet.h" + /* add the two preceeding numbers */ +#include "mtapi/mtapi_write_back-snippet.h" + +static +int fibonacci(int n) { + /* get the node instance, the node is initialized automatically */ + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); + /* start calculation */ +#include "tasks/tasks_cpp_start_task-snippet.h" + /* wait for task completion */ + mtapi_status_t status = task.Wait(MTAPI_INFINITE); + MTAPI_CHECK_STATUS(status); + + return result; +} + +void RunTasks() { + int result = fibonacci(6); + std::cout << "result: " << result << std::endl; +} diff --git b/doc/examples/tasks/tasks_cpp_action_signature-snippet.h a/doc/examples/tasks/tasks_cpp_action_signature-snippet.h new file mode 100644 index 0000000..24aa989 --- /dev/null +++ a/doc/examples/tasks/tasks_cpp_action_signature-snippet.h @@ -0,0 +1,5 @@ +void fibonacciActionFunction( + int n, + int* result, + embb::tasks::TaskContext & task_context + ) { diff --git b/doc/examples/tasks/tasks_cpp_calc_direct-snippet.h a/doc/examples/tasks/tasks_cpp_calc_direct-snippet.h new file mode 100644 index 0000000..eb08e75 --- /dev/null +++ a/doc/examples/tasks/tasks_cpp_calc_direct-snippet.h @@ -0,0 +1,6 @@ + int b = n - 2; + int y; + fibonacciActionFunction( + b, + &y, + task_context); diff --git b/doc/examples/tasks/tasks_cpp_calc_task-snippet.h a/doc/examples/tasks/tasks_cpp_calc_task-snippet.h new file mode 100644 index 0000000..cfe5664 --- /dev/null +++ a/doc/examples/tasks/tasks_cpp_calc_task-snippet.h @@ -0,0 +1,10 @@ + int a = n - 1; + int x; + embb::tasks::Task task = node.Spawn( + embb::base::Bind( + embb::base::MakeFunction(fibonacciActionFunction), + a, /* argument */ + &x, /* result */ + embb::base::Placeholder::_1 + ) + ); diff --git b/doc/examples/tasks/tasks_cpp_get_node-snippet.h a/doc/examples/tasks/tasks_cpp_get_node-snippet.h new file mode 100644 index 0000000..f644ec5 --- /dev/null +++ a/doc/examples/tasks/tasks_cpp_get_node-snippet.h @@ -0,0 +1 @@ + embb::tasks::Node& node = embb::tasks::Node::GetInstance(); diff --git b/doc/examples/tasks/tasks_cpp_start_task-snippet.h a/doc/examples/tasks/tasks_cpp_start_task-snippet.h new file mode 100644 index 0000000..507e634 --- /dev/null +++ a/doc/examples/tasks/tasks_cpp_start_task-snippet.h @@ -0,0 +1,9 @@ + int result; + embb::tasks::Task task = node.Spawn( + embb::base::Bind( + embb::base::MakeFunction(fibonacciActionFunction), + n, + &result, + embb::base::Placeholder::_1 + ) + ); diff --git b/doc/examples/tasks/tasks_cpp_wait_task-snippet.h a/doc/examples/tasks/tasks_cpp_wait_task-snippet.h new file mode 100644 index 0000000..21637fd --- /dev/null +++ a/doc/examples/tasks/tasks_cpp_wait_task-snippet.h @@ -0,0 +1,2 @@ + mtapi_status_t status = task.Wait(MTAPI_INFINITE); + MTAPI_CHECK_STATUS(status); diff --git b/mtapi_c/CMakeLists.txt a/mtapi_c/CMakeLists.txt new file mode 100644 index 0000000..0e147b1 --- /dev/null +++ a/mtapi_c/CMakeLists.txt @@ -0,0 +1,41 @@ +project (project_embb_mtapi_c) + +file(GLOB_RECURSE EMBB_MTAPI_C_SOURCES "src/*.c" "src/*.h") +file(GLOB_RECURSE EMBB_MTAPI_C_HEADERS "include/*.h") + +file(GLOB_RECURSE EMBB_MTAPI_TEST_SOURCES "test/*.cc" "test/*.h") + +IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) +FOREACH(src_tmp ${EMBB_MTAPI_TEST_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +FOREACH(src_tmp ${EMBB_MTAPI_C_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +ENDIF() + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_MTAPI_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_MTAPI_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ) + +add_library(embb_mtapi_c ${EMBB_MTAPI_C_SOURCES} ${EMBB_MTAPI_C_HEADERS}) +target_link_libraries(embb_mtapi_c embb_base_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_mtapi_c_test ${EMBB_MTAPI_TEST_SOURCES}) + target_link_libraries(embb_mtapi_c_test embb_mtapi_c partest embb_base_c ${compiler_libs}) + CopyBin(BIN embb_mtapi_c_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_mtapi_c DESTINATION lib) diff --git b/mtapi_c/include/embb/mtapi/c/mtapi.h a/mtapi_c/include/embb/mtapi/c/mtapi.h new file mode 100644 index 0000000..f007db7 --- /dev/null +++ a/mtapi_c/include/embb/mtapi/c/mtapi.h @@ -0,0 +1,3528 @@ +/* + * MTAPI header contains the public MTAPI API and data type definitions. + * + * This file defines the MTAPI API. it has to be included by any application + * using MTAPI. + * + * \copyright + * Copyright (c) 2012, The Multicore Association. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * (1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * (2) Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * (3) Neither the name of the Multicore Association nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * \note + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_C_MTAPI_H_ +#define EMBB_MTAPI_C_MTAPI_H_ + +/** + * \defgroup C_MTAPI MTAPI + * \ingroup C + * + * Multicore Task Management API (MTAPI®). + * + * MTAPI is an API standardized by the + * Multicore Association + * for leveraging task parallelism on a wide range of embedded devices + * containing symmetric or asymmetric multicore processors. + * A description of the basic terms and concepts is given below. More + * information can be found on the website of the + * Multicore + Task Management Working Group. + * + * ## Definitions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ActionAn action is the hardware or software implementation of a job. An + * action implemented in software consists of the implementation of an + * action function with a predefined signature. Software actions are + * registered with the MTAPI runtime and associated with a job. While + * executing, an action is also associated with a task and task context. + * Hardware implementations of actions must be known a priori in the + * MTAPI runtime implementation. There is no standardized way of + * registering hardware actions because they are highly + * hardware-dependent. Hardware and software actions are referenced by + * handles or indirectly through job IDs and job handles.
Action FunctionThe executable function of an action, invoked by the + * MTAPI runtime when a task is started.
AffinityDefines which cores can execute a given action function.
BlockingA blocking function does not return until the function completes + * successfully or returns with an error.
CoreA core is an undividable processing element. Two cores can share + * resources such as memory or ALUs for hyperthreaded cores. The core + * notion is necessary for core affinity, but is + * implementation-specific.
DomainAn implementation of MTAPI includes one or more domains, each with + * one or more nodes. The concept of domains is consistent in all + * Multicore Association APIs. A domain is comparable to a subnet in a + * network or a namespace for unique names and IDs. Domains are + * supported by a runtime.
HandleAn abstract reference to an object on the same node or to an object + * managed by another node. A handle is valid only on the node on which + * it was requested and generated. A handle is opaque, that is, its + * underlying representation is implementation-defined. Handles can be + * copied, assigned, and passed as arguments, but the application + * should make no other assumptions about the type, representation, or + * contents of a handle.
JobA job provides a way to reference one or more actions. Jobs are + * abstractions of the processing implemented in hardware or software + * by actions. Multiple actions can implement the same job based on + * different hardware resources (for instance a job can be implemented + * by one action on a DSP and by another action on a general purpose + * core, or a job can be implemented by both hardware and software + * actions). Each job is represented by a domain-wide job ID, or by a + * job handle local to a node.
MCAThe Multicore Association.
MTAPIMulticore Task Management API, defined by The Multicore Association. + *
NodeA node represents an independent unit of execution that maps to a + * process, thread, thread pool, instance of an operating system, + * hardware accelerator, processor core, a cluster of processor cores, + * or other abstract processing entity with an independent program + * counter. Each node can belong to only one domain. The concept of + * nodes is consistent in all Multicore Associations APIs. Code + * executed on an MTAPI node shares memory (data) with any other code + * executed on the same node.
QueueA software or hardware entity in which tasks are enqueued in a + * given order. The queue can ensure in-order execution of tasks. + * Furthermore, queues might implement other scheduling policies that + * can be configured by setting queue attributes.
ReferenceA reference exists when an object or abstract entity has knowledge + * or access to another object, without regard to the specific means of + * the implementation.
ResourceA processing core or chip, hardware accelerator, memory region, or + * I/O.
Remote MemoryMemory that cannot be accessed using standard load and store + * operations. For example, host memory is remote to a GPU core.
Runtime SystemAn MTAPI runtime system (or "runtime") is the underlying + * implementation of MTAPI. The core of the runtime system supports + * task scheduling and communication with other nodes. Each MTAPI has + * an MTAPI runtime system.
SMPSMP is short for symmetric multiprocessing, in which two or more + * identical processing cores are connected to a shared main memory + * and are controlled by a single OS instance.
TaskA task is the invocation of an action. A task is associated with a + * job object, which is associated with one or more actions. A task + * may optionally be associated with a task group. A task has + * attributes and an internal state. A task begins its lifetime with a + * call to mtapi_task_start() or mtapi_task_enqueue(). A task is + * referenced by a handle of type mtapi_task_hndl_t. After a task has + * started, it is possible to wait for task completion from other + * parts of the program. Every task can run exactly once, i.e., the + * task cannot be started a second time. (Note that in other contexts, + * the term "task" has a different meaning. Some real-time operating + * systems use "task" for operating system threads, for example.)
Task ContextInformation about the task, accessible by the corresponding action + * function; useful for action code reflection.
+ * + * ## The MTAPI Feature Set + * + * MTAPI supports two programming modes derived from use cases of the working + * group members: + * - __Tasks__
+ * MTAPI allows a programmer to start tasks and to synchronize on task + * completion. Tasks are executed by the runtime system, concurrently to + * other tasks that have been started and have not been completed at that + * point in time. A task can be implemented by software or by hardware. + * Tasks can be started from remote nodes, i.e., the implementation can be + * done on one node, but the starting and synchronization of corresponding + * tasks can be done on other nodes. The developer decides where to deploy + * a task implementation. On the executing node, the runtime system selects + * the cores that execute a particular task. This mapping can be influenced + * by application-specific attributes. Tasks can start sub-tasks. MTAPI + * provides a basic mechanism to pass data to the node that executes a + * task, and back to the calling node. + * - __Queues__
+ * Explicit queues can be used to control the task scheduling policies for + * related tasks. Order-preserving queues ensure that tasks are executed + * sequentially in queue order with no subsequent task starting until the + * previous one is complete. MTAPI also supports non-order-preserving + * queues, allowing control of the scheduling policies of tasks started via + * the same queue (queues may offer implementation specific scheduling + * policies controlled by implementation specific queue attributes). Even + * hardware queues can be associated with queue objects. + * + * MTAPI also supports the following types of tasks: + * - __Single tasks__
+ * Single tasks are the standard case: After a task is started, the + * application may wait for completion of the task at a later point in + * time. In some cases the application waits for completion of a group of + * tasks. In other cases waiting is not required at all. When a + * software-implemented task is started, the corresponding code (action + * function) is executed once by the MTAPI runtime environment. When a + * hardware-implemented task is started, the task execution is triggered + * once by the MTAPI runtime system. + * - __Multi-instance tasks__
+ * Multi-instance tasks execute the same action multiple times in parallel + * (similar to parallel regions in OpenMP or parallel MPI processes). + * - __Multiple-implementation tasks / load balancing__
+ * In heterogeneous systems, there could be implementations of the same job + * for different types of processor cores, e.g., one general purpose + * implementation and a second one for a hardware accelerator. MTAPI allows + * attaching multiple actions to a job. The runtime system shall decide + * dynamically during runtime, depending on the system load, which action to + * utilize. Only one of the alternative actions will be executed. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- MCA ORGANIZATION IDS ----------------------------------------------- */ + +#define MCA_ORG_ID_PSI 0 /* PolyCore Software, Inc. */ +#define MCA_ORG_ID_FSL 1 /* Freescale, Inc. */ +#define MCA_ORG_ID_MGC 2 /* Mentor Graphics, Corp. */ +#define MCA_ORG_ID_ADI 3 /* Analog Devices */ +#define MCA_ORG_ID_SIE 4 /* Siemens */ +#define MCA_ORG_ID_EMB 5 /* EMB2 project */ +#define MCA_ORG_ID_TBD 6 /* TBD */ + + +/* ---- BASIC DEFINITIONS -------------------------------------------------- */ + +/** marks input parameters */ +#define MTAPI_IN const +/** marks output parameters */ +#define MTAPI_OUT +/** marks in/out parameters */ +#define MTAPI_INOUT + + +/* ---- BASIC DATA TYPES --------------------------------------------------- */ + +/* MCA type definitions */ +typedef int mca_int_t; +typedef int8_t mca_int8_t; +typedef int16_t mca_int16_t; +typedef int32_t mca_int32_t; +typedef int64_t mca_int64_t; + +typedef unsigned int mca_uint_t; +typedef uint8_t mca_uint8_t; +typedef uint16_t mca_uint16_t; +typedef uint32_t mca_uint32_t; +typedef uint64_t mca_uint64_t; + +typedef unsigned char mca_boolean_t; + +typedef unsigned int mca_domain_t; +typedef unsigned int mca_node_t; +typedef unsigned int mca_status_t; +typedef int mca_timeout_t; + +/* the MTAPI data types */ +typedef mca_int_t mtapi_int_t; +typedef mca_int8_t mtapi_int8_t; +typedef mca_int16_t mtapi_int16_t; +typedef mca_int32_t mtapi_int32_t; +typedef mca_int64_t mtapi_int64_t; + +typedef mca_uint_t mtapi_uint_t; +typedef mca_uint8_t mtapi_uint8_t; +typedef mca_uint16_t mtapi_uint16_t; +typedef mca_uint32_t mtapi_uint32_t; +typedef mca_uint64_t mtapi_uint64_t; + +typedef mca_domain_t mtapi_domain_t; +typedef mca_node_t mtapi_node_t; +typedef mca_timeout_t mtapi_timeout_t; + +typedef mca_boolean_t mtapi_boolean_t; +typedef mtapi_uint_t mtapi_size_t; + +/** + * Info structure. + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +struct mtapi_info_struct { + mtapi_uint_t mtapi_version; /**< The three last (rightmost) hex + digits are the minor number, and + those left of the minor number are + the major number. */ + mtapi_uint_t organization_id; /**< Implementation vendor or + organization ID. */ + mtapi_uint_t implementation_version; /**< The three last (rightmost) hex + digits are the minor number, and + those left of the minor number are + the major number.*/ + mtapi_uint_t number_of_domains; /**< Number of domains allowed by the + implementation.*/ + mtapi_uint_t number_of_nodes; /**< Number of nodes allowed by the + implementation.*/ + mtapi_uint_t hardware_concurrency; /**< Number of CPU cores available. */ + mtapi_uint_t used_memory; /**< Bytes of memory used by MTAPI. */ +}; + +/** + * Info type. + * \memberof mtapi_info_struct + */ +typedef struct mtapi_info_struct mtapi_info_t; + +/** + * Core affinity type. + * \ingroup CORE_AFFINITY_MASKS + */ +typedef mtapi_uint64_t mtapi_affinity_t; + + +/* ---- BASIC enumerations ------------------------------------------------- */ + +/** + * Status codes returned in the status parameter of the MTAPI interface + * functions. + */ +enum mtapi_status_enum { + /* generic */ + MTAPI_SUCCESS, /**< success, no error */ + MTAPI_TIMEOUT, /**< timeout was reached */ + MTAPI_ERR_PARAMETER, /**< invalid parameter */ + MTAPI_ERR_ATTR_READONLY, /**< tried to write a read-only + attribute */ + MTAPI_ERR_ATTR_NUM, /**< invalid attribute number */ + MTAPI_ERR_ATTR_SIZE, /**< invalid attribute size */ + + /* node specific */ + MTAPI_ERR_NODE_INITFAILED, /**< general error in node + initialization */ + MTAPI_ERR_NODE_INITIALIZED, /**< \a mtapi_initialize called for a + node that already had been + initialized */ + MTAPI_ERR_NODE_INVALID, /**< The node id is not valid */ + MTAPI_ERR_DOMAIN_INVALID, /**< the domain id is not valid */ + MTAPI_ERR_NODE_NOTINIT, /**< the node is not initialized */ + + /* action specific */ + MTAPI_ERR_ACTION_INVALID, /**< The action id is not a valid action + id, i.e., no action was crated for + that ID or the action has been + deleted. */ + MTAPI_ERR_ACTION_EXISTS, /**< mtapi_action_create called with an + ID of an action that already had + been created */ + MTAPI_ERR_ACTION_LIMIT, /**< exceeded maximum number of actions + allowed */ + MTAPI_ERR_ACTION_NUM_INVALID, /**< The number of actions passed to + mtapi_task_start or + mtapi_queue_create is lower than + 1. */ + MTAPI_ERR_ACTION_FAILED, /**< status that can be passed to the + runtime by + \a mtapi_context_status_set if the + task could not be completed as + intended */ + MTAPI_ERR_ACTION_CANCELLED, /**< status that can be passed to the + runtime by + \a mtapi_context_status_set if the + task execution is canceled */ + MTAPI_ERR_ACTION_DELETED, /**< All actions associated with the + task have been deleted before the + execution of the task was started + or the error code has been set in + the action code to + MTAPI_ERR_ACTION_DELETED by + \a mtapi_action_result_set. */ + MTAPI_ERR_ACTION_DISABLED, /**< All actions associated with the + task have been disabled before the + execution of the task was started + or the error code has been + set in the action code to + MTAPI_ERR_ACTION_DISABLED by + \a mtapi_action_result_set. */ + + /* context specific */ + MTAPI_ERR_CONTEXT_INVALID, + MTAPI_ERR_CONTEXT_OUTOFCONTEXT, /**< returned if action code is not + called in the context of a task + execution. This function must be + used in an action function only. + The action function must be called + from the MTAPI runtime system */ + + /* task specific */ + MTAPI_ERR_TASK_INVALID, + MTAPI_ERR_TASK_LIMIT, /**< exceeded maximum number of tasks + allowed */ + + /* job specific */ + MTAPI_ERR_JOB_INVALID, /**< invalid job handle or job ID */ + + /* queue specific */ + MTAPI_ERR_QUEUE_INVALID, /**< argument is not a valid queue + handle or ID */ + MTAPI_ERR_QUEUE_DELETED, /**< a deleted queue is passed as an + argument */ + MTAPI_ERR_QUEUE_DISABLED, /**< a disable queue is passed as an + argument */ + MTAPI_ERR_QUEUE_LIMIT, /**< exceeded maximum number of queues + allowed */ + + /* group specific */ + MTAPI_ERR_GROUP_INVALID, + MTAPI_ERR_GROUP_LIMIT, /**< exceeded maximum number of groups + allowed */ + MTAPI_GROUP_COMPLETED, /**< group completed, i.e., there are no + more tasks to wait for in the group + when waiting with + \a mtapi_group_wait_any */ + + /* others */ + MTAPI_ERR_UNKNOWN, /**< unknown error */ + MTAPI_ERR_BUFFER_SIZE, /**< buffer size mismatch */ + MTAPI_ERR_RESULT_SIZE, /**< result buffer size mismatch + (e.g., in \a mtapi_task_wait) */ + MTAPI_ERR_ARG_SIZE, /**< invalid argument size */ + MTAPI_ERR_WAIT_PENDING, /**< mtapi_*_wait called twice on a + group or task which has not + finished */ + + /* unsupported functions */ + MTAPI_ERR_FUNC_NOT_IMPLEMENTED, /**< The MTAPI function called is not + implemented by the runtime + system. */ + MTAPI_ERR_ARG_NOT_IMPLEMENTED, /**< The MTAPI function called is + implemented by the runtime, but it + does not support the arguments + passed. */ + + /* features that may be not supported by some implementations */ + MTAPI_ERR_RUNTIME_REMOTETASKS_NOTSUPPORTED, + /**< The Runtime system does not support + remote tasks. This allows lighter + implementations for shared memory + environments. */ + MTAPI_ERR_RUNTIME_LOADBALANCING_NOTSUPPORTED, + /**< This error is returned when more + than one action is passed to + mtapi_task_start or + mtapi_queue_create and if the + runtime system does not implement + load balancing between nodes. This + allows light MTAPI implementation + for systems not having the + requirement for inter-node + load-balancing */ + + /* core affinity specific */ + MTAPI_ERR_CORE_NUM, /**< This error occurs when trying to + set an affinity to a non-existing + core */ +}; +typedef enum mtapi_status_enum mtapi_status_t; + /**< defines the MTAPI state codes */ + + +/** + * Task states. + */ +enum mtapi_task_state_enum { + MTAPI_TASK_INTENTIONALLY_UNUSED, /**< never used */ + MTAPI_TASK_ERROR, /**< indicates internal error */ + MTAPI_TASK_PRENATAL, /**< initialization value for newly + allocated task descriptor */ + MTAPI_TASK_CREATED, + MTAPI_TASK_SCHEDULED, + MTAPI_TASK_RUNNING, + MTAPI_TASK_WAITING, + MTAPI_TASK_RETAINED, + MTAPI_TASK_DELETED, + MTAPI_TASK_CANCELLED, /**< \a MTAPI_TASK_CANCELLED is the only + value specified by the MTAPI + specification, the others are + implementation specific and can be + used for debugging purposes. */ + MTAPI_TASK_COMPLETED +}; +typedef enum mtapi_task_state_enum mtapi_task_state_t; + /**< internal task state */ + +/** + * Notification types for the runtime. + */ +enum mtapi_notification_enum { + MTAPI_NOTIF_PREFETCH, /**< implementation specific example */ + MTAPI_NOTIF_EXECUTE_NEXT /**< implementation specific example */ +}; +typedef enum mtapi_notification_enum mtapi_notification_t; + /**< runtime notification */ + + +/** + * Node attributes, to be extended for implementation specific attributes + */ +enum mtapi_node_attributes_enum { + MTAPI_NODE_CORE_AFFINITY, /**< use the given cores only */ + MTAPI_NODE_NUMCORES, /**< number of cores provided by the + node */ + MTAPI_NODE_TYPE, /**< the nodes type, SMP or DSP */ + MTAPI_NODE_MAX_TASKS, /**< maximum number of tasks allowed by + the node */ + MTAPI_NODE_MAX_ACTIONS, /**< maximum number of actions allowed + by the node */ + MTAPI_NODE_MAX_GROUPS, /**< maximum number of groups allowed + by the node */ + MTAPI_NODE_MAX_QUEUES, /**< maximum number of queues allowed + by the node */ + MTAPI_NODE_QUEUE_LIMIT, /**< maximum number of elements that fit + into a queue */ + MTAPI_NODE_MAX_JOBS, /**< maximum number of jobs allowed by + the node */ + MTAPI_NODE_MAX_ACTIONS_PER_JOB, /**< maximum number of actions in a job + allowed by the node */ + MTAPI_NODE_MAX_PRIORITIES, /**< maximum number of priorities + allowed by the node */ + MTAPI_NODE_REUSE_MAIN_THREAD /**< reuse main thread as worker */ +}; +/** size of the \a MTAPI_NODE_CORE_AFFINITY attribute */ +#define MTAPI_NODE_CORE_AFFINITY_SIZE sizeof(embb_core_set_t) +/** size of the \a MTAPI_NODES_NUMCORES attribute */ +#define MTAPI_NODE_NUMCORES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_TYPE attribute */ +#define MTAPI_NODE_TYPE_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_TASKS attribute */ +#define MTAPI_NODE_MAX_TASKS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_ACTIONS attribute */ +#define MTAPI_NODE_MAX_ACTIONS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_GROUPS attribute */ +#define MTAPI_NODE_MAX_GROUPS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_QUEUES attribute */ +#define MTAPI_NODE_MAX_QUEUES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_QUEUE_LIMIT attribute */ +#define MTAPI_NODE_QUEUE_LIMIT_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_JOBS attribute */ +#define MTAPI_NODE_MAX_JOBS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_ACTIONS_PER_JOB attribute */ +#define MTAPI_NODE_MAX_ACTIONS_PER_JOB_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_PRIORITIES attribute */ +#define MTAPI_NODE_MAX_PRIORITIES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_REUSE_MAIN_THREAD attribute */ +#define MTAPI_NODE_REUSE_MAIN_THREAD_SIZE sizeof(mtapi_boolean_t) + +/* example attribute value */ +#define MTAPI_NODE_TYPE_SMP 1 +#define MTAPI_NODE_TYPE_DSP 2 + +/** + * Task handle type. + * \memberof mtapi_task_hndl_struct + */ +typedef struct mtapi_task_hndl_struct mtapi_task_hndl_t; + +/** task completion callback */ +typedef void(*mtapi_task_complete_function_t)( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status); + +/** task attributes */ +enum mtapi_task_attributes_enum { + MTAPI_TASK_DETACHED, /**< task is detached, i.e., the runtime + system cared about deleting + internal data structures + representing the task; detached + tasks cannot be accessed via task + handles */ + MTAPI_TASK_INSTANCES, /**< indicates how many parallel + instances of task shall be started + by MTAPI; the default case is that + each task is executed exactly once, + setting this value to \a n, the + corresponding action code will be + executed n times, if possible in + parallel */ + MTAPI_TASK_PRIORITY, + MTAPI_TASK_AFFINITY, + MTAPI_TASK_USER_DATA, + MTAPI_TASK_COMPLETE_FUNCTION +}; +/** size of the \a MTAPI_TASK_DETACHED attribute */ +#define MTAPI_TASK_DETACHED_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_TASK_INSTANCES attribute */ +#define MTAPI_TASK_INSTANCES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_TASK_PRIORITY attribute */ +#define MTAPI_TASK_PRIORITY_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_TASK_AFFINITY attribute */ +#define MTAPI_TASK_AFFINITY_SIZE sizeof(mtapi_affinity_t) + + +/** + * action attributes + */ +enum mtapi_action_attributes_enum { + MTAPI_ACTION_GLOBAL, + MTAPI_ACTION_AFFINITY, + MTAPI_ACTION_DOMAIN_SHARED +}; +/** size of the \a MTAPI_ACTION_GLOBAL attribute */ +#define MTAPI_ACTION_GLOBAL_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_ACTION_AFFINITY attribute */ +#define MTAPI_ACTION_AFFINITY_SIZE sizeof(mtapi_affinity_t) +/** size of the \a MTAPI_ACTION_DOMAIN_SHARED attribute */ +#define MTAPI_ACTION_DOMAIN_SHARED_SIZE sizeof(mtapi_boolean_t) + + +/** + * queue attributes + */ +enum mtapi_queue_attributes_enum { + MTAPI_QUEUE_GLOBAL, + MTAPI_QUEUE_PRIORITY, + MTAPI_QUEUE_LIMIT, + MTAPI_QUEUE_ORDERED, + MTAPI_QUEUE_RETAIN, + MTAPI_QUEUE_DOMAIN_SHARED +}; +/** size of the \a MTAPI_QUEUE_GLOBAL attribute */ +#define MTAPI_QUEUE_GLOBAL_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_QUEUE_PRIORITY attribute */ +#define MTAPI_QUEUE_PRIORITY_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_QUEUE_LIMIT attribute */ +#define MTAPI_QUEUE_LIMIT_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_QUEUE_ORDERED attribute */ +#define MTAPI_QUEUE_ORDERED_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_QUEUE_RETAIN attribute */ +#define MTAPI_QUEUE_RETAIN_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_QUEUE_DOMAIN_SHARED attribute */ +#define MTAPI_QUEUE_DOMAIN_SHARED_SIZE sizeof(mtapi_boolean_t) + + +#define MTAPI_ATTRIBUTE_VALUE(value) ((void*)(value)) +#define MTAPI_ATTRIBUTE_POINTER_AS_VALUE 0 + + +/* ---- ATTRIBUTES --------------------------------------------------------- */ + +/** + * Node attributes. + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +struct mtapi_node_attributes_struct { + embb_core_set_t core_affinity; /**< stores MTAPI_NODE_CORE_AFFINITY */ + mtapi_uint_t num_cores; /**< stores MTAPI_NODE_NUMCORES */ + mtapi_uint_t type; /**< stores MTAPI_NODE_TYPE */ + mtapi_uint_t max_tasks; /**< stores MTAPI_NODE_MAX_TASKS */ + mtapi_uint_t max_actions; /**< stores MTAPI_NODE_MAX_ACTIONS */ + mtapi_uint_t max_groups; /**< stores MTAPI_NODE_MAX_GROUPS */ + mtapi_uint_t max_queues; /**< stores MTAPI_NODE_MAX_QUEUES */ + mtapi_uint_t queue_limit; /**< stores MTAPI_NODE_QUEUE_LIMIT */ + mtapi_uint_t max_jobs; /**< stores MTAPI_NODE_MAX_JOBS */ + mtapi_uint_t max_actions_per_job; /**< stores + MTAPI_NODE_MAX_ACTIONS_PER_JOB */ + mtapi_uint_t max_priorities; /**< stores MTAPI_NODE_MAX_PRIORITIES */ + mtapi_boolean_t reuse_main_thread; /**< stores + MTAPI_NODE_REUSE_MAIN_THREAD */ +}; + +/** + * Action attributes. + * \ingroup ACTIONS + */ +struct mtapi_action_attributes_struct { + mtapi_boolean_t global; /**< stores MTAPI_ACTION_GLOBAL */ + mtapi_affinity_t affinity; /**< stores MTAPI_ACTION_AFFINITY */ + mtapi_boolean_t domain_shared; /**< stores MTAPI_ACTION_DOMAIN_SHARED*/ +}; + +/** + * Task attributes. + * \ingroup TASKS + */ +struct mtapi_task_attributes_struct { + mtapi_boolean_t is_detached; /**< stores MTAPI_TASK_DETACHED */ + mtapi_uint_t num_instances; /**< stores MTAPI_TASK_INSTANCES */ + mtapi_uint_t priority; /**< stores MTAPI_TASK_PRIORITY */ + mtapi_affinity_t affinity; /**< stores MTAPI_TASK_AFFINITY */ + void * user_data; /**< stores MTAPI_TASK_USER_DATA */ + mtapi_task_complete_function_t + complete_func; /**< stores + MTAPI_TASK_COMPLETE_FUNCTION */ +}; + +/** + * Queue attributes. + * \ingroup QUEUES + */ +struct mtapi_queue_attributes_struct { + mtapi_boolean_t global; /**< stores MTAPI_QUEUE_GLOBAL */ + mtapi_uint_t priority; /**< stores MTAPI_QUEUE_PRIORITY */ + mtapi_uint_t limit; /**< stores MTAPI_QUEUE_LIMIT */ + mtapi_boolean_t ordered; /**< stores MTAPI_QUEUE_ORDERED */ + mtapi_boolean_t retain; /**< stores MTAPI_QUEUE_RETAIN */ + mtapi_boolean_t domain_shared; /**< stores MTAPI_QUEUE_DOMAIN_SHARED */ +}; + +/** + * Group attributes. + * \ingroup TASK_GROUPS + */ +struct mtapi_group_attributes_struct { + mtapi_int_t some_value; /**< just a placeholder */ +}; + +/** + * Node attributes type. + * \memberof mtapi_node_attributes_struct + */ +typedef struct mtapi_node_attributes_struct mtapi_node_attributes_t; + +/** + * Action attributes type. + * \memberof mtapi_action_attributes_struct + */ +typedef struct mtapi_action_attributes_struct mtapi_action_attributes_t; + +/** + * Task attributes type. + * \memberof mtapi_task_attributes_struct + */ +typedef struct mtapi_task_attributes_struct mtapi_task_attributes_t; + +/** + * Queue attributes type. + * \memberof mtapi_queue_attributes_struct + */ +typedef struct mtapi_queue_attributes_struct mtapi_queue_attributes_t; + +/** + * Group attributes type. + * \memberof mtapi_group_attributes_struct + */ +typedef struct mtapi_group_attributes_struct mtapi_group_attributes_t; + +/** short form for using the default node attributes */ +#define MTAPI_DEFAULT_NODE_ATTRIBUTES MTAPI_NULL + +/** short form for using the default action attributes */ +#define MTAPI_DEFAULT_ACTION_ATTRIBUTES MTAPI_NULL + +/** short form for using the default task attributes */ +#define MTAPI_DEFAULT_TASK_ATTRIBUTES MTAPI_NULL + +/** short form for using the default queue attributes */ +#define MTAPI_DEFAULT_QUEUE_ATTRIBUTES MTAPI_NULL + +/** short form for using the default group attributes */ +#define MTAPI_DEFAULT_GROUP_ATTRIBUTES MTAPI_NULL + + +/* ---- FUNCTION TYPES ----------------------------------------------------- */ + +/** + * Task context type. + * \memberof embb_mtapi_task_context_struct + */ +typedef struct embb_mtapi_task_context_struct mtapi_task_context_t; + +/** + * An action function is the executable software function that implements an + * action. + * + * The runtime passes arguments to the action function when a task is started. + * Passing arguments from one node to another node should be implemented as a + * copy operation. Just as the arguments are passed before start of execution, + * the result buffer is copied back to the calling node after the action + * function terminates. In shared memory environments, the copying of data in + * both cases is not necessary. The node-local data is data used by several + * action functions being executed on the same node (or at least in the same + * address space). The shared data is specified when the action is created. + * + * An action function can interact with the runtime environment through a task + * context object of type mtapi_task_context_t . A task context object is + * allocated and managed by the runtime. The runtime passes a pointer to the + * context object when the action function is invoked. The action may then + * query information about the execution context (e.g., its core number, the + * number of tasks and task number in a multi-instance task, polling the task + * state) by calling the mtapi_context_* functions. Furthermore it is possible + * to pass information from the action function to the runtime system which + * is executing the action function (setting the status manually, for example). + * All of these mtapi_context_* functions are called in the context of task + * execution. + * + * \ingroup ACTION_FUNCTIONS + */ +typedef void(*mtapi_action_function_t)( + const void* args, /**< arguments */ + mtapi_size_t args_size, /**< length of arguments */ + void* result_buffer, /**< buffer for storing results */ + mtapi_size_t result_buffer_size, /**< length of result_buffer */ + const void* node_local_data, /**< node-local data, shared data by + several tasks executed on the same + node */ + mtapi_size_t node_local_data_size, /**< length of shared data */ + mtapi_task_context_t * context /**< MTAPI task context provided by the + runtime systems identifying the + current task for calling back the + runtime system from the action + function */ +); + + +/* ---- HANDLES and IDs ---------------------------------------------------- */ + +typedef mtapi_uint_t mtapi_action_id_t; +typedef mtapi_uint_t mtapi_job_id_t; +typedef mtapi_uint_t mtapi_queue_id_t; +typedef mtapi_uint_t mtapi_group_id_t; +typedef mtapi_uint_t mtapi_task_id_t; + +/** + * Action handle. + * \ingroup ACTIONS + */ +struct mtapi_action_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_action_id_t id; /**< pool index of this handle */ +}; + +/** + * Action handle type. + * \memberof mtapi_action_hndl_struct + */ +typedef struct mtapi_action_hndl_struct mtapi_action_hndl_t; + +/** + * Job handle. + * \ingroup JOBS + */ +struct mtapi_job_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_job_id_t id; /**< pool index of this handle */ +}; + +/** + * Job handle type. + * \memberof mtapi_job_hndl_struct + */ +typedef struct mtapi_job_hndl_struct mtapi_job_hndl_t; + +/** + * Queue handle. + * \ingroup QUEUES + */ +struct mtapi_queue_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_queue_id_t id; /**< pool index of this handle */ +}; + +/** + * Queue handle type. + * \memberof mtapi_queue_hndl_struct + */ +typedef struct mtapi_queue_hndl_struct mtapi_queue_hndl_t; + +/** + * Group handle. + * \ingroup TASK_GROUPS + */ +struct mtapi_group_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_group_id_t id; /**< pool index of this handle */ +}; + +/** + * Group handle type. + * \memberof mtapi_group_hndl_struct + */ +typedef struct mtapi_group_hndl_struct mtapi_group_hndl_t; + +/** + * Task handle. + * \ingroup TASKS + */ +struct mtapi_task_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_task_id_t id; /**< pool index of this handle */ +}; + +// was forward declared +//typedef struct mtapi_task_hndl_struct mtapi_task_hndl_t; + + +/* ---- BASIC CONSTANTS ---------------------------------------------------- */ + +#define MTAPI_TRUE ((mtapi_boolean_t)1) +#define MTAPI_FALSE ((mtapi_boolean_t)0) + +extern mtapi_group_hndl_t MTAPI_GROUP_NONE; + +#define MTAPI_NULL 0 + +#define TEN_SECONDS 10000 +#define MTAPI_INFINITE -1 +#define MTAPI_NOWAIT 0 + +#define MTAPI_NODE_MAX_TASKS_DEFAULT 1024 +#define MTAPI_NODE_MAX_ACTIONS_DEFAULT 1024 +#define MTAPI_NODE_MAX_GROUPS_DEFAULT 128 +#define MTAPI_NODE_MAX_QUEUES_DEFAULT 16 +/** default size for MTAPI queues */ +#define MTAPI_NODE_QUEUE_LIMIT_DEFAULT 1024 +#define MTAPI_NODE_MAX_JOBS_DEFAULT 256 +#define MTAPI_NODE_MAX_ACTIONS_PER_JOB_DEFAULT 4 +#define MTAPI_NODE_MAX_PRIORITIES_DEFAULT 4 + +#define MTAPI_JOB_ID_INVALID 0 +#define MTAPI_DOMAIN_ID_INVALID 0 +#define MTAPI_NODE_ID_INVALID 0 + +#define MTAPI_TASK_ID_NONE 0 +#define MTAPI_GROUP_ID_NONE 0 +#define MTAPI_QUEUE_ID_NONE 0 +#define MTAPI_ACTION_ID_NONE 0 + + +/* ---- RUNTIME INIT & SHUTDOWN -------------------------------------------- */ + +/** + * \defgroup RUNTIME_INIT_SHUTDOWN General + * + * \ingroup C_MTAPI + * + * Initialization, introspection, and finalization functions. + * + * All applications wishing to use MTAPI functionality must use the + * initialization and finalization routines. After initialization, the + * introspection functions can provide important information to MTAPI-based + * applications. + */ + +/** + * This function initializes a node attributes object. + * + * A node attributes object is a container of node attributes. It is an + * optional argument passed to mtapi_initialize() to specify non-default node + * attributes when creating a node. + * + * To set node attributes to non-default values, the application must allocate + * a node attributes object of type mtapi_node_attributes_t and initialize it + * with a call to mtapi_nodeattr_init(). The application may call + * mtapi_nodeattr_set() to specify attribute values. Calls to + * mtapi_nodeattr_init() have no effect on node attributes after the node has + * been created and initialized with mtapi_initialize(). The + * mtapi_node_attributes_t object may safely be deleted by the application + * after the call to mtapi_nodeattr_init(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ---------------------- | --------------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * + * \see mtapi_initialize(), mtapi_nodeattr_set() + * + * \notthreadsafe + * \memberof mtapi_node_attributes_struct + */ +void mtapi_nodeattr_init( + MTAPI_OUT mtapi_node_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets node attribute values in a node attributes object. + * + * A node attributes object is a container of node attributes, optionally + * passed to mtapi_initialize() to specify non-default node attributes when + * creating a node. + * + * \c attributes is a pointer to a node attributes object that was previously + * initialized with a call to mtapi_nodeattr_init(). Calls to + * mtapi_nodeattr_set() have no effect on node attributes after the node has + * been created and initialized with mtapi_initialize(). The node attributes + * object may safely be deleted by the application after the call to + * mtapi_initialize(). + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. + * Additional attributes may be defined by the implementation. + * + * MTAPI-defined node attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
\c MTAPI_NODES_NUMCORES(Read-only) number of processor cores of the node.\c mtapi_uint_t(none)
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * + * \see mtapi_nodeattr_init(), mtapi_initialize() + * + * \notthreadsafe + * \memberof mtapi_node_attributes_struct + */ +void mtapi_nodeattr_set( + MTAPI_INOUT mtapi_node_attributes_t* attributes, + /**< [in, out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Initializes the MTAPI environment on a given MTAPI node in a given MTAPI + * domain. + * + * It must be called on each node using MTAPI. A node maps to a process, + * thread, thread pool, instance of an operating system, hardware accelerator, + * processor core, a cluster of processor cores, or another abstract processing + * entity with an independent program counter. In other words, an MTAPI node + * is an independent thread of control. + * + * Application software running on an MTAPI node must call mtapi_initialize() + * once per node. It is an error to call mtapi_initialize() multiple times + * from a given node, unless mtapi_finalize() is called in between. + * + * The values for \c domain_id and \c node_id must be known a priori by the + * application and MTAPI. + * + * \c mtapi_info is used to obtain information from the MTAPI implementation, + * including MTAPI and the underlying implementation version numbers, + * implementation vendor identification, the number of cores of a node, and + * vendor-specific implementation information. See the header files for + * additional information. + * + * A given MTAPI implementation will specify what is a node, i.e., how the + * concrete system is partitioned into nodes and what are the underlying units + * of execution executing tasks, e.g., threads, a thread pool, processes, or + * hardware units. + * + * \c attributes is a pointer to a node attributes object that was previously + * prepared with mtapi_nodeattr_init() and mtapi_nodeattr_set(). If + * \c attributes is \c MTAPI_NULL, then the following default + * attributes will be used: + * - all available cores will be used + * - maximum number of tasks is 1024 + * - maximum number of groups is 128 + * - maximum number of queues is 16 + * - maximum queue capacity is 1024 + * - maximum number of priorities is 4. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_NODE_INITFAILED | MTAPI environment could not be initialized. + * \c MTAPI_ERR_NODE_INITIALIZED | MTAPI environment was already initialized. + * \c MTAPI_ERR_NODE_INVALID | The \c node_id parameter is not valid. + * \c MTAPI_ERR_DOMAIN_INVALID | The \c domain_id parameter is not valid. + * \c MTAPI_ERR_PARAMETER | Invalid mtapi_node_attributes or mtapi_info. + * + * \see mtapi_nodeattr_init(), mtapi_nodeattr_set() + * + * \notthreadsafe + * \memory Allocates some memory depending on the node attributes. The amount + * allocated is returned in the mtapi_info structure. + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +void mtapi_initialize( + MTAPI_IN mtapi_domain_t domain_id, /**< [in] Domain id */ + MTAPI_IN mtapi_node_t node_id, /**< [in] Node id */ + MTAPI_IN mtapi_node_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_info_t* mtapi_info, /**< [out] Pointer to info struct */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Given a node and attribute number, returns a copy of the corresponding + * attribute value in \c *attribute. + * + * See mtapi_nodeattr_set() for a list of predefined attribute numbers and the + * sizes of the attribute values. The application is responsible for allocating + * sufficient space for the returned attribute value and for setting + * \c attribute_size to the exact size in bytes of the attribute value. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value + * will be written to \c *attribute. On error, \c *status is set to the + * appropriate error defined below and \c *attribute is undefined. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_nodeattr_set() + * + * \waitfree + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +void mtapi_node_get_attribute( + MTAPI_IN mtapi_node_t node, /**< [in] Node handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Finalizes the MTAPI environment on a given MTAPI node and domain. + * + * It has to be called by each node using MTAPI. It is an error to call + * mtapi_finalize() without first calling mtapi_initialize(). An MTAPI node + * can call mtapi_finalize() once for each call to mtapi_initialize(), but it + * is an error to call mtapi_finalize() multiple times from a given node + * unless mtapi_initialize() has been called prior to each mtapi_finalize() + * call. + * + * All tasks that have not completed and that have been started on the node + * where mtapi_finalize() is called will be canceled + * (see mtapi_task_cancel()). mtapi_finalize() blocks until all tasks that + * have been started on the same node return (long-running tasks already + * executing must actively poll the task state and return if canceled). Tasks + * that execute actions on the node where mtapi_finalize() is called, also + * block finalization of the MTAPI runtime system on that node. They are + * canceled as well and return with an \c MTAPI_ERR_NODE_NOTINIT status. Other + * functions that have a dependency to the node and that are called after + * mtapi_finalize() also return \c MTAPI_ERR_NODE_NOTINIT (e.g., + * mtapi_task_get() starting a task associated with an action implemented on + * the already-finalized node). + * + * mtapi_finalize() may not be called from an action function. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_NODE_FINALFAILED | The MTAPI environment couldn't be finalized. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_initialize(), mtapi_task_cancel(), mtapi_task_get() + * + * \notthreadsafe + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +void mtapi_finalize( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the domain id associated with the local node. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \returns Domain id of local node + * \waitfree + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +mtapi_domain_t mtapi_domain_id_get( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the node id associated with the local node and domain. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \returns Node id of local node + * \waitfree + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +mtapi_node_t mtapi_node_id_get( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- ACTIONS ------------------------------------------------------------ */ + +/** + * \defgroup ACTIONS Actions + * + * \ingroup C_MTAPI + * + * Hardware or software implementations of jobs. + * + * An action is referenced by an opaque handle of type \c mtapi_action_hndl_t, + * or indirectly through a handle to a job of type \c mtapi_job_hndl_t. A job + * refers to all actions implementing the same job, regardless of the node(s) + * where they are implemented. + * + * An action's lifetime begins when the application successfully calls + * mtapi_action_create() and obtains a handle to the action. Its lifetime ends + * upon successful completion of mtapi_action_delete() or mtapi_finalize(). + * + * While an opaque handle to an action may be used in the scope of one node + * only, a job can be used to refer to all its associated actions implementing + * the same job, regardless of the node where they are implemented. Tasks may + * be invoked in this way from nodes that do not share memory or even the same + * ISA with the node where the action resides. + */ + +/** + * This function initializes an action attributes object. + * + * A action attributes object is a container of action attributes, optionally + * passed to mtapi_action_create() to create an action with non-default + * attributes. + * + * The application is responsible for allocating the + * \c mtapi_action_attributes_t + * object and initializing it with a call to mtapi_actionattr_init(). The + * application may then call mtapi_actionattr_set() to specify action + * attribute values. Calls to mtapi_actionattr_init() have no effect on action + * attributes after the action has been created with mtapi_action_create(). The + * \c mtapi_action_attributes_t object may safely be deleted by the application + * after the call to mtapi_action_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set(), mtapi_action_create() + * + * \notthreadsafe + * \memberof mtapi_action_attributes_struct + */ +void mtapi_actionattr_init( + MTAPI_OUT mtapi_action_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets action attribute values in an action attributes object. + * + * An action attributes object is a container of action attributes, optionally + * passed to mtapi_action_create() to create an action with non-default + * attributes. + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. Additional attributes may + * be defined by the implementation. + * + * Calls to mtapi_actionattr_set() have no effect on action attributes after + * the action has been created. The \c mtapi_action_attributes_t object may + * safely be deleted by the application after the call to + * mtapi_action_create(). + * + * MTAPI-defined action attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
MTAPI_ACTION_GLOBALIndicates whether or not this is a globally visible action. Local + * actions are not shared with other nodes.mtapi_boolean_tMTAPI_TRUE
MTAPI_ACTION_AFFINITYCore affinity of action code.mtapi_affinity_tall cores set
MTAPI_DOMAIN_SHAREDIndicates whether or not the action is shareable across + * domains.mtapi_boolean_tMTAPI_TRUE
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_action_create() + * + * \notthreadsafe + * \memberof mtapi_action_attributes_struct + */ +void mtapi_actionattr_set( + MTAPI_INOUT mtapi_action_attributes_t* attributes, + /**< [in,out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function creates a software action (hardware actions are considered to + * be pre-existent and do not need to be created). + * + * It is called on the node where the action function is implemented. An + * action is an abstract encapsulation of everything needed to implement a + * job. An action contains attributes, a reference to a job, a reference to an + * action function, and a reference to node-local data. After an action is + * created, it is referenced by the application using a node-local handle of + * type \c mtapi_action_hndl_t, or indirectly through a node-local job handle + * of type \c mtapi_job_hndl_t. An action's life-cycle begins with + * mtapi_action_create(), and ends when mtapi_action_delete() or + * mtapi_finalize() is called. + * + * To create an action, the application must supply the domain-wide job ID of + * the job associated with the action. Job IDs must be predefined in the + * application and runtime, of type \c mtapi_job_id_t, which is an + * implementation-defined type. The job ID is unique in the sense that it is + * unique for the job implemented by the action. However several actions may + * implement the same job for load balancing purposes. + * + * For non-default behavior, \c *attributes must be prepared with + * mtapi_actionattr_init() and mtapi_actionattr_set() prior to calling + * mtapi_action_create(). If attributes is \c MTAPI_NULL, then default + * attributes will be used. + * + * If \c node_local_data_size is not zero, \c node_local_data specifies the + * start of node local data shared by action functions executed on the same + * node. \c node_local_data_size can be used by the runtime for cache coherency + * operations. + * + * On success, an action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the action already exists, \c status will + * be set to \c MTAPI_ERR_ACTION_EXISTS and the handle returned will not be a + * valid handle. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_JOB_INVALIDThe \c job_id is not a valid job ID, i.e., no action was created for + * that ID or the action has been deleted.
\c MTAPI_ERR_ACTION_EXISTSThis action is already created.
\c MTAPI_ERR_ACTION_LIMITExceeded maximum number of actions allowed.
\c MTAPI_ERR_ACTION_NOAFFINITYThe action was created with an \c MTAPI_ACTION_AFFINITY attribute + * that has set the affinity to all cores of the node to + * \c MTAPI_FALSE.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_ERR_PARAMETERInvalid attributes parameter.
+ * + * \see mtapi_actionattr_init(), mtapi_actionattr_set(), mtapi_action_delete(), + * mtapi_finalize() + * + * \returns Handle to newly created action, invalid handle on error + * \threadsafe + * \ingroup ACTIONS + */ +mtapi_action_hndl_t mtapi_action_create( + MTAPI_IN mtapi_job_id_t job_id, /**< [in] Job id */ + MTAPI_IN mtapi_action_function_t function, + /**< [in] Action function pointer */ + MTAPI_IN void* node_local_data, /**< [in] Data shared across tasks */ + MTAPI_IN mtapi_size_t node_local_data_size, + /**< [in] Size of shared data */ + MTAPI_IN mtapi_action_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function changes the value of the attribute that corresponds to the + * given \c attribute_num for this action. + * + * \c attribute must point to the attribute value, and \c attribute_size must + * be set to the exact size of the attribute value. See mtapi_actionattr_set() + * for a list of predefined attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set() + * + * \notthreadsafe + * \ingroup ACTIONS + */ +void mtapi_action_set_attribute( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the attribute value that corresponds to the given \c attribute_num + * for this action. + * + * \c attribute must point to the location where the attribute value is to be + * returned, and \c attribute_size must be set to the exact size of the + * attribute value. See mtapi_actionattr_set() for a list of predefined + * attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and \c *attribute is undefined. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set() + * + * \waitfree + * \ingroup ACTIONS + */ +void mtapi_action_get_attribute( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function deletes a software action (Hardware actions exist perpetually + * and cannot be deleted). + * + * mtapi_action_delete() may be called by any node that has a valid action + * handle. Tasks associated with an action that has been deleted may still be + * executed depending on their internal state: + * - If mtapi_action_delete() is called on an action that is currently + * executing, the associated task's state will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * action functions must poll the task state with + * mtapi_context_taskstate_get(). A call to mtapi_task_wait() on the task + * executing this code will return the status set by + * mtapi_context_status_set(), or \c MTAPI_SUCCESS if not explicitly set. + * - Tasks that are started or enqueued but waiting for execution by the + * MTAPI runtime when mtapi_action_delete() is called will not be executed + * anymore if the deleted action is the only action associated with that + * task. A call to mtapi_task_wait() will return the status + * \c MTAPI_ERR_ACTION_DELETED. + * - Tasks that are started or enqueued after deletion of the action will + * return \c MTAPI_ERR_ACTION_INVALID if the deleted action is the only + * action associated with that task. + * + * Calling mtapi_action_get_attribute() on a deleted action will return + * \c MTAPI_ERR_ACTION_INVALID if all actions implementing the job had been + * deleted. + * + * The function mtapi_action_delete() blocks until the corresponding action + * code is left by all tasks that are executing the code or until the timeout + * is reached. If \c timeout is a constant 0 or the symbolic constant + * \c MTAPI_NOWAIT, this function only returns \c MTAPI_SUCCESS if no tasks are + * executing the action when it is called. If it is set to \c MTAPI_INFINITE, + * the function may block infinitely. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_TIMEOUT | Timeout was reached. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait() + * + * \threadsafe + * \ingroup ACTIONS + */ +void mtapi_action_delete( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function disables an action. + * + * Tasks associated with an action that has been disabled may still be + * executed depending on their internal state: + * - If mtapi_action_disable() is called on an action that is currently + * executing, the associated task's state will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * action functions must poll the task with mtapi_context_taskstate_get(). + * A call to mtapi_task_wait() on the task executing this code will return + * the status set by mtapi_context_status_set(), or \c MTAPI_SUCCESS if not + * explicitly set. + * - Tasks that are started or enqueued but waiting for execution by the + * MTAPI runtime when mtapi_action_disable() is called will not be executed + * anymore if the disabled action is the only action associated with that + * task. A call to mtapi_task_wait() will return the status + * \c MTAPI_ERR_ACTION_DISABLED. + * - Tasks that are started or enqueued after the action has been disabled + * will return \c MTAPI_ERR_ACTION_DISABLED if either the disabled action is + * the only action associated with a task or all actions associated with a + * task are disabled. + * mtapi_action_disable() blocks until all running tasks exit the code, or + * until the timeout is reached. If timeout is the constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function only returns \c MTAPI_SUCCESS if no + * tasks are executing the action when it is called. If it is set to + * \c MTAPI_INFINITE the function may block infinitely. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_TIMEOUT | Timeout was reached. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait() + * + * \waitfree + * \ingroup ACTIONS + */ +void mtapi_action_disable( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function enables a previously disabled action. + * + * If this function is called on an action that no longer exists, an + * \c MTAPI_ERR_ACTION_INVALID error will be returned. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \waitfree + * \ingroup ACTIONS + */ +void mtapi_action_enable( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * \defgroup ACTION_FUNCTIONS Action Functions + * + * \ingroup C_MTAPI + * + * Executable software functions that implement actions. + * + * The runtime passes arguments to the action function when a task is started. + * Passing arguments from one node to another node should be implemented as a + * copy operation. Just as the arguments are passed before start of execution, + * the result buffer is copied back to the calling node after the action + * function terminates. In shared memory environments, the copying of data in + * both cases is not necessary. The node-local data is data used by several + * action functions being executed on the same node (or at least in the same + * address space). The shared data is specified when the action is created. + * + * An action function can interact with the runtime environment through a task + * context object of type \c mtapi_task_context_t. A task context object is + * allocated and managed by the runtime. The runtime passes a pointer to the + * context object when the action function is invoked.The action may then + * query information about the execution context(e.g., its core number, the + * number of tasks and task number in a multi - instance task, polling the + * task state) by calling the \c mtapi_context_* functions. Furthermore it is + * possible to pass information from the action function to the runtime system + * which is executing the action function(setting the status manually, for + * example). All of these \c mtapi_context_* functions are called in the + * context of task execution. + */ + +/** + * This function can be called from an action function to set the status that + * can be obtained by a subsequent call to mtapi_task_wait() or + * mtapi_group_wait_any(). + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * The status can be passed from the action function to the runtime system by + * setting error_code to one of the following values: + * - \c MTAPI_SUCCESS for successful completion + * - \c MTAPI_ERR_ACTION_CANCELLED if the action execution is canceled + * - \c MTAPI_ERR_ACTION_FAILED if the task could not be completed as intended + * The error code will be especially important in future versions of MTAPI + * where tasks shall be chained (flow graphs). The chain execution can then be + * aborted if the error code is not \c MTAPI_SUCCESS. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \see mtapi_task_wait(), mtapi_group_wait_any() + * + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +void mtapi_context_status_set( + MTAPI_INOUT mtapi_task_context_t* task_context, + /**< [in,out] Pointer to task context */ + MTAPI_IN mtapi_status_t error_code, /**< [in] Task return value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to notify the runtime + * system. + * + * This is used to communicate certain states to the runtime implementation to + * allow it to optimize task execution. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * The underlying type \c mtapi_notification_t and the valid values for + * notification are implementation-defined. The notification system is meant + * to be flexible, and can be used in many ways, for example: + * - To trigger prefetching of data for further processing + * - To order execution via queues there might be point in the action code + * where the next task in the queue may be started, even if the current + * code, started from the same queue, is still executing + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +void mtapi_context_runtime_notify( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_IN mtapi_notification_t notification, + /**< [in] Notification id */ + MTAPI_IN void* data, /**< [in] Pointer to associated data */ + MTAPI_IN mtapi_size_t data_size, /**< [in] Size of data */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * An action function may call this function to obtain the state of the task + * that is associated with the action function. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * The underlying representation of type \c mtapi_task_state_t is + * implementation-defined. Values of type \c mtapi_task_state_t may be copied, + * assigned, and compared with other values of type \c mtapi_task_state_t, but + * the caller should make no other assumptions about its type or contents. A + * minimal implementation must return a status of \c MTAPI_TASK_CANCELLED if + * the task is canceled, and \c MTAPI_TASK_RUNNING otherwise. Other values of + * the task state are implementation-defined. This task state can be used to + * abort a long running computation inside an action function. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Task state of current context + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_task_state_t mtapi_context_taskstate_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to query the instance + * number of the associated task. + * + * A task can have multiple instances (multi-instance tasks), in which case + * the same job is executed multiple times in parallel. Each instance has a + * number, and this function gives the instance number. Task instances are + * numbered sequentially, starting at zero. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the task instance + * number is returned. On error, \c *status is set to the appropriate error + * defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Instance number of current task + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_uint_t mtapi_context_instnum_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to query the total + * number of parallel task instances. + * + * This value is greater than one for multi-instance tasks. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Total number of parallel task instances + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_uint_t mtapi_context_numinst_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to query the current + * core number for debugging purposes. + * + * The core numbering is implementation-defined. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it was invoked. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Worker thread index the current task is running on + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_uint_t mtapi_context_corenum_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- CORE AFFINITY MASKS ------------------------------------------------ */ + +/** + * \defgroup CORE_AFFINITY_MASKS Core Affinities + * + * \ingroup C_MTAPI + * + * Affinities for executing action functions on subsets of cores. + * + * To set core affinities, the application must allocate an affinity mask + * object of type \c mtapi_affinity_t and initialize it with a call to + * mtapi_affinity_init(). Affinities are specified by calling + * mtapi_affinity_set(). The application must also allocate and initialize an + * action attributes object of type \c mtapi_action_attributes_t. The affinity + * mask object is then passed to mtapi_actionattr_set() to set the prescribed + * affinities in the action attributes object. The action attributes object is + * then passed to mtapi_action_create() to create a new action with those + * attributes. + * + * It is in the nature of core affinities to be highly hardware dependent. The + * least common denominator for different architectures is enabling and + * disabling core numbers in the affinity mask. Action-to-core affinities can + * be set via the action attribute \c MTAPI_ACTION_AFFINITY during the creation + * of an action. + */ + +/** + * This function initializes an affinity mask object. + * + * The affinity to all cores will be initialized to the value of affinity. + * This function should be called prior to calling mtapi_affinity_set() to + * specify non-default affinity settings. The affinity mask object may then + * be used to set the \c MTAPI_ACTION_AFFINITY attribute when creating an + * action with mtapi_action_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_AFFINITY_MASK | Invalid mask parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_affinity_set(), mtapi_action_create() + * + * \notthreadsafe + * \ingroup CORE_AFFINITY_MASKS + */ +void mtapi_affinity_init( + MTAPI_OUT mtapi_affinity_t* mask, /**< [out] Pointer to affinity mask */ + MTAPI_IN mtapi_boolean_t affinity, /**< [in] Initial affinity */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function is used to change the default values of an affinity mask + * object. + * + * The affinity mask object can then be passed to mtapi_actionattr_set() to + * set the \c MTAPI_ACTION_AFFINITY action attribute. An action function will + * be executed on a core only if the core's affinity is set to \c MTAPI_TRUE. + * Calls to mtapi_affinity_set() have no effect on action attributes after the + * action has been created. + * + * \c mask must be a pointer to an affinity mask object previously initialized + * with mtapi_affinity_init(). + * + * The \c core_num is a hardware- and implementation-specific numeric + * identifier for a single core of the current node. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_AFFINITY_MASK | Invalid mask parameter. + * \c MTAPI_ERR_CORE_NUM | Unknown core number. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set(), mtapi_affinity_init() + * + * \notthreadsafe + * \ingroup CORE_AFFINITY_MASKS + */ +void mtapi_affinity_set( + MTAPI_INOUT mtapi_affinity_t* mask, /**< [in, out] Pointer to affinity + mask */ + MTAPI_IN mtapi_uint_t core_num, /**< [in] Core number */ + MTAPI_IN mtapi_boolean_t affinity, /**< [in] Affinity to given core */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the affinity that corresponds to the given \c core_num for this + * affinity mask. + * + * \c mask is a pointer to an affinity mask object previously initialized with + * mtapi_affinity_init(). + * + * Note that affinities may be queried but may not be changed for an action + * after it has been created. If affinities need to be modified at runtime, + * new actions must be created. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_AFFINITY_MASK | Invalid mask parameter. + * \c MTAPI_ERR_CORE_NUM | Unknown core number. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_affinity_init() + * + * \returns \c MTAPI_TRUE if affinity to \c core_num is set, \c MTAPI_FALSE + * otherwise + * \waitfree + * \ingroup CORE_AFFINITY_MASKS + */ +mtapi_boolean_t mtapi_affinity_get( + MTAPI_OUT mtapi_affinity_t* mask, /**< [out] Pointer to affinity mask */ + MTAPI_IN mtapi_uint_t core_num, /**< [in] Core number */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- QUEUES ------------------------------------------------------------- */ + +/** + * \defgroup QUEUES Queues + * + * \ingroup C_MTAPI + * + * Queues for controlling the scheduling policy of tasks. + * + * The default scheduling policy for queues is ordered task execution. Tasks + * that have to be executed sequentially are enqueued into the same queue. In + * this case every queue is associated with exactly one action. Tasks started + * via different queues can be executed in parallel. This is needed for packet + * processing applications, for example: each stream is processed by one + * queue. This ensures sequential processing of packets belonging to the same + * stream. Different streams are processed in parallel. + * + * Queues were made explicit in MTAPI. This allows mapping of queues onto + * hardware queues, if available. One MTAPI queue is associated with one + * action, or for purposes of load balancing, with actions implementing the + * same job on different nodes. + */ + +/** + * This function initializes a queue attributes object. + * + * A queue attributes object is a container of queue attributes, optionally + * passed to mtapi_queue_create() to create a queue with non-default + * attributes. + * + * The application is responsible for allocating the + * \c mtapi_queue_attributes_t + * object and initializing it with a call to mtapi_queueattr_init(). The + * application may then call mtapi_queueattr_set() to specify queue attribute + * values. Calls to mtapi_queueattr_init() have no effect on queue attributes + * after the queue has been created. To change an attribute of an existing + * queue, see mtapi_queue_set_attribute(). The \c mtapi_queue_attributes_t + * object may safely be deleted by the application after the call to + * mtapi_queue_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queue_create(), mtapi_queueattr_set(), + * mtapi_queue_set_attribute() + * + * \notthreadsafe + * \memberof mtapi_queue_attributes_struct + */ +void mtapi_queueattr_init( + MTAPI_OUT mtapi_queue_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets queue attribute values in a queue attributes object. + * + * A queue attributes object is a container of queue attributes, optionally + * passed to mtapi_queue_create() to create a queue with non-default + * attributes. + * + * \c attributes must be a pointer to a queue attributes object previously + * initialized by mtapi_queueattr_init(). + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. Additional attributes may + * be defined by the implementation. + * + * Calls to mtapi_queueattr_set() have no effect on queue attributes once the + * queue has been created. The \c mtapi_queue_attributes_t object may safely be + * deleted by the application after the call to mtapi_queue_create(). + * + * MTAPI-defined queue attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
\c MTAPI_QUEUE_GLOBALIndicates if this is a globally visible queue. Only global queues + * are shared with other nodes.\c mtapi_boolean_t\c MTAPI_TRUE
\c MTAPI_QUEUE_PRIORITYPriority of the queue.\c mtapi_uint_t0(default priority)
MTAPI_QUEUE_LIMITMax. number of elements in the queue; the queue blocks on queuing + * more items.\c mtapi_uint_t0(0 stands for 'unlimited')
\c MTAPI_QUEUE_ORDEREDSpecify if the queue is order-preserving.\c mtapi_boolean_t\c MTAPI_TRUE
\c MTAPI_QUEUE_RETAINAllow enqueuing of jobs when queue is disabled.\c mtapi_boolean_t\c MTAPI_FALSE
\c MTAPI_DOMAIN_SHAREDIndicates if the queue is shareable across domains.\c mtapi_boolean_t\c MTAPI_TRUE
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queue_create(), mtapi_queueattr_init() + * + * \notthreadsafe + * \memberof mtapi_queue_attributes_struct + */ +void mtapi_queueattr_set( + MTAPI_INOUT mtapi_queue_attributes_t* attributes, + /**< [in,out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function creates a software queue object and associates it with the + * specified job. + * + * A job is associated with one or more actions that provide the executable + * implementation of the job. Hardware queues are considered to be pre-existent + * and do not need to be created. + * + * \c queue_id is an identifier of implementation-defined type that must be + * supplied by the application. If \c queue_id is set to + * \c MTAPI_QUEUE_ID_NONE, the queue will be accessible only on the node on + * which it was created by using the returned queue handle. Otherwise the + * application may supply a \c queue_id by which the queue can be referenced + * domain-wide using mtapi_queue_get() to convert the id into a handle. The + * minimum and maximum values for \c queue_id may be derived from + * \c MTAPI_MIN_USER_QUEUE_ID and \c MTAPI_MAX_USER_QUEUE_ID. + * + * job is a handle to a job obtained by a previous call to mtapi_job_get(). If + * \c attributes is \c MTAPI_NULL, the queue will be created with default + * attribute values. Otherwise \c attributes must point to a queue attributes + * object previously prepared using mtapi_queueattr_init() and + * mtapi_queueattr_set(). + * + * There is an implementation-defined maximum number of queues permitted. + * + * If more than one action is associated with the job, the runtime system + * chooses dynamically which action is used for execution (for load balancing + * purposes). + * + * On success, a queue handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the queue already exists, \c *status will + * be set to \c MTAPI_QUEUE_EXISTS and the handle returned will not be a + * valid handle. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | The \c queue_id is not a valid queue id. + * \c MTAPI_ERR_QUEUE_EXISTS | This queue is already created. + * \c MTAPI_ERR_QUEUE_LIMIT | Exceeded maximum number of queues allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_JOB_INVALID | The associated job is not valid. + * + * \see mtapi_queue_get(), mtapi_job_get(), mtapi_queueattr_init(), + * mtapi_queueattr_set() + * + * \returns Handle to newly created queue, invalid handle on error + * \threadsafe + * \ingroup QUEUES + */ +mtapi_queue_hndl_t mtapi_queue_create( + MTAPI_IN mtapi_queue_id_t queue_id, /**< [in] Queue id */ + MTAPI_IN mtapi_job_hndl_t job, /**< [in] Job handle */ + MTAPI_IN mtapi_queue_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Changes the attribute value that corresponds to the given \c attribute_num + * for the specified queue. + * + * See mtapi_queueattr_set() for a list of predefined attribute numbers and + * the sizes of the attribute values. The application must set + * \c attribute_size to the exact size in bytes of the attribute value. + * Additional attributes may be defined by the implementation. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below and the attribute value is + * undefined. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queueattr_set() + * + * \notthreadsafe + * \ingroup QUEUES + */ +void mtapi_queue_set_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the attribute value that corresponds to the given \c attribute_num + * for the specified queue. + * + * \c attribute must point to a location in memory sufficiently large to hold + * the returned attribute value. See mtapi_queueattr_set() for a list of + * predefined attribute numbers and the sizes of the attribute values. The + * application must set \c attribute_size to the exact size in bytes of the + * attribute value. Additional attributes may be defined by the implementation. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and the \c *attribute value is undefined. If this + * function is called on a queue that no longer exists, an + * \c MTAPI_ERR_QUEUE_INVALID error will be returned. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queueattr_set() + * + * \waitfree + * \ingroup QUEUES + */ +void mtapi_queue_get_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function converts a domain-wide \c queue_id into a node-local queue + * handle. + * + * \c queue_id must match the \c queue_id that was associated with a software + * queue that was created with mtapi_queue_create(), or it must be a valid + * predefined queue identifier known a priori to the runtime and application + * (e.g., to reference a hardware queue. The minimum and maximum values for + * \c queue_id may be derived from \c MTAPI_MIN_USER_QUEUE_ID and + * \c MTAPI_MAX_USER_QUEUE_ID. + * + * On success, the queue handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. If this function is called on a queue that no longer exists, + * an \c MTAPI_ERR_QUEUE_INVALID error will be returned. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_QUEUE_INVALIDThe \c queue_id parameter does not refer to a valid queue or it is set + * to \c MTAPI_QUEUE_ID_ANY.
\c MTAPI_ERR_NODE_NOTINITThe node/domain is not initialized.
\c MTAPI_ERR_DOMAIN_NOTSHAREDThis resource cannot be shared by this domain.
+ * + * \see mtapi_queue_create() + * + * \returns Handle to preexisting queue with given \c queue_id, + * invalid handle on error + * \threadsafe + * \ingroup QUEUES + */ +mtapi_queue_hndl_t mtapi_queue_get( + MTAPI_IN mtapi_queue_id_t queue_id, /**< [in] Queue id */ + MTAPI_IN mtapi_domain_t domain_id, /**< [in] Domain id */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function deletes the specified software queue. + * + * Hardware queues are perpetual and cannot be deleted. + * + * \c queue must be a valid handle to an existing queue. + * + * \c timeout determines how long the function should wait for tasks already + * started via that queue to finish. The underlying type of \c mtapi_timeout_t + * is implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function deletes the queue and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * This function can be called from any node that has a valid queue handle. + * Tasks previously enqueued in a queue that has been deleted may still be + * executed depending on their internal state: + * - If mtapi_queue_delete() is called on a queue that is currently executing + * an action, the task state of the corresponding task will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * the action function must poll the task state with + * mtapi_context_taskstate_get(). A call to mtapi_task_wait() on the task + * executing this code will return the status set by + * mtapi_context_status_set(), or \c MTAPI_SUCCESS if not explicitly set. + * - Tasks that are enqueued and waiting for execution by the MTAPI runtime + * environment when mtapi_queue_delete() is called will not be executed any + * more. A call to mtapi_task_wait() will return the status + * \c MTAPI_ERR_QUEUE_DELETED. + * - Tasks that are enqueued after deletion of the queue will return a status + * of \c MTAPI_ERR_QUEUE_INVALID. + * + * If this function is called on a queue that no longer exists, an + * \c MTAPI_ERR_QUEUE_INVALID status will be returned. A call to + * mtapi_queue_get() on a deleted queue will return \c MTAPI_ERR_QUEUE_INVALID + * as well, as long as no new queue has been created for the same queue ID. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_TIMEOUT | Timeout was reached. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait(), mtapi_queue_get() + * + * \threadsafe + * \ingroup QUEUES + */ +void mtapi_queue_delete( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function disables the specified queue in such a way that it can be + * resumed later. + * + * This is needed to perform certain maintenance tasks. It can be called by + * any node that has a valid queue handle. + * + * \c timeout determines how long the function should wait for tasks already + * started via that queue to finish. The underlying type of \c mtapi_timeout_t + * is implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function deletes the queue and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * Tasks previously enqueued in a queue that has been disabled may still be + * executed depending on their internal state: + * - If mtapi_queue_disable() is called on a queue that is currently executing + * an action, the task state of the corresponding task will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * the action function must poll the task state by calling + * mtapi_context_taskstate_get(). A call to mtapi_task_wait() on the task + * executing this code will return the status set by + * mtapi_context_status_set(), or \c MTAPI_SUCCESS if not explicitly set. + * - Tasks that are enqueued and waiting for execution by the MTAPI runtime + * environment when mtapi_queue_disable() is called will not be executed + * anymore. They will be held in anticipation the queue is enabled again + * if the \c MTAPI_QUEUE_RETAIN attribute is set to \c MTAPI_TRUE. A call to + * mtapi_task_wait() will return the status \c MTAPI_ERR_QUEUE_DISABLED. + * - Tasks that are enqueued after the queue had been disabled will return + * \c MTAPI_ERR_QUEUE_DISABLED if the \c MTAPI_QUEUE_RETAIN attribute is set + * to \c MTAPI_FALSE. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_TIMEOUT | Timeout was reached. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait() + * + * \threadsafe + * \ingroup QUEUES + */ +void mtapi_queue_disable( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function may be called from any node with a valid queue handle to + * re-enable a queue previously disabled with mtapi_queue_disable(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \threadsafe + * \ingroup QUEUES + */ +void mtapi_queue_enable( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- JOBS --------------------------------------------------------------- */ + +/** + * \defgroup JOBS Jobs + * + * \ingroup C_MTAPI + * + * Jobs implementing one or more actions. + * + * An action is a hardware or software implementation of a job. In some cases, + * an action is referenced by an action handle, while in other cases, an action + * is referenced indirectly through a job handle. Each job is represented by a + * domain-wide job ID, or by a job handle which is local to one node. + * + * Several actions can implement the same job based on different hardware + * resources (for instance a job can be implemented by one action on a DSP and + * by another action on a general purpose core, or a job can be implemented by + * both hardware and software actions). + */ + +/** + * Given a \c job_id, this function returns the MTAPI handle for referencing + * the actions implementing the job. + * + * This function converts a domain-wide job ID into a node-local job handle. + * + * On success, the action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_JOB_INVALID | The job ID does not refer to a valid action. + * \c MTAPI_ERR_DOMAIN_NOTSHARED | The resource can't be shared by this domain. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \returns Handle to job with given \c job_id, invalid handle on error + * \threadsafe + * \ingroup JOBS + */ +mtapi_job_hndl_t mtapi_job_get( + MTAPI_IN mtapi_job_id_t job_id, /**< [in] Job id */ + MTAPI_IN mtapi_domain_t domain_id, /**< [in] Domain id */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- TASKS -------------------------------------------------------------- */ + +/** + * \defgroup TASKS Tasks + * + * \ingroup C_MTAPI + * + * Tasks representing pieces of work "in flight" (similar to a thread handles). + * + * A task is associated with a job object, which is associated with one or + * more actions implementing the same job for load balancing purposes. + * A task may optionally be associated with a task group. A task has + * attributes, and an internal state. A task begins its lifetime with a call + * to mtapi_task_start() or mtapi_task_enqueue(). A + * task is referenced by a handle of type \c mtapi_task_hndl_t. The underlying + * type of \c mtapi_task_hndl_t is implementation defined. Task handles may be + * copied, assigned, and passed as arguments, but otherwise the application + * should make no assumptions about the internal representation of a task + * handle. + * + * Once a task is started, it is possible to wait for task completion from + * other parts of the program. + */ + +/** + * This function initializes a task attributes object. + * + * A task attributes object is a container of task attributes. It is an + * optional argument passed to mtapi_task_start() or mtapi_task_enqueue() to + * specify non-default task attributes when starting a task. + * + * To set task attributes to non-default values, the application must allocate + * a task attributes object of type \c mtapi_task_attributes_t and initialize + * it with a call to mtapi_taskattr_init(). The application may call + * mtapi_taskattr_set() to specify attribute values. Calls to + * mtapi_taskattr_init() have no effect on task attributes after the task has + * started. The \c mtapi_task_attributes_t object may safely be deleted by the + * application after the task has started. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_taskattr_set() + * + * \notthreadsafe + * \memberof mtapi_task_attributes_struct + */ +void mtapi_taskattr_init( + MTAPI_OUT mtapi_task_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets task attribute values in a task attributes object. + * + * A task attributes object is a container of task attributes, optionally + * passed to mtapi_task_start() or mtapi_task_enqueue() to specify non-default + * task attributes when starting a task. + * + * attributes is a pointer to a task attributes object that was previously + * initialized with a call to mtapi_taskattr_init(). Calls to + * mtapi_taskattr_set() have no effect on task attributes after the task has + * been created. The task attributes object may safely be deleted by the + * application after the task has started. + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set attribute_size to + * the exact size in bytes of the attribute value. Additional attributes may + * be defined by the implementation. + * + * MTAPI-defined task attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
\c MTAPI_TASK_DETACHEDIndicates if this is a detached task. A detached task is deleted by + * MTAPI runtime after execution. The task handle of detached tasks + * must not be used, i.e., it is not possible to wait for completion + * of dedicated detached tasks. But it is possible to add detached + * tasks to a group and wait for completion of the group.\c mtapi_boolean_t\c MTAPI_FALSE
\c MTAPI_TASK_INSTANCESIndicates how many parallel instances of task shall be started by + * MTAPI. The default case is that each task is executed exactly once. + * Setting this value to n, the corresponding action code will be + * executed n times, in parallel, if the underlying hardware allows it. + * (see chapter 4.1.7 Multi-Instance Tasks, page 107)\c mtapi_uint_t1
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_taskattr_init() + * + * \notthreadsafe + * \memberof mtapi_task_attributes_struct + */ +void mtapi_taskattr_set( + MTAPI_INOUT mtapi_task_attributes_t* attributes, + /**< [in, out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function schedules a task for execution. + * + * A task is associated with a job. A job is associated with one or more + * actions. An action provides an action function, which is the executable + * implementation of a job. If more than one action is associated with the job, + * the runtime system chooses dynamically which action is used for execution + * for load balancing purposes. + * + * \c task_id is an optional ID provided by the application for debugging + * purposes. If not needed, it can be set to \c MTAPI_TASK_ID_NONE. The minimum + * and maximum values for \c task_id may be derived from + * \c MTAPI_MIN_USER_TASK_ID and \c MTAPI_MAX_USER_TASK_ID. + * + * \c job must be a handle to a job obtained by a previous call to + * mtapi_job_get(). + * + * If \c arguments_size is not zero, then arguments must point to data of + * \c arguments_size bytes. The arguments will be transferred by the runtime + * from the node where the action was created to the executing node if + * necessary. Marshalling of arguments is not part of the MTAPI specification + * and is implementation-defined. + * + * If \c attributes is \c MTAPI_NULL, the task will be started with default + * attribute values. Otherwise \c attributes must point to a task attributes + * object previously prepared using mtapi_taskattr_init() and + * mtapi_taskattr_set(). The attributes of a task cannot be changed after the + * task is created. + * + * \c group must be set to \c MTAPI_GROUP_NONE if the task is not part of a + * task group. Otherwise \c group must be a group handle obtained by a previous + * call to mtapi_group_create(). + * + * On success, a task handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_TASK_LIMIT | Exceeded maximum number of tasks allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_JOB_INVALID | The associated job is not valid. + * + * \see mtapi_job_get(), mtapi_taskattr_init(), mtapi_taskattr_set(), + * mtapi_group_create() + * + * \returns Handle to newly started task, invalid handle on error + * \threadsafe + * \ingroup TASKS + */ +mtapi_task_hndl_t mtapi_task_start( + MTAPI_IN mtapi_task_id_t task_id, /**< [in] Task id */ + MTAPI_IN mtapi_job_hndl_t job, /**< [in] Job handle */ + MTAPI_IN void* arguments, /**< [in] Pointer to arguments */ + MTAPI_IN mtapi_size_t arguments_size,/**< [in] Size of arguments */ + MTAPI_OUT void* result_buffer, /**< [out] Pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /**< [in] Size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle, may be + \c MTAPI_GROUP_NONE */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function schedules a task for execution using a queue. + * + * A queue is a task associated with a job. A job is associated with one or + * more actions. An action provides an action function, which is the executable + * implementation of a job. + * + * \c task_id is an optional ID provided by the application for debugging + * purposes. If not needed, it can be set to \c MTAPI_TASK_ID_NONE. The + * underlying type of \c mtapi_task_id_t is implementation-defined. The + * minimum and maximum values for \c task_id may be derived from + * \c MTAPI_MIN_USER_TASK_ID and \c MTAPI_MAX_USER_TASK_ID. + * + * \c queue must be a handle to a queue obtained by a previous call to + * mtapi_queue_create(). + * + * If \c arguments_size is not zero, then arguments must point to data of + * \c arguments_size bytes. The arguments will be transferred by the runtime + * from the node where the action was created to the executing node. + * Marshalling of arguments is not part of the MTAPI specification and is + * implementation-defined. + * + * If \c attributes is \c MTAPI_NULL, the task will be started with default + * attribute values. Otherwise \c attributes must point to a task attributes + * object previously prepared using mtapi_taskattr_init() and + * mtapi_taskattr_set(). Once a task has been enqueued, its attributes may not + * be changed. + * + * \c group must be set to \c MTAPI_GROUP_NONE if the task is not part of a + * task group. Otherwise \c group must be a group handle obtained by a + * previous call to mtapi_group_create(). + * + * On success, a task handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_TASK_LIMIT | Exceeded maximum number of tasks allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * + * \see mtapi_queue_create(), mtapi_taskattr_init(), mtapi_taskattr_set(), + * mtapi_group_create() + * + * \returns Handle to newly enqueued task, invalid handle on error + * \threadsafe + * \ingroup TASKS + */ +mtapi_task_hndl_t mtapi_task_enqueue( + MTAPI_IN mtapi_task_id_t task_id, /**< [in] Task id */ + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN void* arguments, /**< [in] Pointer to arguments */ + MTAPI_IN mtapi_size_t arguments_size,/**< [in] Size of arguments */ + MTAPI_OUT void* result_buffer, /**< [out] Pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /**< [in] Size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + /**< [in] Pointer to task attributes */ + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle, may be + \c MTAPI_GROUP_NONE */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns a copy of the attribute value that corresponds to the given + * \c attribute_num for the specified task. + * + * The attribute value will be returned in \c *attribute. Note that task + * attributes may be queried but may not be changed after a task has been + * created. + * + * \c task must be a valid handle to a task that was obtained by a previous + * call to mtapi_task_start() or mtapi_task_enqueue(). + * + * See mtapi_taskattr_set() for a list of predefined attribute numbers + * and the sizes of the attribute values. The application is responsible for + * allocating sufficient space for the returned attribute value and for + * setting \c attribute_size to the exact size in bytes of the attribute value. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and the attribute value is undefined. If this function + * is called on a task that no longer exists, an \c MTAPI_ERR_TASK_INVALID + * error code will be returned. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_TASK_INVALID | Argument is not a valid task handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_taskattr_set() + * + * \waitfree + * \ingroup TASKS + */ +void mtapi_task_get_attribute( + MTAPI_IN mtapi_task_hndl_t task, /**< [in] Task handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function cancels a task and sets the task status to + * \c MTAPI_TASK_CANCELLED. + * + * \c task must be a valid handle to a task that was obtained by a previous + * call to mtapi_task_start() or mtapi_task_enqueue(). + * + * If the execution of a task has not been started, the runtime system might + * remove the task from the runtime-internal task queues. If task execution is + * already running, an action function implemented in software can poll the + * task status and react accordingly. + * + * Since the task is referenced by a task handle which can only be used + * node-locally, a task can be canceled only on the node where the task was + * created. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_TASK_INVALID | Argument is not a valid task handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue() + * + * \waitfree + * \ingroup TASKS + */ +void mtapi_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, /**< [in] Task handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function waits for the completion of the specified task. + * + * \c task must be a valid handle to a task that was obtained by a previous + * call to mtapi_task_start() or mtapi_task_enqueue(). The task handle becomes + * invalid on a successful wait, i.e., after the task had run to completion + * and mtapi_task_wait() returns \c MTAPI_SUCCESS. + * + * \c timeout determines how long the function should wait for tasks already + * started via that queue to finish. The underlying type of \c mtapi_timeout_t + * is implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function does not block and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation-defined. + * + * Results of completed tasks can be obtained via \c result_buffer associated + * with the task. The size of the buffer has to be equal to the result size + * written in the action code. If the result is not needed by the calling + * code, \c result_buffer may be set to \c MTAPI_NULL. For multi-instance + * tasks, the result buffer is filled by an array of all the task instances' + * results. I.e., the result buffer has to be allocated big enough (number of + * instances times size of result). + * + * Calling mtapi_task_wait() more than once for the same task results in + * undefined behavior. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. If this function is called on + * a task that no longer exists, an \c MTAPI_ERR_TASK_INVALID error code will + * be returned. \c status will be \c MTAPI_ERR_ARG_SIZE or + * \c MTAPI_ERR_RESULT_SIZE if the sizes of arguments or result buffer do + * not match. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_TASK_INVALIDArgument is not a valid task handle.
\c MTAPI_TIMEOUTTimeout was reached.
\c MTAPI_ERR_PARAMETERInvalid timeout parameter.
\c MTAPI_ERR_TASK_CANCELLEDThe task has been canceled because of mtapi_task_cancel() was + * called before the task was executed or the error code was set to + * \c MTAPI_ERR_TASK_CANCELLED by mtapi_context_status_set() in the + * action function.
\c MTAPI_ERR_WAIT_PENDINGmtapi_task_wait() had already been called for the same task and the + * first wait call is still pending.
\c MTAPI_ERR_ACTION_CANCELLEDAction execution was canceled by the action function + * (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_FAILEDError set by action function (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_DELETEDAll actions associated with the task have been deleted before the + * execution of the task was started or the error code has been set in + * the action function to \c MTAPI_ERR_ACTION_DELETED by + * mtapi_context_status_set().
\c MTAPI_ERR_ARG_SIZEThe size of the arguments expected by action differs from arguments + * size of the caller.
\c MTAPI_ERR_RESULT_SIZEThe size of the result buffer expected by action differs from + * result buffer size of the caller.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_task_wait(), + * mtapi_task_cancel(), mtapi_context_status_set() + * + * \threadsafe + * \ingroup TASKS + */ +void mtapi_task_wait( + MTAPI_IN mtapi_task_hndl_t task, /**< [in] Task handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- TASK GROUPS -------------------------------------------------------- */ + +/** + * \defgroup TASK_GROUPS Task Groups + * + * \ingroup C_MTAPI + * + * Facilities for synchronizing on groups of tasks. + * + * This concept is similar to barrier synchronization of threads. MTAPI + * specifies a minimal task group feature set in order to allow small and + * efficient implementations. + */ + +/** + * This function initializes a group attributes object. + * + * A group attributes object is a container of group attributes. It is an + * optional argument passed to mtapi_group_create() to specify non-default + * group attributes when creating a task group. + * + * To set group attributes to non-default values, the application must + * allocate a group attributes object of type \c mtapi_group_attributes_t and + * initialize it with a call to mtapi_groupattr_init(). The application may + * call mtapi_groupattr_set() to specify attribute values. Calls to + * mtapi_groupattr_init() have no effect on group attributes after the group + * has been created. The \c mtapi_group_attributes_t object may safely be + * deleted by the application after the call to mtapi_group_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_group_create(), mtapi_groupattr_set() + * + * \notthreadsafe + * \memberof mtapi_group_attributes_struct + */ +void mtapi_groupattr_init( + MTAPI_OUT mtapi_group_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets group attribute values in a group attributes object. + * + * A group attributes object is a container of group attributes, optionally + * passed to mtapi_group_create() to specify non-default group attributes when + * creating a task group. + * + * \c attributes is a pointer to a group attributes object that was previously + * initialized with a call to mtapi_groupattr_init(). Calls to + * mtapi_groupattr_set() have no effect on group attributes after the group + * has been created. The group attributes object may safely be deleted by the + * application after the call to mtapi_group_create(). + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. + * Additional attributes may be defined by the implementation. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_group_create(), mtapi_groupattr_init() + * + * \notthreadsafe + * \memberof mtapi_group_attributes_struct + */ +void mtapi_groupattr_set( + MTAPI_INOUT mtapi_group_attributes_t* attributes, + /**< [in,out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function creates a task group and returns a handle to the group. + * + * After a group is created, a task may be associated with a group when the + * task is started with mtapi_task_start() or mtapi_task_enqueue(). + * + * \c group_id is an optional ID provided by the application for debugging + * purposes. If not needed, it can be set to \c MTAPI_GROUP_ID_NONE. The + * underlying type of \c mtapi_group_id_t is implementation-defined. The + * minimum and maximum values for \c group_id may be derived from + * \c MTAPI_MIN_USER_GROUP_ID and \c MTAPI_MAX_USER_GROUP_ID. + * + * If \c attributes is \c MTAPI_NULL, the group will be created with default + * attribute values. Otherwise \c attributes must point to a group attributes + * object previously prepared using mtapi_groupattr_init() and + * mtapi_groupattr_set(). + * + * On success, a group handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_GROUP_LIMIT | Exceeded maximum number of groups allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_groupattr_init(), + * mtapi_groupattr_set() + * + * \returns Handle to newly created group, invalid handle on error + * \threadsafe + * \memory This function allocates a new queue for tracking completion of the + * tasks belonging to the group. + * \ingroup TASK_GROUPS + */ +mtapi_group_hndl_t mtapi_group_create( + MTAPI_IN mtapi_group_id_t group_id, /**< [in] Group id */ + MTAPI_IN mtapi_group_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Changes the value of the attribute that corresponds to the given + * \c attribute_num for the specified task group. + * + * \c attribute must point to the attribute value, and \c attribute_size must + * be set to the exact size of the attribute value. See mtapi_groupattr_set() + * for a list of predefined attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_groupattr_set() + * + * \notthreadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_set_attribute( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the attribute value that corresponds to the given \c attribute_num + * for this task group. + * + * \c attribute must point to the location where the attribute value is to be + * returned, and \c attribute_size must be set to the exact size of the + * attribute value. See mtapi_groupattr_set() for a list of predefined + * attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and \c *attribute is undefined. If this function is + * called on a group that no longer exists, an \c MTAPI_ERR_GROUP_INVALID + * error code will be returned. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_groupattr_set() + * + * \waitfree + * \ingroup TASK_GROUPS + */ +void mtapi_group_get_attribute( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function waits for the completion of a task group. + * + * Tasks may be associated with groups when the tasks are started. Each task + * is associated with one or more actions. This function returns when all the + * associated action functions have completed or canceled. The group handle + * becomes invalid if this function returns \c MTAPI_SUCCESS. + * + * \c timeout determines how long the function should wait for tasks already + * started in the group to finish. The underlying type of \c mtapi_timeout_t is + * implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function does not block and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * To obtain results from a task, the application should call + * mtapi_group_wait_any() instead. + * + * During execution, an action function may optionally call + * mtapi_context_status_set() to set a task status that will be returned in + * this function in \c *status. If multiple action functions set different task + * status values, it is implementation-defined which of those is returned in + * mtapi_group_wait_all(). The following task status values may be set by an + * action function: \c MTAPI_ERR_TASK_CANCELLED, \c MTAPI_ERR_ACTION_CANCELLED, + * \c MTAPI_ERR_ACTION_FAILED, and \c MTAPI_ERR_ACTION_DELETED. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_TIMEOUTTimeout was reached.
\c MTAPI_ERR_GROUP_INVALIDArgument is not a valid task handle.
\c MTAPI_ERR_WAIT_PENDINGmtapi_group_wait_all() had already been called for the same group + * and the first wait call is still pending.
\c MTAPI_ERR_PARAMETERInvalid timeout parameter.
\c MTAPI_ERR_ARG_SIZEThe size of the arguments expected by action differs from arguments + * size of the caller.
\c MTAPI_ERR_RESULT_SIZEThe size of the result buffer expected by action differs from + * result buffer size of the caller.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_GROUP_COMPLETEDGroup completed, i.e., there are no more task to wait for in + * the group.
\c MTAPI_ERR_TASK_CANCELLEDAt least one task has been canceled because of mtapi_task_cancel() + * was called before the task was executed or the error code was set + * to \c MTAPI_ERR_TASK_CANCELLED by mtapi_context_status_set() in the + * action function.
\c MTAPI_ERR_ACTION_CANCELLEDThe action execution of at least one task was canceled by the + * action function (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_FAILEDError set by at least one action function + * (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_DELETEDAll actions associated with the task have been deleted before the + * execution of the task was started or the error code has been set in + * the action function to \c MTAPI_ERR_ACTION_DELETED by + * mtapi_context_status_set().
+ * + * \see mtapi_group_wait_any(), mtapi_context_status_set(), mtapi_task_cancel() + * + * \threadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_wait_all( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function waits for the completion of any task in a task group. + * + * Tasks may be associated with groups when the tasks are started. Each task + * is associated with one or more actions. This function returns when any of + * the associated action functions have completed or have been canceled. + * + * The group handle does not become invalid if this function returns + * \c MTAPI_SUCCESS. The group handle becomes invalid if this function returns + * \c MTAPI_GROUP_COMPLETED. + * + * \c group must be a valid group handle obtained by a previous call to + * mtapi_group_create(). + * + * Action functions may pass results that will be available in \c *result after + * mtapi_group_wait_any() returns. If the results are not needed, \c result may + * be set to \c MTAPI_NULL. Otherwise, \c result must point to an area in + * memory of sufficient size to hold the array of results from the completed + * task(s). The size of the result buffer is given in the argument + * \c result_buffer_size that the runtime passes to an action function upon + * invocation. + * + * \c timeout determines how long the function should wait for a task in the + * group to finish. The underlying type of \c mtapi_timeout_t is + * implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function does not block and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * During execution, an action function may optionally call + * mtapi_context_status_set() to set a task status that will be returned in + * this function in \c *status. The following task status values may be set by + * an action function: \c MTAPI_ERR_TASK_CANCELLED, + * \c MTAPI_ERR_ACTION_CANCELLED, \c MTAPI_ERR_ACTION_FAILED, and + * \c MTAPI_ERR_ACTION_DELETED. + * + * On success, \c *status is either set to \c MTAPI_SUCCESS if one of the + * tasks in the group completed or to \c MTAPI_GROUP_COMPLETED if all tasks of + * the group have completed and successfully waited for. On error, \c *status + * is set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_TIMEOUTTimeout was reached.
\c MTAPI_ERR_GROUP_INVALIDArgument is not a valid task handle.
\c MTAPI_ERR_PARAMETERInvalid timeout parameter.
\c MTAPI_ERR_ARG_SIZEThe size of the arguments expected by action differs from arguments + * size of the caller.
\c MTAPI_ERR_RESULT_SIZEThe size of the result buffer expected by action differs from + * result buffer size of the caller.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_GROUP_COMPLETEDGroup completed, i.e., there are no more tasks to wait for in + * the group.
\c MTAPI_ERR_TASK_CANCELLEDThe task has been canceled because mtapi_task_cancel() was called + * before the task was executed, or the error code was set to + * \c MTAPI_ERR_TASK_CANCELLED by mtapi_context_status_set() in the + * action code.
\c MTAPI_ERR_ACTION_CANCELLEDAction execution was canceled by the action function + * (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_FAILEDError set by action function (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_DELETEDAll actions associated with the task have been deleted before the + * execution of the task was started or the error code has been set in + * the action code to \c MTAPI_ERR_ACTION_DELETED + * by mtapi_context_status_set().
+ * + * \see mtapi_group_create(), mtapi_context_status_set(), mtapi_task_cancel() + * + * \threadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_wait_any( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_OUT void** result, /**< [out] Pointer to result buffer + supplied at task start */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function deletes a task group. + * + * Deleting a group does not have any influence on tasks belonging to the + * group. Adding tasks to a group that is already deleted will result in an + * \c MTAPI_ERR_GROUP_INVALID error. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \threadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_delete( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/** + * \internal + * + * \defgroup INTERNAL Internal Implementation + * + * \ingroup C_MTAPI + * + * This section describes the internal implementation of the MTAPI interface. + */ + + +#ifdef __cplusplus +} +#endif + +#endif // EMBB_MTAPI_C_MTAPI_H_ diff --git b/mtapi_c/include/embb/mtapi/c/mtapi_ext.h a/mtapi_c/include/embb/mtapi/c/mtapi_ext.h new file mode 100644 index 0000000..54630b2 --- /dev/null +++ a/mtapi_c/include/embb/mtapi/c/mtapi_ext.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_C_MTAPI_EXT_H_ +#define EMBB_MTAPI_C_MTAPI_EXT_H_ + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup C_MTAPI_EXT MTAPI Extensions + * + * \ingroup C_MTAPI + * + * Provides extensions to the standard MTAPI API. + * + * There is a single extension function defined here to support user defined + * behavior of an action to allow for actions that are not implemented locally + * in software but e.g. on a remote node in a network or on an accelerator + * device like a GPU or FPGA. + */ + +/** + * Represents a callback function that is called when a plugin action is about + * to start a plugin task. + * This function should return MTAPI_SUCCESS if the task could be started and + * the appropriate MTAPI_ERR_* if not. + * + * \ingroup C_MTAPI_EXT + */ +typedef void(*mtapi_ext_plugin_task_start_function_t)( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status +); + +/** + * Represents a callback function that is called when a plugin task is about + * to be canceled. + * This function should return MTAPI_SUCCESS if the task could be canceled and + * the appropriate MTAPI_ERR_* if not. + * + * \ingroup C_MTAPI_EXT + */ +typedef void(*mtapi_ext_plugin_task_cancel_function_t)( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status +); + +/** + * Represents a callback function that is called when a plugin action is about + * to be finalized. + * This function should return MTAPI_SUCCESS if the action could be deleted and + * the appropriate MTAPI_ERR_* if not. + * + * \ingroup C_MTAPI_EXT + */ +typedef void(*mtapi_ext_plugin_action_finalize_function_t)( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status +); + +/** + * This function creates a plugin action. + * + * It is called on the node where the plugin action is implemented. A plugin + * action is an abstract encapsulation of a user defined action that is needed + * to implement a job that does not represent a software action. A plugin + * action contains a reference to a job, callback functions to start and cancel + * tasks and a reference to an callback function to finalize the action. + * After a plugin action is created, it is referenced by the application using + * a node-local handle of type \c mtapi_action_hndl_t, or indirectly through a + * node-local job handle of type \c mtapi_job_hndl_t. A plugin action's + * life-cycle begins with mtapi_ext_plugin_action_create(), and ends when + * mtapi_action_delete() or mtapi_finalize() is called. + * + * To create an action, the application must supply the domain-wide job ID of + * the job associated with the action. Job IDs must be predefined in the + * application and runtime, of type \c mtapi_job_id_t, which is an + * implementation-defined type. The job ID is unique in the sense that it is + * unique for the job implemented by the action. However several actions may + * implement the same job for load balancing purposes. + * + * If \c node_local_data_size is not zero, \c node_local_data specifies the + * start of node local data shared by action functions executed on the same + * node. \c node_local_data_size can be used by the runtime for cache coherency + * operations. + * + * On success, an action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the action already exists, \c status will + * be set to \c MTAPI_ERR_ACTION_EXISTS and the handle returned will not be a + * valid handle. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_JOB_INVALIDThe \c job_id is not a valid job ID, i.e., no action was created for + * that ID or the action has been deleted.
\c MTAPI_ERR_ACTION_EXISTSThis action is already created.
\c MTAPI_ERR_ACTION_LIMITExceeded maximum number of actions allowed.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \see mtapi_action_delete(), mtapi_finalize() + * + * \returns Handle to newly created plugin action, invalid handle on error + * \threadsafe + * \ingroup C_MTAPI_EXT + */ +mtapi_action_hndl_t mtapi_ext_plugin_action_create( + MTAPI_IN mtapi_job_id_t job_id, /**< [in] Job id */ + MTAPI_IN mtapi_ext_plugin_task_start_function_t task_start_function, + /**< [in] Task start function */ + MTAPI_IN mtapi_ext_plugin_task_cancel_function_t task_cancel_function, + /**< [in] Task cancel function */ + MTAPI_IN mtapi_ext_plugin_action_finalize_function_t action_finalize_function, + /**< [in] Finalize action function */ + MTAPI_IN void* plugin_data, + /**< [in] Pointer to plugin data */ + MTAPI_IN void* node_local_data, + /**< [in] Pointer to node local data */ + MTAPI_IN mtapi_size_t node_local_data_size, + /**< [in] Size of node local data */ + MTAPI_IN mtapi_action_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status/**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + + +#ifdef __cplusplus +} +#endif + + +#endif // EMBB_MTAPI_C_MTAPI_EXT_H_ diff --git b/mtapi_c/include/mtapi.h a/mtapi_c/include/mtapi.h new file mode 100644 index 0000000..7ac0853 --- /dev/null +++ a/mtapi_c/include/mtapi.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_H_ +#define MTAPI_H_ + +// Just includes the actual MTAPI header for standard conformity + +#include + +#endif // MTAPI_H_ diff --git b/mtapi_c/include/mtapi_ext.h a/mtapi_c/include/mtapi_ext.h new file mode 100644 index 0000000..1058ce4 --- /dev/null +++ a/mtapi_c/include/mtapi_ext.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_EXT_H_ +#define MTAPI_EXT_H_ + +// Just includes the actual MTAPI extension header for standard conformity + +#include + +#endif // MTAPI_EXT_H_ diff --git b/mtapi_c/src/embb_mtapi_action_plugin_t.c a/mtapi_c/src/embb_mtapi_action_plugin_t.c new file mode 100644 index 0000000..18e0841 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_action_plugin_t.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +mtapi_action_hndl_t mtapi_ext_plugin_action_create( + MTAPI_IN mtapi_job_id_t job_id, + MTAPI_IN mtapi_ext_plugin_task_start_function_t task_start_function, + MTAPI_IN mtapi_ext_plugin_task_cancel_function_t task_cancel_function, + MTAPI_IN mtapi_ext_plugin_action_finalize_function_t + action_finalize_function, + MTAPI_IN void* plugin_data, + MTAPI_IN void* node_local_data, + MTAPI_IN mtapi_size_t node_local_data_size, + MTAPI_IN mtapi_action_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_action_hndl_t action_handle = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_ext_plugin_action_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + /* check if job is valid */ + if (embb_mtapi_job_is_id_valid(node, job_id)) { + embb_mtapi_job_t* job = embb_mtapi_job_get_storage_for_id(node, job_id); + embb_mtapi_action_t* new_action = + embb_mtapi_action_pool_allocate(node->action_pool); + if (MTAPI_NULL != new_action) { + new_action->domain_id = node->domain_id; + new_action->node_id = node->node_id; + new_action->job_id = job_id; + new_action->node_local_data = node_local_data; + new_action->node_local_data_size = node_local_data_size; + new_action->enabled = MTAPI_TRUE; + new_action->is_plugin_action = MTAPI_TRUE; + embb_atomic_store_int(&new_action->num_tasks, 0); + + new_action->plugin_task_start_function = task_start_function; + new_action->plugin_task_cancel_function = task_cancel_function; + new_action->plugin_action_finalize_function = action_finalize_function; + new_action->plugin_data = (void*)plugin_data; + + /* set defaults if no attributes were given */ + if (MTAPI_NULL != attributes) { + new_action->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + /* use the default */ + mtapi_actionattr_init(&new_action->attributes, &local_status); + } + + /* check if affinity is sane */ + if (0 == new_action->attributes.affinity) { + local_status = MTAPI_ERR_PARAMETER; + } + + if (MTAPI_SUCCESS == local_status) { + action_handle = new_action->handle; + embb_mtapi_job_add_action(job, new_action); + } else { + embb_mtapi_action_pool_deallocate(node->action_pool, new_action); + } + } else { + /* no more space left in action pool */ + local_status = MTAPI_ERR_ACTION_LIMIT; + } + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return action_handle; +} diff --git b/mtapi_c/src/embb_mtapi_action_t.c a/mtapi_c/src/embb_mtapi_action_t.c new file mode 100644 index 0000000..dff3606 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_action_t.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +embb_mtapi_pool_implementation(action) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_action_initialize(embb_mtapi_action_t* that) { + that->action_function = NULL; + that->job_id = MTAPI_JOB_ID_INVALID; + that->domain_id = MTAPI_DOMAIN_ID_INVALID; + that->node_id = MTAPI_NODE_ID_INVALID; + that->enabled = MTAPI_FALSE; + that->node_local_data = NULL; + that->node_local_data_size = 0; + that->plugin_data = MTAPI_NULL; + embb_atomic_store_int(&that->num_tasks, 0); +} + +void embb_mtapi_action_finalize(embb_mtapi_action_t* that) { + if (that->is_plugin_action) { + // TODO(mw): check status + that->plugin_action_finalize_function(that->handle, NULL); + } + embb_mtapi_action_initialize(that); +} + +static mtapi_boolean_t embb_mtapi_action_delete_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_action_t * action = (embb_mtapi_action_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != action); + assert(MTAPI_NULL != task); + + if ( + task->action.id == action->handle.id && + task->action.tag == action->handle.tag) { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_ACTION_DELETED; + result = MTAPI_TRUE; + } + + return result; +} + +static mtapi_boolean_t embb_mtapi_action_disable_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_action_t * action = (embb_mtapi_action_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != action); + assert(MTAPI_NULL != task); + + if ( + task->action.id == action->handle.id && + task->action.tag == action->handle.tag) { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_ACTION_DISABLED; + result = MTAPI_TRUE; + } + + return result; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_action_hndl_t mtapi_action_create( + MTAPI_IN mtapi_job_id_t job_id, + MTAPI_IN mtapi_action_function_t action_function, + MTAPI_IN void* node_local_data, + MTAPI_IN mtapi_size_t node_local_data_size, + MTAPI_IN mtapi_action_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_action_hndl_t action_handle = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_action_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + /* check if job is valid */ + if (embb_mtapi_job_is_id_valid(node, job_id)) { + embb_mtapi_job_t* job = embb_mtapi_job_get_storage_for_id(node, job_id); + embb_mtapi_action_t* new_action = + embb_mtapi_action_pool_allocate(node->action_pool); + if (MTAPI_NULL != new_action) { + new_action->domain_id = node->domain_id; + new_action->node_id = node->node_id; + new_action->job_id = job_id; + new_action->node_local_data = node_local_data; + new_action->node_local_data_size = node_local_data_size; + new_action->enabled = MTAPI_TRUE; + new_action->is_plugin_action = MTAPI_FALSE; + embb_atomic_store_int(&new_action->num_tasks, 0); + + new_action->action_function = action_function; + + /* set defaults if no attributes were given */ + if (MTAPI_NULL != attributes) { + new_action->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + /* use the default */ + mtapi_actionattr_init(&new_action->attributes, &local_status); + } + + /* check if affinity is sane */ + if (0 == new_action->attributes.affinity) { + local_status = MTAPI_ERR_PARAMETER; + } + + if (MTAPI_SUCCESS == local_status) { + action_handle = new_action->handle; + embb_mtapi_job_add_action(job, new_action); + } else { + embb_mtapi_action_pool_deallocate(node->action_pool, new_action); + } + } else { + /* no more space left in action pool */ + local_status = MTAPI_ERR_ACTION_LIMIT; + } + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return action_handle; +} + + +void mtapi_action_set_attribute( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_set_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + mtapi_actionattr_set( + &local_action->attributes, + attribute_num, + attribute, + attribute_size, + &local_status); + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + + +void mtapi_action_get_attribute( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_ACTION_GLOBAL: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_action->attributes.global, attribute, attribute_size); + break; + + case MTAPI_ACTION_AFFINITY: + local_status = embb_mtapi_attr_get_mtapi_affinity_t( + &local_action->attributes.affinity, attribute, attribute_size); + break; + + case MTAPI_ACTION_DOMAIN_SHARED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_action->attributes.domain_shared, + attribute, + attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + + +void mtapi_action_delete( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_delete() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* cancel all tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_action_delete_visitor, local_action); + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + local_status = MTAPI_SUCCESS; + while (embb_atomic_load_int(&local_action->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + + /* delete action */ + if (embb_mtapi_job_is_id_valid(node, local_action->job_id)) { + embb_mtapi_job_t* local_job = embb_mtapi_job_get_storage_for_id( + node, local_action->job_id); + embb_mtapi_job_remove_action(local_job, local_action); + } + /* this is done by pool deallocate: + embb_mtapi_action_finalize(local_action); */ + embb_mtapi_action_pool_deallocate(node->action_pool, local_action); + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_action_disable( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_disable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + local_action->enabled = MTAPI_FALSE; + + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* cancel all tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_action_disable_visitor, local_action); + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + local_status = MTAPI_SUCCESS; + while (embb_atomic_load_int(&local_action->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_action_enable( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_enable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + local_action->enabled = MTAPI_TRUE; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/embb_mtapi_action_t.h a/mtapi_c/src/embb_mtapi_action_t.h new file mode 100644 index 0000000..7fd7533 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_action_t.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Action class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_action_struct { + mtapi_action_hndl_t handle; + + mtapi_domain_t domain_id; + mtapi_node_t node_id; + mtapi_job_id_t job_id; + mtapi_action_function_t action_function; + const void* node_local_data; + mtapi_size_t node_local_data_size; + mtapi_action_attributes_t attributes; + mtapi_boolean_t enabled; + + mtapi_boolean_t is_plugin_action; + void* plugin_data; + mtapi_ext_plugin_task_start_function_t plugin_task_start_function; + mtapi_ext_plugin_task_cancel_function_t plugin_task_cancel_function; + mtapi_ext_plugin_action_finalize_function_t plugin_action_finalize_function; + + embb_atomic_int num_tasks; +}; + +#include + +/** + * Default constructor. + * \memberof embb_mtapi_action_struct + */ +void embb_mtapi_action_initialize(embb_mtapi_action_t* that); + +/** + * Destructor. + * \memberof embb_mtapi_action_struct + */ +void embb_mtapi_action_finalize(embb_mtapi_action_t* that); + + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(action) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_H_ diff --git b/mtapi_c/src/embb_mtapi_action_t_fwd.h a/mtapi_c/src/embb_mtapi_action_t_fwd.h new file mode 100644 index 0000000..f8a4a66 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_action_t_fwd.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Action type + * \memberof embb_mtapi_action_struct + */ +typedef struct embb_mtapi_action_struct embb_mtapi_action_t; + +/** + * Action pool type. + * \memberof embb_mtapi_action_pool_struct + */ +typedef struct embb_mtapi_action_pool_struct embb_mtapi_action_pool_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_alloc.c a/mtapi_c/src/embb_mtapi_alloc.c new file mode 100644 index 0000000..5b02614 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_alloc.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +static embb_atomic_unsigned_int embb_mtapi_alloc_bytes_allocated = { 0 }; + +void * embb_mtapi_alloc_allocate(unsigned int bytes) { + void * ptr = embb_alloc(bytes); + if (ptr != NULL) { + embb_atomic_fetch_and_add_unsigned_int( + &embb_mtapi_alloc_bytes_allocated, sizeof(unsigned int)+bytes); + } + return ptr; +} + +void embb_mtapi_alloc_deallocate(void * ptr) { + if (ptr != NULL) { + embb_free(ptr); + } +} + +void embb_mtapi_alloc_reset_bytes_allocated() { + embb_atomic_store_unsigned_int(&embb_mtapi_alloc_bytes_allocated, 0); +} + +unsigned int embb_mtapi_alloc_get_bytes_allocated() { + return embb_atomic_load_unsigned_int(&embb_mtapi_alloc_bytes_allocated); +} diff --git b/mtapi_c/src/embb_mtapi_alloc.h a/mtapi_c/src/embb_mtapi_alloc.h new file mode 100644 index 0000000..6644444 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_alloc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_ALLOC_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ALLOC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +void * embb_mtapi_alloc_allocate(unsigned int bytes); +void embb_mtapi_alloc_deallocate(void * ptr); +void embb_mtapi_alloc_reset_bytes_allocated(); +unsigned int embb_mtapi_alloc_get_bytes_allocated(); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ALLOC_H_ diff --git b/mtapi_c/src/embb_mtapi_attr.c a/mtapi_c/src/embb_mtapi_attr.c new file mode 100644 index 0000000..fc14074 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_attr.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#define embb_mtapi_attr_implementation(TYPE) \ +mtapi_status_t embb_mtapi_attr_set_##TYPE(TYPE* target, \ + const void* attribute, mtapi_size_t attribute_size) { \ + assert(MTAPI_NULL != target); \ +\ + if (sizeof(TYPE) == attribute_size) { \ + assert(MTAPI_NULL != attribute); \ + memcpy(target, attribute, sizeof(TYPE)); \ + return MTAPI_SUCCESS; \ + } else if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE == attribute_size) { \ + memcpy(target, &attribute, sizeof(TYPE)); \ + return MTAPI_SUCCESS; \ + } else { \ + return MTAPI_ERR_ATTR_SIZE; \ + } \ +} \ +\ +mtapi_status_t embb_mtapi_attr_get_##TYPE(const TYPE* source, \ + void* attribute, mtapi_size_t attribute_size) { \ + assert(MTAPI_NULL != source); \ + assert(MTAPI_NULL != attribute); \ +\ + if (sizeof(TYPE) == attribute_size) { \ + memcpy(attribute, source, sizeof(TYPE)); \ + return MTAPI_SUCCESS; \ + } else { \ + return MTAPI_ERR_ATTR_SIZE; \ + } \ +} + +embb_mtapi_attr_implementation(mtapi_uint_t); +embb_mtapi_attr_implementation(mtapi_affinity_t); +embb_mtapi_attr_implementation(mtapi_boolean_t); diff --git b/mtapi_c/src/embb_mtapi_attr.h a/mtapi_c/src/embb_mtapi_attr.h new file mode 100644 index 0000000..467ae09 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_attr.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_ATTR_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ATTR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define embb_mtapi_attr(TYPE) \ +mtapi_status_t embb_mtapi_attr_set_##TYPE(TYPE* target, \ + const void* attribute, mtapi_size_t attribute_size); \ +mtapi_status_t embb_mtapi_attr_get_##TYPE(const TYPE* source, \ + void* attribute, mtapi_size_t attribute_size); + +embb_mtapi_attr(mtapi_uint_t) +embb_mtapi_attr(mtapi_affinity_t) +embb_mtapi_attr(mtapi_boolean_t) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ATTR_H_ diff --git b/mtapi_c/src/embb_mtapi_group_t.c a/mtapi_c/src/embb_mtapi_group_t.c new file mode 100644 index 0000000..c0d4082 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_group_t.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +mtapi_group_hndl_t MTAPI_GROUP_NONE = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +embb_mtapi_pool_implementation(group) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_group_initialize(embb_mtapi_group_t * that) { + assert(MTAPI_NULL != that); + + that->group_id = MTAPI_GROUP_ID_NONE; + embb_atomic_store_int(&that->deleted, MTAPI_FALSE); + that->num_tasks.internal_variable = 0; + embb_mtapi_task_queue_initialize(&that->queue); +} + +void embb_mtapi_group_initialize_with_node( + embb_mtapi_group_t * that, + embb_mtapi_node_t * node) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + that->group_id = MTAPI_GROUP_ID_NONE; + embb_atomic_store_int(&that->deleted, MTAPI_FALSE); + that->num_tasks.internal_variable = 0; + embb_mtapi_task_queue_initialize_with_capacity( + &that->queue, node->attributes.queue_limit); +} + +void embb_mtapi_group_finalize(embb_mtapi_group_t * that) { + assert(MTAPI_NULL != that); + + embb_atomic_store_int(&that->deleted, MTAPI_TRUE); + that->num_tasks.internal_variable = 0; + embb_mtapi_task_queue_finalize(&that->queue); +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_group_hndl_t mtapi_group_create( + MTAPI_IN mtapi_group_id_t group_id, + MTAPI_IN mtapi_group_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_group_hndl_t group_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + embb_mtapi_group_t* group = NULL; + + embb_mtapi_log_trace("mtapi_group_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + group = embb_mtapi_group_pool_allocate(node->group_pool); + if (MTAPI_NULL != group) { + embb_mtapi_group_initialize_with_node(group, node); + group->group_id = group_id; + if (MTAPI_NULL != attributes) { + group->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + mtapi_groupattr_init(&group->attributes, &local_status); + } + if (MTAPI_SUCCESS == local_status) { + group_hndl = group->handle; + } else { + embb_mtapi_group_finalize(group); + embb_mtapi_group_pool_deallocate(node->group_pool, group); + } + } else { + local_status = MTAPI_ERR_GROUP_LIMIT; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return group_hndl; +} + +void mtapi_group_set_attribute( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_group_t* local_group; + + embb_mtapi_log_trace("mtapi_group_set_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + local_group = embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + mtapi_groupattr_set(&local_group->attributes, attribute_num, + attribute, attribute_size, &local_status); + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_group_get_attribute( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(attribute_num); + EMBB_UNUSED(attribute_size); + + embb_mtapi_log_trace("mtapi_group_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + /* the following is not needed for now, since there are no attributes + + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); */ + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + /* switch is not needed for now, since there are no attributes + switch (attribute_num) { + default: */ + local_status = MTAPI_ERR_ATTR_NUM; + /* break; + }*/ + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_group_wait_all( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_group_wait_all() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_thread_context_t * context = NULL; + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* wait for all tasks to arrive in the queue */ + local_status = MTAPI_SUCCESS; + while (embb_atomic_load_int(&local_group->num_tasks)) { + embb_mtapi_task_t* local_task; + + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* fetch and delete all available tasks */ + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + while (MTAPI_NULL != local_task) { + if (MTAPI_SUCCESS != local_task->error_code) { + local_status = local_task->error_code; + } + embb_mtapi_task_delete(local_task, node->task_pool); + embb_atomic_fetch_and_add_int(&local_group->num_tasks, -1); + + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + if (MTAPI_TIMEOUT != local_status) { + /* group becomes invalid, so delete it */ + mtapi_group_delete(group, MTAPI_NULL); + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + embb_mtapi_log_trace("mtapi_group_wait_all() returns\n"); +} + +void mtapi_group_wait_any( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT void** result, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + void* local_result = MTAPI_NULL; + + embb_mtapi_log_trace("mtapi_group_wait_any() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + + embb_mtapi_task_t* local_task; + /* are there any tasks left? */ + if (0 == embb_atomic_load_int(&local_group->num_tasks)) { + /* group becomes invalid, so delete it */ + mtapi_group_delete(group, &local_status); + local_status = MTAPI_GROUP_COMPLETED; + } else { + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* wait for any task to arrive */ + local_status = MTAPI_SUCCESS; + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + while (MTAPI_NULL == local_task) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + + /* try to pop a task from the group queue */ + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + } + /* was there a timeout, or is there a result? */ + if (MTAPI_NULL != local_task) { + local_result = local_task->result_buffer; + + /* return error code set by the task */ + local_status = local_task->error_code; + + /* delete task */ + embb_mtapi_task_delete(local_task, node->task_pool); + embb_atomic_fetch_and_add_int(&local_group->num_tasks, -1); + } + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + /* store result */ + if (MTAPI_NULL != result) { + *result = local_result; + } + + mtapi_status_set(status, local_status); + embb_mtapi_log_trace("mtapi_group_wait_any() returns\n"); +} + +void mtapi_group_delete( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + + if (embb_atomic_load_int(&local_group->deleted)) { + local_status = MTAPI_ERR_GROUP_INVALID; + } else { + embb_mtapi_group_finalize(local_group); + embb_mtapi_group_pool_deallocate(node->group_pool, local_group); + local_status = MTAPI_SUCCESS; + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/embb_mtapi_group_t.h a/mtapi_c/src/embb_mtapi_group_t.h new file mode 100644 index 0000000..9bf2932 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_group_t.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Group class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_group_struct { + mtapi_group_hndl_t handle; + + mtapi_group_id_t group_id; + embb_atomic_int deleted; + embb_atomic_int num_tasks; + mtapi_group_attributes_t attributes; + embb_mtapi_task_queue_t queue; +}; + +#include + +/** + * Default constructor. + * \memberof embb_mtapi_group_struct + */ +void embb_mtapi_group_initialize(embb_mtapi_group_t * that); + +/** + * Constructor using parameters from embb_mtapi_node_struct. + * \memberof embb_mtapi_group_struct + */ +void embb_mtapi_group_initialize_with_node( + embb_mtapi_group_t * that, + embb_mtapi_node_t * node); + +/** + * Destructor. + * \memberof embb_mtapi_group_struct + */ +void embb_mtapi_group_finalize(embb_mtapi_group_t * that); + + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(group) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_H_ diff --git b/mtapi_c/src/embb_mtapi_group_t_fwd.h a/mtapi_c/src/embb_mtapi_group_t_fwd.h new file mode 100644 index 0000000..6b40ec8 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_group_t_fwd.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Group type + * \memberof embb_mtapi_group_struct + */ +typedef struct embb_mtapi_group_struct embb_mtapi_group_t; + +/** + * Group pool type. + * \memberof embb_mtapi_group_pool_struct + */ +typedef struct embb_mtapi_group_pool_struct embb_mtapi_group_pool_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_id_pool_t.c a/mtapi_c/src/embb_mtapi_id_pool_t.c new file mode 100644 index 0000000..db40a76 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_id_pool_t.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +void embb_mtapi_id_pool_initialize( + embb_mtapi_id_pool_t * that, + mtapi_uint_t capacity) { + mtapi_uint_t ii; + + that->id_buffer = (mtapi_uint_t*) + embb_mtapi_alloc_allocate(sizeof(mtapi_uint_t)*(capacity + 1)); + if (NULL != that->id_buffer) { + that->capacity = capacity; + that->id_buffer[0] = EMBB_MTAPI_IDPOOL_INVALID_ID; + for (ii = 1; ii <= capacity; ii++) { + that->id_buffer[ii] = ii; + } + that->ids_available = capacity; + } else { + that->capacity = 0; + that->ids_available = 0; + } + that->put_id_position = 0; + that->get_id_position = 1; + embb_spin_init(&that->lock); +} + +void embb_mtapi_id_pool_finalize(embb_mtapi_id_pool_t * that) { + that->capacity = 0; + that->ids_available = 0; + that->get_id_position = 0; + that->put_id_position = 0; + embb_mtapi_alloc_deallocate(that->id_buffer); + that->id_buffer = NULL; + embb_spin_destroy(&that->lock); +} + +mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that) { + mtapi_uint_t id = EMBB_MTAPI_IDPOOL_INVALID_ID; + + assert(MTAPI_NULL != that); + + if (embb_spin_lock(&that->lock) == EMBB_SUCCESS) { + if (0 < that->ids_available) { + /* take away one id */ + that->ids_available--; + + /* acquire position to fetch id from */ + mtapi_uint_t id_position = that->get_id_position; + that->get_id_position++; + if (that->capacity < that->get_id_position) { + that->get_id_position = 0; + } + + /* fetch id */ + id = that->id_buffer[id_position]; + + /* make id entry invalid just in case */ + that->id_buffer[id_position] = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + embb_spin_unlock(&that->lock); + } + + return id; +} + +void embb_mtapi_id_pool_deallocate( + embb_mtapi_id_pool_t * that, + mtapi_uint_t id) { + assert(MTAPI_NULL != that); + + if (embb_spin_lock(&that->lock) == EMBB_SUCCESS) { + if (that->capacity > that->ids_available) { + /* acquire position to put id to */ + mtapi_uint_t id_position = that->put_id_position; + that->put_id_position++; + if (that->capacity < that->put_id_position) { + that->put_id_position = 0; + } + + /* put id back into buffer */ + that->id_buffer[id_position] = id; + + /* make it available */ + that->ids_available++; + } + embb_spin_unlock(&that->lock); + } else { + embb_mtapi_log_error( + "could not acquire lock in embb_mtapi_IdPool_deallocate\n"); + } +} diff --git b/mtapi_c/src/embb_mtapi_id_pool_t.h a/mtapi_c/src/embb_mtapi_id_pool_t.h new file mode 100644 index 0000000..d3f2616 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_id_pool_t.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_ID_POOL_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ID_POOL_T_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * IdPool class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_id_pool_struct { + mtapi_uint_t capacity; + mtapi_uint_t *id_buffer; + mtapi_uint_t ids_available; + mtapi_uint_t get_id_position; + mtapi_uint_t put_id_position; + embb_spinlock_t lock; +}; + +/** + * IdPool type. + * \memberof embb_mtapi_id_pool_struct + */ +typedef struct embb_mtapi_id_pool_struct embb_mtapi_id_pool_t; + +#define EMBB_MTAPI_IDPOOL_INVALID_ID 0 + +/** + * Constructor with configurable capacity. + * \memberof embb_mtapi_id_pool_struct + */ +void embb_mtapi_id_pool_initialize( + embb_mtapi_id_pool_t * that, + mtapi_uint_t capacity); + +/** + * Destructor. + * \memberof embb_mtapi_id_pool_struct + */ +void embb_mtapi_id_pool_finalize(embb_mtapi_id_pool_t * that); + +/** + * Allocates a single item and removes its id from the pool. + * \memberof embb_mtapi_id_pool_struct + */ +mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that); + +/** + * Dellocates a single item and puts its id back into the pool. + * \memberof embb_mtapi_id_pool_struct + */ +void embb_mtapi_id_pool_deallocate( + embb_mtapi_id_pool_t * that, + mtapi_uint_t id); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ID_POOL_T_H_ diff --git b/mtapi_c/src/embb_mtapi_job_t.c a/mtapi_c/src/embb_mtapi_job_t.c new file mode 100644 index 0000000..ae555d3 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_job_t.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +mtapi_boolean_t embb_mtapi_job_initialize_list(embb_mtapi_node_t * node) { + node->job_list = (embb_mtapi_job_t*)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_job_t)*(node->attributes.max_jobs + 1)); + if (NULL == node->job_list) { + return MTAPI_FALSE; + } + mtapi_uint_t ii; + for (ii = 0; ii <= node->attributes.max_jobs; ii++) { + embb_mtapi_job_initialize( + &node->job_list[ii], node->attributes.max_actions_per_job); + node->job_list[ii].handle.id = ii; + node->job_list[ii].handle.tag = 0; + } + return MTAPI_TRUE; +} + +void embb_mtapi_job_finalize_list(embb_mtapi_node_t * node) { + mtapi_uint_t ii; + for (ii = 0; ii <= node->attributes.max_jobs; ii++) { + embb_mtapi_job_finalize(&node->job_list[ii]); + node->job_list[ii].handle.id = 0; + } + embb_mtapi_alloc_deallocate(node->job_list); + node->job_list = MTAPI_NULL; +} + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +mtapi_boolean_t embb_mtapi_job_is_handle_valid( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle) { + assert(MTAPI_NULL != node); + return ((0 < handle.id) && + (handle.id <= node->attributes.max_jobs) && + (node->job_list[handle.id].handle.tag == handle.tag)) ? + MTAPI_TRUE : MTAPI_FALSE; +} + +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_handle( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle) { + assert(MTAPI_NULL != node); + assert(MTAPI_NULL != node->job_list); + assert(embb_mtapi_job_is_handle_valid(node, handle)); + return &node->job_list[handle.id]; +} + +mtapi_boolean_t embb_mtapi_job_is_id_valid( + embb_mtapi_node_t * node, + mtapi_job_id_t id) { + assert(MTAPI_NULL != node); + return ((0 < id) && (id <= node->attributes.max_jobs)) ? + MTAPI_TRUE : MTAPI_FALSE; +} + +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_id( + embb_mtapi_node_t * node, + mtapi_job_id_t id) { + assert(MTAPI_NULL != node); + assert(MTAPI_NULL != node->job_list); + assert(embb_mtapi_job_is_id_valid(node, id)); + return &node->job_list[id]; +} + +void embb_mtapi_job_initialize( + embb_mtapi_job_t * that, + mtapi_uint_t max_actions) { + mtapi_uint_t ii; + + assert(MTAPI_NULL != that); + + that->handle.tag = 0; + + that->domain_id = 0; + that->node_id = 0; + that->num_actions = 0; + that->actions = (mtapi_action_hndl_t*) + embb_mtapi_alloc_allocate(sizeof(mtapi_action_hndl_t)*max_actions); + if (NULL != that->actions) { + that->max_actions = max_actions; + for (ii = 0; ii < max_actions; ii++) { + that->actions[ii].id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + } else { + that->max_actions = 0; + } +} + +void embb_mtapi_job_finalize(embb_mtapi_job_t * that) { + assert(MTAPI_NULL != that); + + that->handle.tag++; + + that->domain_id = 0; + that->node_id = 0; + that->num_actions = 0; + that->max_actions = 0; + embb_mtapi_alloc_deallocate(that->actions); + that->actions = NULL; +} + +mtapi_boolean_t embb_mtapi_job_add_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action) { + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != action); + + /* check if job has enough room for another action */ + if (that->max_actions > that->num_actions) { + that->domain_id = action->domain_id; + that->node_id = action->node_id; + that->actions[that->num_actions] = action->handle; + that->num_actions++; + } else { + result = MTAPI_FALSE; + } + + return result; +} + +void embb_mtapi_job_remove_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != action); + mtapi_uint_t ii; + + for (ii = 0; ii + 1 < that->num_actions; ii++) { + if (that->actions[ii].id == action->handle.id && + that->actions[ii].tag == action->handle.tag) { + that->actions[ii] = that->actions[that->num_actions - 1]; + } + } + + that->num_actions--; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_job_hndl_t mtapi_job_get( + MTAPI_IN mtapi_job_id_t job_id, + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + embb_mtapi_job_t* job = MTAPI_NULL; + mtapi_job_hndl_t job_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + EMBB_UNUSED(domain_id); + + embb_mtapi_log_trace("mtapi_job_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_job_is_id_valid(node, job_id)) { + job = embb_mtapi_job_get_storage_for_id(node, job_id); + job_hndl = job->handle; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return job_hndl; +} diff --git b/mtapi_c/src/embb_mtapi_job_t.h a/mtapi_c/src/embb_mtapi_job_t.h new file mode 100644 index 0000000..0ca4d47 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_job_t.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_JOB_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_JOB_T_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Job class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_job_struct { + mtapi_job_hndl_t handle; + + mtapi_domain_t domain_id; + mtapi_node_t node_id; + mtapi_uint_t num_actions; + mtapi_uint_t max_actions; + mtapi_action_hndl_t* actions; +}; + +#include + +/** + * Constructs the global job list. + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_initialize_list(embb_mtapi_node_t * node); + +/** + * Destroys the global job list. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_finalize_list(embb_mtapi_node_t * node); + +/** + * Checks if a given job handle is valid. + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_is_handle_valid( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle); + +/** + * Gets the pointer associated with the given handle. + * + * The handle is expected to be valid, so check it beforehand using + * embb_mtapi_job_is_handle_valid(). + * \memberof embb_mtapi_job_struct + */ +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_handle( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle); + +/** + * Checks if a given job id is valid. + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_is_id_valid( + embb_mtapi_node_t * node, + mtapi_job_id_t id); + +/** + * Gets the pointer associated with the given id. + * + * The id is expected to be valid, so check it beforehand using + * embb_mtapi_job_is_id_valid(). + * + * \memberof embb_mtapi_job_struct + */ +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_id( + embb_mtapi_node_t * node, + mtapi_job_id_t id); + +/** + * Constructor with configurable maximum action count. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_initialize( + embb_mtapi_job_t * that, + mtapi_uint_t max_actions); + +/** + * Destructor. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_finalize(embb_mtapi_job_t * that); + +/** + * Add an action to the job. + * + * Returns MTAPI_TRUE if there are less actions than the configured maximum + * action count in the job already. Otherwise returns MTAPI_FALSE: + * + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_add_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action); + +/** + * Remove an action from the job. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_remove_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action); + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_JOB_T_H_ diff --git b/mtapi_c/src/embb_mtapi_job_t_fwd.h a/mtapi_c/src/embb_mtapi_job_t_fwd.h new file mode 100644 index 0000000..1e1bc21 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_job_t_fwd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_JOB_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_JOB_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Job type. + * \memberof embb_mtapi_job_struct + */ +typedef struct embb_mtapi_job_struct embb_mtapi_job_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_JOB_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_log.h a/mtapi_c/src/embb_mtapi_log.h new file mode 100644 index 0000000..00a521a --- /dev/null +++ a/mtapi_c/src/embb_mtapi_log.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_LOG_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define embb_mtapi_log_trace(...) \ + embb_log_trace("mtapi_c", __VA_ARGS__) +#define embb_mtapi_log_info(...) \ + embb_log_info("mtapi_c", __VA_ARGS__) +#define embb_mtapi_log_warning(...) \ + embb_log_warning("mtapi_c", __VA_ARGS__) +#define embb_mtapi_log_error(...) \ + embb_log_error("mtapi_c", __VA_ARGS__) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_LOG_H_ diff --git b/mtapi_c/src/embb_mtapi_node_t.c a/mtapi_c/src/embb_mtapi_node_t.c new file mode 100644 index 0000000..da6a8b3 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_node_t.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +static embb_mtapi_node_t* embb_mtapi_node_instance = NULL; + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +mtapi_boolean_t embb_mtapi_node_is_initialized() { + return (mtapi_boolean_t)(embb_mtapi_node_instance != NULL); +} + +embb_mtapi_node_t* embb_mtapi_node_get_instance() { + return embb_mtapi_node_instance; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_initialize( + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_IN mtapi_node_t node_id, + MTAPI_IN mtapi_node_attributes_t* attributes, + MTAPI_OUT mtapi_info_t* mtapi_info, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node; + + embb_mtapi_log_trace( + "mtapi_initialize() called (domain: %i, node: %i)\n", domain_id, node_id); + + /* check if node was already initialized */ + if (embb_mtapi_node_is_initialized()) { + local_status = MTAPI_ERR_NODE_INITIALIZED; + + node = embb_mtapi_node_instance; + + /* return previously set information structure */ + if (MTAPI_NULL != mtapi_info) { + *mtapi_info = node->info; + } + } else { + embb_mtapi_alloc_reset_bytes_allocated(); + /* create node instance */ + embb_mtapi_node_instance = (embb_mtapi_node_t*) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_node_t)); + if (NULL == embb_mtapi_node_instance) { + /* out of memory! */ + local_status = MTAPI_ERR_UNKNOWN; + } else { + node = embb_mtapi_node_instance; + + node->domain_id = domain_id; + node->node_id = node_id; + + if (MTAPI_NULL != attributes) { + node->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + mtapi_nodeattr_init(&node->attributes, &local_status); + } + + if (MTAPI_SUCCESS == local_status) { + mtapi_affinity_init(&node->affinity_all, MTAPI_TRUE, &local_status); + } + + if (MTAPI_SUCCESS == local_status) { + embb_atomic_store_int(&node->is_scheduler_running, MTAPI_FALSE); + + /* initialize storage */ + embb_mtapi_job_initialize_list(node); + node->action_pool = embb_mtapi_action_pool_new( + node->attributes.max_actions); + node->group_pool = embb_mtapi_group_pool_new( + node->attributes.max_groups); + node->task_pool = embb_mtapi_task_pool_new( + node->attributes.max_tasks); + node->queue_pool = embb_mtapi_queue_pool_new( + node->attributes.max_queues); + if (MTAPI_NULL == node->job_list || + MTAPI_NULL == node->action_pool || + MTAPI_NULL == node->group_pool || + MTAPI_NULL == node->task_pool || + MTAPI_NULL == node->queue_pool) { + mtapi_finalize(NULL); + local_status = MTAPI_ERR_NODE_INITFAILED; + } + + if (local_status == MTAPI_SUCCESS) { + /* initialize scheduler for local node */ + node->scheduler = embb_mtapi_scheduler_new(); + if (MTAPI_NULL != node->scheduler) { + /* fill information structure */ + node->info.mtapi_version = 0x1000; // mtapi version 1.0 + node->info.organization_id = MCA_ORG_ID_EMB; + node->info.implementation_version = + EMBB_BASE_VERSION_MAJOR * 0x1000 + EMBB_BASE_VERSION_MINOR; + node->info.number_of_domains = ~0u; + node->info.number_of_nodes = ~0u; + node->info.hardware_concurrency = embb_core_count_available(); + node->info.used_memory = embb_mtapi_alloc_get_bytes_allocated(); + if (MTAPI_NULL != mtapi_info) { + *mtapi_info = node->info; + } + + /* initialization succeeded, tell workers to start working */ + embb_atomic_store_int(&node->is_scheduler_running, MTAPI_TRUE); + } else { + mtapi_finalize(MTAPI_NULL); + local_status = MTAPI_ERR_NODE_INITFAILED; + } + } + } else { + embb_mtapi_alloc_deallocate(node); + local_status = MTAPI_ERR_PARAMETER; + } + } + } + + mtapi_status_set(status, local_status); +} + +void mtapi_finalize(MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_finalize() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + + /* finalize scheduler */ + if (MTAPI_NULL != node->scheduler) { + embb_mtapi_scheduler_delete(node->scheduler); + node->scheduler = MTAPI_NULL; + } + + /* finalize storage in reverse order */ + if (MTAPI_NULL != node->queue_pool) { + embb_mtapi_queue_pool_delete(node->queue_pool); + node->queue_pool = MTAPI_NULL; + } + + if (MTAPI_NULL != node->task_pool) { + embb_mtapi_task_pool_delete(node->task_pool); + node->task_pool = MTAPI_NULL; + } + + if (MTAPI_NULL != node->group_pool) { + embb_mtapi_group_pool_delete(node->group_pool); + node->group_pool = MTAPI_NULL; + } + + if (MTAPI_NULL != node->action_pool) { + embb_mtapi_action_pool_delete(node->action_pool); + node->action_pool = MTAPI_NULL; + } + + if (MTAPI_NULL != node->job_list) { + embb_mtapi_job_finalize_list(node); + } + + /* free system instance */ + embb_mtapi_alloc_deallocate(node); + embb_mtapi_node_instance = MTAPI_NULL; + + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_node_get_attribute( + MTAPI_IN mtapi_node_t node, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* local_node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_node_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (local_node->node_id == node) { + if (MTAPI_NULL != attribute) { + switch (attribute_num) { + case MTAPI_NODE_CORE_AFFINITY: + if (MTAPI_NODE_CORE_AFFINITY_SIZE == attribute_size) { + *(embb_core_set_t*)attribute = + local_node->attributes.core_affinity; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ATTR_SIZE; + } + break; + + case MTAPI_NODE_NUMCORES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.num_cores, attribute, attribute_size); + break; + + case MTAPI_NODE_TYPE: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.type, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_TASKS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_tasks, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_actions, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_GROUPS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_groups, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_QUEUES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_queues, attribute, attribute_size); + break; + + case MTAPI_NODE_QUEUE_LIMIT: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.queue_limit, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_JOBS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_jobs, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS_PER_JOB: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_actions_per_job, attribute, + attribute_size); + break; + + case MTAPI_NODE_MAX_PRIORITIES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_priorities, attribute, attribute_size); + break; + + default: + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +mtapi_domain_t mtapi_domain_id_get( + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_domain_t domain_id = MTAPI_DOMAIN_ID_INVALID; + + embb_mtapi_log_trace("mtapi_domain_id_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + domain_id = node->domain_id; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return domain_id; +} + +mtapi_node_t mtapi_node_id_get( + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_node_t node_id = MTAPI_NODE_ID_INVALID; + + embb_mtapi_log_trace("mtapi_domain_id_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + node_id = node->node_id; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return node_id; +} diff --git b/mtapi_c/src/embb_mtapi_node_t.h a/mtapi_c/src/embb_mtapi_node_t.h new file mode 100644 index 0000000..be16101 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_node_t.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_NODE_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_NODE_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Node class. + * + * The node class is used as a singleton and is the root of this + * MTAPI implementation. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_node_struct { + mtapi_domain_t domain_id; + mtapi_node_t node_id; + mtapi_node_attributes_t attributes; + mtapi_info_t info; + embb_mtapi_scheduler_t * scheduler; + embb_mtapi_job_t * job_list; + embb_mtapi_action_pool_t * action_pool; + embb_mtapi_group_pool_t * group_pool; + embb_mtapi_task_pool_t * task_pool; + embb_mtapi_queue_pool_t * queue_pool; + embb_atomic_int is_scheduler_running; + mtapi_affinity_t affinity_all; +}; + +#include + +/** + * Checks if the node singleton was initialized already. + * \memberof embb_mtapi_node_struct + */ +mtapi_boolean_t embb_mtapi_node_is_initialized(); + +/** + * Retrieves the previously initialized node instance, but returns MTAPI_NULL + * if the node singleton is not yet created. + * \memberof embb_mtapi_node_struct + */ +embb_mtapi_node_t* embb_mtapi_node_get_instance(); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_NODE_T_H_ diff --git b/mtapi_c/src/embb_mtapi_node_t_fwd.h a/mtapi_c/src/embb_mtapi_node_t_fwd.h new file mode 100644 index 0000000..f9a7e19 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_node_t_fwd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_NODE_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_NODE_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Node type. + * \memberof embb_mtapi_node_struct + */ +typedef struct embb_mtapi_node_struct embb_mtapi_node_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_NODE_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_pool_template-inl.h a/mtapi_c/src/embb_mtapi_pool_template-inl.h new file mode 100644 index 0000000..7754952 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_pool_template-inl.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_INL_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_INL_H_ + +#include +#include + +#include +#include + +#define embb_mtapi_pool_implementation(TYPE) \ +\ +/* ---- POOL STORAGE FUNCTIONS ------------------------------------------- */ \ +\ +embb_mtapi_##TYPE##_pool_t * embb_mtapi_##TYPE##_pool_new( \ + mtapi_uint_t capacity) { \ + embb_mtapi_##TYPE##_pool_t * that = (embb_mtapi_##TYPE##_pool_t*) \ + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_##TYPE##_pool_t)); \ + if (MTAPI_NULL != that) { \ + embb_mtapi_##TYPE##_pool_initialize(that, capacity); \ + } \ + return that; \ +} \ +\ +void embb_mtapi_##TYPE##_pool_delete(embb_mtapi_##TYPE##_pool_t * that) { \ + assert(MTAPI_NULL != that); \ + embb_mtapi_##TYPE##_pool_finalize(that); \ + embb_mtapi_alloc_deallocate(that); \ +} \ +\ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_initialize( \ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_uint_t capacity) { \ + mtapi_uint_t ii; \ + assert(MTAPI_NULL != that); \ + embb_mtapi_id_pool_initialize(&that->id_pool, capacity); \ + that->storage = (embb_mtapi_##TYPE##_t*)embb_mtapi_alloc_allocate( \ + sizeof(embb_mtapi_##TYPE##_t)*(capacity + 1)); \ + if (NULL != that->storage) { \ + for (ii = 0; ii <= capacity; ii++) { \ + that->storage[ii].handle.id = EMBB_MTAPI_IDPOOL_INVALID_ID; \ + that->storage[ii].handle.tag = 0; \ + } \ + /* use entry 0 as invalid */ \ + embb_mtapi_##TYPE##_initialize(that->storage); \ + return MTAPI_TRUE; \ + } else { \ + that->id_pool.ids_available = 0; \ + return MTAPI_FALSE; \ + } \ +} \ +\ +void embb_mtapi_##TYPE##_pool_finalize(embb_mtapi_##TYPE##_pool_t * that) { \ + embb_mtapi_id_pool_finalize(&that->id_pool); \ + embb_mtapi_alloc_deallocate(that->storage); \ + that->storage = MTAPI_NULL; \ +} \ +\ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_allocate( \ + embb_mtapi_##TYPE##_pool_t * that) { \ + mtapi_uint_t pool_id = embb_mtapi_id_pool_allocate(&that->id_pool); \ + if (EMBB_MTAPI_IDPOOL_INVALID_ID != pool_id) { \ + that->storage[pool_id].handle.id = pool_id; \ + return &that->storage[pool_id]; \ + } else { \ + return MTAPI_NULL; \ + } \ +} \ +\ +void embb_mtapi_##TYPE##_pool_deallocate( \ + embb_mtapi_##TYPE##_pool_t * that, \ + embb_mtapi_##TYPE##_t * object) { \ + mtapi_uint_t pool_id = object->handle.id; \ + embb_mtapi_##TYPE##_finalize(object); \ + object->handle.id = EMBB_MTAPI_IDPOOL_INVALID_ID; \ + object->handle.tag++; \ + embb_mtapi_id_pool_deallocate(&that->id_pool, pool_id); \ +} \ +\ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_is_handle_valid( \ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle) { \ + assert(MTAPI_NULL != that); \ + return ((0 < handle.id) && \ + (handle.id <= that->id_pool.capacity) && \ + (that->storage[handle.id].handle.tag == handle.tag)) ? \ + MTAPI_TRUE : MTAPI_FALSE; \ +} \ +\ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_get_storage_for_handle( \ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle) { \ + assert(MTAPI_NULL != that); \ + assert(embb_mtapi_##TYPE##_pool_is_handle_valid(that, handle)); \ + return &that->storage[handle.id]; \ +} + +#endif // MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_INL_H_ diff --git b/mtapi_c/src/embb_mtapi_pool_template.h a/mtapi_c/src/embb_mtapi_pool_template.h new file mode 100644 index 0000000..088eeaf --- /dev/null +++ a/mtapi_c/src/embb_mtapi_pool_template.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_H_ + +#include + +#include + +#define embb_mtapi_pool(TYPE) \ +\ +/** \internal +TYPE pool class providing a fixed number of TYPE elements. Used to allocate +TYPE elements without using dynamic memory. + +\ingroup INTERNAL +*/ \ +struct embb_mtapi_##TYPE##_pool_struct \ +{ \ + embb_mtapi_id_pool_t id_pool; \ + embb_mtapi_##TYPE##_t * storage; \ +}; \ +\ +/** operator new with configurable capacity. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +embb_mtapi_##TYPE##_pool_t * embb_mtapi_##TYPE##_pool_new(\ + mtapi_uint_t capacity); \ +\ +/** operator delete. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +void embb_mtapi_##TYPE##_pool_delete(embb_mtapi_##TYPE##_pool_t * that); \ +\ +/** Constructor with configurable capacity. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_initialize(\ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_uint_t capacity); \ +\ +/** Destructor. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +void embb_mtapi_##TYPE##_pool_finalize(\ + embb_mtapi_##TYPE##_pool_t * that); \ +\ +/** Allocate a single TYPE element in the pool. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_allocate(\ + embb_mtapi_##TYPE##_pool_t * that); \ +\ +/** Deallocate given TYPE element in the pool. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +void embb_mtapi_##TYPE##_pool_deallocate(\ + embb_mtapi_##TYPE##_pool_t * that, \ + embb_mtapi_##TYPE##_t * object); \ +\ +/** Check if given pool handle is valid. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_is_handle_valid(\ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle); \ +\ +/** Return pointer to storage for given handle. Handle is expected to be valid, +so check it beforehand using embb_mtapi_##TYPE##_pool_is_handle_valid(). +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_get_storage_for_handle(\ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle); + +#endif // MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_H_ diff --git b/mtapi_c/src/embb_mtapi_queue_t.c a/mtapi_c/src/embb_mtapi_queue_t.c new file mode 100644 index 0000000..5e9599f --- /dev/null +++ a/mtapi_c/src/embb_mtapi_queue_t.c @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +embb_mtapi_pool_implementation(queue) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_queue_initialize(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + + mtapi_queueattr_init(&that->attributes, MTAPI_NULL); + that->queue_id = MTAPI_QUEUE_ID_NONE; + embb_atomic_store_char(&that->enabled, MTAPI_FALSE); + embb_atomic_store_int(&that->num_tasks, 0); + that->job_handle.id = 0; + that->job_handle.tag = 0; +} + +void embb_mtapi_queue_initialize_with_attributes_and_job( + embb_mtapi_queue_t* that, + mtapi_queue_attributes_t* attributes, + mtapi_job_hndl_t job) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != attributes); + + that->attributes = *attributes; + that->queue_id = MTAPI_QUEUE_ID_NONE; + embb_atomic_store_char(&that->enabled, MTAPI_TRUE); + embb_atomic_store_int(&that->num_tasks, 0); + that->job_handle = job; +} + +void embb_mtapi_queue_finalize(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + + that->job_handle.id = 0; + that->job_handle.tag = 0; + embb_mtapi_queue_initialize(that); +} + +void embb_mtapi_queue_task_started(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + embb_atomic_fetch_and_add_int(&that->num_tasks, 1); +} + +void embb_mtapi_queue_task_finished(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + embb_atomic_fetch_and_add_int(&that->num_tasks, -1); +} + +static mtapi_boolean_t embb_mtapi_queue_delete_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_queue_t * queue = (embb_mtapi_queue_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != queue); + assert(MTAPI_NULL != task); + + if (task->queue.id == queue->handle.id && + task->queue.tag == queue->handle.tag) { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_QUEUE_DELETED; + result = MTAPI_TRUE; + } + + return result; +} + +static mtapi_boolean_t embb_mtapi_queue_disable_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_queue_t * queue = (embb_mtapi_queue_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != queue); + assert(MTAPI_NULL != task); + + if (task->queue.id == queue->handle.id && + task->queue.tag == queue->handle.tag) { + if (queue->attributes.retain) { + /* task is scheduled and needs to be retained */ + embb_mtapi_task_set_state(task, MTAPI_TASK_RETAINED); + } else { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_QUEUE_DISABLED; + } + result = MTAPI_TRUE; + } + + return result; +} + +static mtapi_boolean_t embb_mtapi_queue_enable_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_queue_t * queue = (embb_mtapi_queue_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != queue); + assert(MTAPI_NULL != task); + + if (task->queue.id == queue->handle.id && + task->queue.tag == queue->handle.tag) { + /* task is retained and should be scheduled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_SCHEDULED); + result = MTAPI_TRUE; + } + + return result; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_queue_hndl_t mtapi_queue_create( + MTAPI_IN mtapi_queue_id_t queue_id, + MTAPI_IN mtapi_job_hndl_t job, + MTAPI_IN mtapi_queue_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_queue_t* queue = MTAPI_NULL; + mtapi_queue_hndl_t queue_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + mtapi_queue_attributes_t attr; + + embb_mtapi_log_trace("mtapi_queue_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + queue = embb_mtapi_queue_pool_allocate(node->queue_pool); + if (MTAPI_NULL != queue) { + if (MTAPI_NULL != attributes) { + attr = *attributes; + local_status = MTAPI_SUCCESS; + } else { + mtapi_queueattr_init(&attr, &local_status); + } + if (MTAPI_SUCCESS == local_status) { + if (embb_mtapi_job_is_handle_valid(node, job)) { + embb_mtapi_queue_initialize_with_attributes_and_job( + queue, &attr, job); + /* for an ordered queue, initialize affinity */ + if (queue->attributes.ordered) { + mtapi_affinity_init( + &queue->ordered_affinity, + MTAPI_FALSE, MTAPI_NULL); + mtapi_affinity_set( + &queue->ordered_affinity, + queue->handle.id % node->scheduler->worker_count, + MTAPI_TRUE, MTAPI_NULL); + } + queue->queue_id = queue_id; + queue_hndl = queue->handle; + } else { + embb_mtapi_queue_pool_deallocate(node->queue_pool, queue); + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_queue_pool_deallocate(node->queue_pool, queue); + } + } else { + local_status = MTAPI_ERR_QUEUE_LIMIT; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return queue_hndl; +} + +void mtapi_queue_set_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_queue_t* local_queue; + + embb_mtapi_log_trace("mtapi_queue_set_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + local_queue = embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + mtapi_queueattr_set(&local_queue->attributes, attribute_num, + attribute, attribute_size, &local_status); + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queue_get_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_queue_t* local_queue; + + embb_mtapi_log_trace("mtapi_queue_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + local_queue = embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_QUEUE_GLOBAL: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.global, attribute, attribute_size); + break; + + case MTAPI_QUEUE_PRIORITY: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_queue->attributes.priority, attribute, attribute_size); + break; + + case MTAPI_QUEUE_LIMIT: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_queue->attributes.limit, attribute, attribute_size); + break; + + case MTAPI_QUEUE_ORDERED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.ordered, attribute, attribute_size); + break; + + case MTAPI_QUEUE_RETAIN: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.retain, attribute, attribute_size); + break; + + case MTAPI_QUEUE_DOMAIN_SHARED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.domain_shared, attribute, attribute_size); + break; + + default: + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +mtapi_queue_hndl_t mtapi_queue_get( + MTAPI_IN mtapi_queue_id_t queue_id, + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_OUT mtapi_status_t* status) { + mtapi_queue_hndl_t queue_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(domain_id); + + embb_mtapi_log_trace("mtapi_queue_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_uint_t ii; + + local_status = MTAPI_ERR_QUEUE_INVALID; + for (ii = 0; ii < node->attributes.max_queues; ii++) { + if (queue_id == node->queue_pool->storage[ii].queue_id) { + queue_hndl = node->queue_pool->storage[ii].handle; + local_status = MTAPI_SUCCESS; + break; + } + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return queue_hndl; +} + +void mtapi_queue_delete( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queue_delete() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* cancel all tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_queue_delete_visitor, local_queue); + + /* wait for tasks in queue to finish */ + local_status = MTAPI_SUCCESS; + while (0 != embb_atomic_load_int(&local_queue->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + + /* delete queue */ + embb_mtapi_queue_finalize(local_queue); + embb_mtapi_queue_pool_deallocate(node->queue_pool, local_queue); + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queue_disable( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queue_disable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + embb_atomic_store_char(&local_queue->enabled, MTAPI_FALSE); + + /* cancel or retain all tasks scheduled via queue */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_queue_disable_visitor, local_queue); + + /* if queue is not retaining, wait for all tasks to finish */ + if (MTAPI_FALSE == local_queue->attributes.retain) { + /* find out on which thread we are */ + embb_mtapi_thread_context_t * context = + embb_mtapi_scheduler_get_current_thread_context(node->scheduler); + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* wait for tasks in queue to finish */ + local_status = MTAPI_SUCCESS; + while (0 != embb_atomic_load_int(&local_queue->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + } else { + local_status = MTAPI_SUCCESS; + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queue_enable( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queue_enable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + embb_atomic_store_char(&local_queue->enabled, MTAPI_TRUE); + local_status = MTAPI_SUCCESS; + if (local_queue->attributes.retain) { + /* reschedule retained tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_queue_enable_visitor, local_queue); + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/embb_mtapi_queue_t.h a/mtapi_c/src/embb_mtapi_queue_t.h new file mode 100644 index 0000000..1f3fc35 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_queue_t.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Queue class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_queue_struct { + mtapi_queue_hndl_t handle; + + mtapi_queue_id_t queue_id; + embb_atomic_char enabled; + mtapi_job_hndl_t job_handle; + mtapi_queue_attributes_t attributes; + + embb_atomic_int num_tasks; + mtapi_affinity_t ordered_affinity; +}; + +#include + +/** + * Default constructor. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_initialize(embb_mtapi_queue_t* that); + +/** + * Constructor with attributes and job. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_initialize_with_attributes_and_job( + embb_mtapi_queue_t* that, + mtapi_queue_attributes_t* attributes, + mtapi_job_hndl_t job); + +/** + * Destructor. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_finalize(embb_mtapi_queue_t* that); + +/** + * Notify queue that an associated Task has started. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_task_started(embb_mtapi_queue_t* that); + +/** + * Notify queue that an associated Task has finished. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_task_finished(embb_mtapi_queue_t* that); + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(queue) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_H_ diff --git b/mtapi_c/src/embb_mtapi_queue_t_fwd.h a/mtapi_c/src/embb_mtapi_queue_t_fwd.h new file mode 100644 index 0000000..23c49f4 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_queue_t_fwd.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Queue type. + * \memberof embb_mtapi_queue_struct + */ +typedef struct embb_mtapi_queue_struct embb_mtapi_queue_t; + +/** + * Queue pool type. + * \memberof embb_mtapi_queue_pool_struct + */ +typedef struct embb_mtapi_queue_pool_struct embb_mtapi_queue_pool_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_scheduler_t.c a/mtapi_c/src/embb_mtapi_scheduler_t.c new file mode 100644 index 0000000..d7549c6 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_scheduler_t.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +embb_mtapi_task_t * embb_mtapi_scheduler_get_private_task_from_context( + embb_mtapi_scheduler_t * that, + embb_mtapi_thread_context_t * thread_context, + mtapi_uint_t priority) { + EMBB_UNUSED(that); + + assert(MTAPI_NULL != that); + assert(NULL != thread_context); + + embb_mtapi_task_t * task = + embb_mtapi_task_queue_pop(thread_context->private_queue[priority]); + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_public_task_from_context( + embb_mtapi_scheduler_t * that, + embb_mtapi_thread_context_t * thread_context, + mtapi_uint_t priority) { + EMBB_UNUSED(that); + + embb_mtapi_task_t * task; + + assert(MTAPI_NULL != that); + assert(NULL != thread_context); + + task = embb_mtapi_task_queue_pop(thread_context->queue[priority]); + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task_vhpf( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + embb_mtapi_task_t * task = MTAPI_NULL; + mtapi_uint_t ii; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + assert(NULL != thread_context); + + for (ii = 0; + ii < node->attributes.max_priorities && MTAPI_NULL == task; + ii++) { + /* try local queues, first private. */ + task = embb_mtapi_scheduler_get_private_task_from_context( + that, thread_context, ii); + if (MTAPI_NULL == task) { + /* found nothing, so local public next. */ + task = embb_mtapi_scheduler_get_public_task_from_context( + that, thread_context, ii); + if (MTAPI_NULL == task) { + /* still nothing, steal from public queues of other workers. + the process starts at the worker "after" the current worker, + it might be better to start at a random worker + */ + mtapi_uint_t context_index = + (thread_context->worker_index + 1) % that->worker_count; + mtapi_uint_t kk; + for (kk = 0; + kk < that->worker_count - 1 && MTAPI_NULL == task; + kk++) { + task = embb_mtapi_task_queue_pop( + that->worker_contexts[context_index].queue[ii]); + context_index = + (context_index + 1) % that->worker_count; + } + } + } + } + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task_lf( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + embb_mtapi_task_t * task = MTAPI_NULL; + mtapi_uint_t prio; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + assert(NULL != thread_context); + + /* Try local queues on all priorities, first private. */ + for (prio = 0; + MTAPI_NULL == task && prio < node->attributes.max_priorities; + prio++) { + task = embb_mtapi_scheduler_get_private_task_from_context( + that, thread_context, prio); + } + + /* found nothing, so local public next. */ + for (prio = 0; + MTAPI_NULL == task && prio < node->attributes.max_priorities; + prio++) { + task = embb_mtapi_scheduler_get_public_task_from_context( + that, thread_context, prio); + } + + /* still nothing, steal from public queues of other workers. + the process starts at the worker "after" the current worker, + it might be better to start at a random worker + */ + for (prio = 0; + MTAPI_NULL == task && prio < node->attributes.max_priorities; + prio++) { + mtapi_uint_t context_index = + (thread_context->worker_index + 1) % that->worker_count; + mtapi_uint_t kk; + for (kk = 0; + kk < that->worker_count - 1 && MTAPI_NULL == task; + kk++) { + task = embb_mtapi_task_queue_pop( + that->worker_contexts[context_index].queue[prio]); + context_index = + (context_index + 1) % that->worker_count; + } + } + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + embb_mtapi_task_t * task = MTAPI_NULL; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + assert(NULL != thread_context); + + switch (that->mode) { + case WORK_STEAL_LF: + task = embb_mtapi_scheduler_get_next_task_lf( + that, node, thread_context); + break; + case WORK_STEAL_VHPF: + task = embb_mtapi_scheduler_get_next_task_vhpf( + that, node, thread_context); + break; + case NUM_SCHEDULER_MODES: + default: + embb_mtapi_log_error( + "embb_mtapi_Scheduler_getNextTask() unknown scheduler mode: %d\n", + that->mode); + } + return task; +} + +embb_mtapi_thread_context_t * embb_mtapi_scheduler_get_current_thread_context( + embb_mtapi_scheduler_t * that) { + mtapi_uint_t ii; + embb_mtapi_thread_context_t * context = NULL; + + assert(MTAPI_NULL != that); + + /* find out on which thread we are */ + for (ii = 0; ii < that->worker_count; ii++) { + context = (embb_mtapi_thread_context_t*)embb_tss_get( + &(that->worker_contexts[ii].tss_id)); + if (NULL != context) { + break; + } + } + + return context; +} + +void embb_mtapi_scheduler_execute_task_or_yield( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + if (NULL != thread_context) { + embb_mtapi_task_t* new_task = embb_mtapi_scheduler_get_next_task( + that, node, thread_context); + /* if there was work, execute it */ + if (MTAPI_NULL != new_task) { + embb_mtapi_task_context_t task_context; + embb_mtapi_task_context_initialize_with_thread_context_and_task( + &task_context, thread_context, new_task); + embb_mtapi_task_execute(new_task, &task_context); + } else { + embb_thread_yield(); + } + } else { + embb_thread_yield(); + } +} + +embb_mtapi_scheduler_worker_func_t * +embb_mtapi_scheduler_worker_func(embb_mtapi_scheduler_t * that) { + EMBB_UNUSED(that); + + assert(MTAPI_NULL != that); + + /* Currently just returns embb_mtapi_scheduler_worker, + but could return any custom worker function, e.g. depending + on scheduler->mode. + */ + return &embb_mtapi_scheduler_worker; +} + +int embb_mtapi_scheduler_worker(void * arg) { + embb_mtapi_thread_context_t * thread_context = + (embb_mtapi_thread_context_t*)arg; + embb_mtapi_task_context_t task_context; + embb_mtapi_node_t * node; + embb_duration_t sleep_duration; + int err; + int counter = 0; + + embb_mtapi_log_trace( + "embb_mtapi_scheduler_worker() called for thread %d on core %d\n", + thread_context->worker_index, thread_context->core_num); + + assert(MTAPI_NULL != thread_context); + + err = embb_tss_create(&thread_context->tss_id); + if (EMBB_SUCCESS != err) { + /* report error to scheduler */ + embb_atomic_store_int(&thread_context->run, -1); + return MTAPI_FALSE; + } + + /* node is initialized here, otherwise the worker would not run */ + node = thread_context->node; + + embb_tss_set(&(thread_context->tss_id), thread_context); + + embb_duration_set_milliseconds(&sleep_duration, 10); + + /* signal that we're up & running */ + embb_atomic_store_int(&thread_context->run, 1); + /* potentially wait for node to come up completely */ + while (MTAPI_FALSE == embb_atomic_load_int(&node->is_scheduler_running)) { + embb_thread_yield(); + } + + /* do work while not requested to stop */ + while (embb_atomic_load_int(&thread_context->run)) { + /* try to get work */ + embb_mtapi_task_t * task = embb_mtapi_scheduler_get_next_task( + node->scheduler, node, thread_context); + /* check if there was work */ + if (MTAPI_NULL != task) { + embb_mtapi_queue_t * local_queue = MTAPI_NULL; + embb_mtapi_group_t * local_group = MTAPI_NULL; + embb_mtapi_action_t * local_action = MTAPI_NULL; + + /* is task associated with a queue? */ + if (embb_mtapi_queue_pool_is_handle_valid( + node->queue_pool, task->queue)) { + local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, task->queue); + } + + /* is task associated with a group? */ + if (embb_mtapi_group_pool_is_handle_valid( + node->group_pool, task->group)) { + local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, task->group); + } + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, task->action)) { + local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, task->action); + } + + switch (embb_atomic_load_int(&task->state)) { + case MTAPI_TASK_SCHEDULED: + /* multi-instance task, another instance might be running */ + case MTAPI_TASK_RUNNING: + /* there was work, execute it */ + embb_mtapi_task_context_initialize_with_thread_context_and_task( + &task_context, thread_context, task); + if (embb_mtapi_task_execute(task, &task_context)) { + /* tell queue that a task is done */ + if (MTAPI_NULL != local_queue) { + embb_mtapi_queue_task_finished(local_queue); + } + } + counter = 0; + break; + + case MTAPI_TASK_RETAINED: + /* put task into queue again for later execution */ + embb_mtapi_scheduler_schedule_task( + node->scheduler, task, 0); + /* yield, as there may be only retained tasks in the queue */ + embb_thread_yield(); + /* task is not done, so do not notify queue */ + break; + + case MTAPI_TASK_CANCELLED: + /* set return value to cancelled */ + task->error_code = MTAPI_ERR_ACTION_CANCELLED; + if (embb_atomic_fetch_and_add_unsigned_int( + &task->instances_todo, (unsigned int)-1) == 0) { + /* tell queue that a task is done */ + if (MTAPI_NULL != local_queue) { + embb_mtapi_queue_task_finished(local_queue); + } + if (MTAPI_NULL != local_group) { + embb_mtapi_task_queue_push(&local_group->queue, task); + } + } + if (MTAPI_NULL != local_action) { + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + } + break; + + case MTAPI_TASK_COMPLETED: + case MTAPI_TASK_DELETED: + case MTAPI_TASK_WAITING: + case MTAPI_TASK_CREATED: + case MTAPI_TASK_PRENATAL: + case MTAPI_TASK_ERROR: + case MTAPI_TASK_INTENTIONALLY_UNUSED: + default: + /* do nothing, although this is an error */ + break; + } + + /* issue task complete callback if set */ + if (MTAPI_NULL != task->attributes.complete_func) { + task->attributes.complete_func(task->handle, MTAPI_NULL); + } + } else if (counter < 1024) { + /* spin and yield for a while before going to sleep */ + embb_thread_yield(); + counter++; + } else { + /* no work, go to sleep */ + embb_atomic_store_int(&thread_context->is_sleeping, 1); + embb_mutex_lock(&thread_context->work_available_mutex); + embb_condition_wait_for( + &thread_context->work_available, + &thread_context->work_available_mutex, + &sleep_duration); + embb_mutex_unlock(&thread_context->work_available_mutex); + embb_atomic_store_int(&thread_context->is_sleeping, 0); + } + } + + embb_tss_delete(&(thread_context->tss_id)); + + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_scheduler_wait_for_task( + embb_mtapi_task_t * task, + mtapi_timeout_t timeout) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_thread_context_t * context = NULL; + embb_duration_t wait_duration; + embb_time_t end_time; + + assert(MTAPI_NULL != node); + assert(MTAPI_NULL != task); + + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds(&wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* now wait and schedule new tasks if we are on a worker */ + mtapi_task_state_t task_state = + (mtapi_task_state_t)embb_atomic_load_int(&task->state); + while ( + (MTAPI_TASK_SCHEDULED == task_state) || + (MTAPI_TASK_RUNNING == task_state) || + (MTAPI_TASK_RETAINED == task_state) ) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + return MTAPI_FALSE; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + + task_state = (mtapi_task_state_t)embb_atomic_load_int(&task->state); + } + + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_scheduler_initialize( + embb_mtapi_scheduler_t * that) { + return embb_mtapi_scheduler_initialize_with_mode(that, WORK_STEAL_VHPF); +} + +mtapi_boolean_t embb_mtapi_scheduler_initialize_with_mode( + embb_mtapi_scheduler_t * that, + embb_mtapi_scheduler_mode_t mode) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_uint_t ii; + + embb_mtapi_log_trace("embb_mtapi_scheduler_initialize() called\n"); + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + embb_atomic_store_int(&that->affine_task_counter, 0); + + /* Paranoia sanitizing of scheduler mode */ + if (mode >= NUM_SCHEDULER_MODES) { + mode = WORK_STEAL_VHPF; + } + that->mode = mode; + + assert(node->attributes.num_cores == + embb_core_set_count(&node->attributes.core_affinity)); + that->worker_count = node->attributes.num_cores; + + that->worker_contexts = (embb_mtapi_thread_context_t*) + embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_thread_context_t)*that->worker_count); + if (NULL == that->worker_contexts) { + return MTAPI_FALSE; + } + mtapi_boolean_t isinit = MTAPI_TRUE; + for (ii = 0; ii < that->worker_count; ii++) { + unsigned int core_num = 0; + mtapi_uint_t ll = 0; + mtapi_boolean_t run = MTAPI_TRUE; + while (run) { + if (embb_core_set_contains(&node->attributes.core_affinity, core_num)) { + if (ll == ii) break; + ll++; + } + core_num++; + } + isinit &= embb_mtapi_thread_context_initialize_with_node_worker_and_core( + &that->worker_contexts[ii], node, ii, core_num); + } + if (!isinit) { + return MTAPI_FALSE; + } + for (ii = 0; ii < that->worker_count; ii++) { + if (MTAPI_FALSE == embb_mtapi_thread_context_start( + &that->worker_contexts[ii], that)) { + /* on error return false, finalize will shut everything down */ + return MTAPI_FALSE; + } + } + return MTAPI_TRUE; +} + +void embb_mtapi_scheduler_finalize(embb_mtapi_scheduler_t * that) { + mtapi_uint_t ii; + embb_mtapi_log_trace("embb_mtapi_scheduler_finalize() called\n"); + + assert(MTAPI_NULL != that); + + if (MTAPI_NULL != that->worker_contexts) { + /* finalize all workers */ + for (ii = 0; ii < that->worker_count; ii++) { + embb_mtapi_thread_context_stop(&that->worker_contexts[ii]); + } + for (ii = 0; ii < that->worker_count; ii++) { + embb_mtapi_thread_context_finalize(&that->worker_contexts[ii]); + } + + that->worker_count = 0; + embb_mtapi_alloc_deallocate(that->worker_contexts); + that->worker_contexts = MTAPI_NULL; + } +} + +embb_mtapi_scheduler_t * embb_mtapi_scheduler_new() { + embb_mtapi_scheduler_t * that = + (embb_mtapi_scheduler_t*)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_scheduler_t)); + if (MTAPI_NULL != that) { + if (MTAPI_FALSE == embb_mtapi_scheduler_initialize(that)) { + /* on error delete and return MTAPI_NULL */ + embb_mtapi_scheduler_finalize(that); + embb_mtapi_scheduler_delete(that); + return MTAPI_NULL; + } + } + return that; +} + +void embb_mtapi_scheduler_delete(embb_mtapi_scheduler_t * that) { + assert(MTAPI_NULL != that); + + embb_mtapi_scheduler_finalize(that); + embb_mtapi_alloc_deallocate(that); +} + +mtapi_boolean_t embb_mtapi_scheduler_process_tasks( + embb_mtapi_scheduler_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data) { + mtapi_uint_t ii; + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + + for (ii = 0; ii < that->worker_count; ii++) { + result = embb_mtapi_thread_context_process_tasks( + &that->worker_contexts[ii], process, user_data); + if (MTAPI_FALSE == result) { + break; + } + } + + return result; +} + +mtapi_boolean_t embb_mtapi_scheduler_schedule_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_task_t * task, + mtapi_uint_t instance) { + embb_mtapi_scheduler_t * scheduler = that; + /* distribute round robin */ + mtapi_uint_t ii = (task->handle.id + instance) % scheduler->worker_count; + mtapi_boolean_t pushed = MTAPI_FALSE; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + + assert(MTAPI_NULL != node); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, task->action)) { + embb_mtapi_queue_t* local_queue = MTAPI_NULL; + /* fetch action and schedule */ + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, task->action); + + mtapi_affinity_t affinity = + local_action->attributes.affinity & task->attributes.affinity; + + /* check if task is running from an ordered queue */ + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, task->queue)) { + local_queue = embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, task->queue); + if (local_queue->attributes.ordered) { + /* yes, modify affinity accordingly */ + affinity = local_queue->ordered_affinity; + } + } + + /* check affinity */ + if (affinity == 0) { + affinity = node->affinity_all; + } + + /* one more task in flight for this action */ + embb_atomic_fetch_and_add_int(&local_action->num_tasks, 1); + + if (affinity == node->affinity_all) { + /* no affinity restrictions, schedule for stealing */ + pushed = embb_mtapi_task_queue_push( + scheduler->worker_contexts[ii].queue[task->attributes.priority], + task); + } else { + mtapi_status_t affinity_status; + + /* affinity is restricted, check and adapt scheduling target */ + ii = (mtapi_uint_t)embb_atomic_fetch_and_add_int( + &scheduler->affine_task_counter, 1); + while (MTAPI_FALSE == mtapi_affinity_get( + &affinity, ii, &affinity_status)) { + ii = (ii + 1) % scheduler->worker_count; + } + /* schedule into private queue to disable stealing */ + pushed = embb_mtapi_task_queue_push( + scheduler->worker_contexts[ii].private_queue[task->attributes.priority], + task); + } + + if (pushed) { + /* signal the worker thread a task was pushed to */ + if (embb_atomic_load_int(&scheduler->worker_contexts[ii].is_sleeping)) { + embb_condition_notify_one( + &scheduler->worker_contexts[ii].work_available); + } + } else { + /* task could not be launched */ + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + } + } + + return pushed; +} diff --git b/mtapi_c/src/embb_mtapi_scheduler_t.h a/mtapi_c/src/embb_mtapi_scheduler_t.h new file mode 100644 index 0000000..ac94a0d --- /dev/null +++ a/mtapi_c/src/embb_mtapi_scheduler_t.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include +#include +#include +#include +typedef int (embb_mtapi_scheduler_worker_func_t)(void * args); + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Scheduler mode type + * + * \ingroup INTERNAL + */ +enum embb_mtapi_scheduler_mode_enum { + // Victim Higher Priority First. Steal if at least one local queue is empty. + WORK_STEAL_VHPF = 0, + // Local First. Steal if all local queues are empty. + WORK_STEAL_LF = 1, + + NUM_SCHEDULER_MODES +}; + +/** + * Scheduler mode type. + * \memberof embb_mtapi_scheduler_struct + */ +typedef enum embb_mtapi_scheduler_mode_enum embb_mtapi_scheduler_mode_t; + +/** + * \internal + * Scheduler class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_scheduler_struct { + mtapi_uint_t worker_count; + embb_mtapi_thread_context_t * worker_contexts; + mtapi_action_attributes_t attributes; + + // using enum value instead of function pointer to simplify testing + // for modes, like + // if (scheduler->mode == WORK_STEAL_VHPF) + embb_mtapi_scheduler_mode_t mode; + + embb_atomic_int affine_task_counter; +}; + +#include + +/** + * The default worker thread function used by the scheduler. Implements + * scheduling strategy as defined by scheduler mode. + * \memberof embb_mtapi_scheduler_struct + */ +int embb_mtapi_scheduler_worker(void * arg); + +/** + * \internal + * Resolve the worker function for a scheduler. + * \memberof embb_mtapi_scheduler_struct + * \ingroup INTERNAL + */ +embb_mtapi_scheduler_worker_func_t * +embb_mtapi_scheduler_worker_func(embb_mtapi_scheduler_t * that); + +/** + * Wait for a given task and schedule new ones while waiting. + * \memberof embb_mtapi_scheduler_struct + */ +mtapi_boolean_t embb_mtapi_scheduler_wait_for_task( + embb_mtapi_task_t * task, + mtapi_timeout_t timeout); + +/** + * Get a task from any of the available queues. + * \memberof embb_mtapi_scheduler_struct + */ +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context); + +/** + * Set the scheduling strategy. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_set_mode( + embb_mtapi_scheduler_t * that, + embb_mtapi_scheduler_mode_t mode); + +/** + * Determines the thread context associated with the current thread. + * \memberof embb_mtapi_scheduler_struct + */ +embb_mtapi_thread_context_t * embb_mtapi_scheduler_get_current_thread_context( + embb_mtapi_scheduler_t * that); + +/** + * Fetches and executes a single task if the thread context is valid, + * yields otherwise. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_execute_task_or_yield( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context); + +/** + * operator new. + * \memberof embb_mtapi_scheduler_struct + * \returns pointer to the scheduler oder MTAPI_NULL on error + */ +embb_mtapi_scheduler_t * embb_mtapi_scheduler_new(); + +/** + * operator delete. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_delete(embb_mtapi_scheduler_t * that); + +/** + * Default constructor. Using default scheduling strategy. + * \memberof embb_mtapi_scheduler_struct + * \returns MTAPI_TRUE on success, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_scheduler_initialize(embb_mtapi_scheduler_t * that); + +/** + * Constructor allowing manual setting of scheduling strategy. + * \memberof embb_mtapi_scheduler_struct + * \returns MTAPI_TRUE on success, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_scheduler_initialize_with_mode( + embb_mtapi_scheduler_t * that, + embb_mtapi_scheduler_mode_t mode); + +/** + * Destructor. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_finalize(embb_mtapi_scheduler_t * that); + +/** + * Apply visitor to all Tasks in the queues of the scheduler. + * \memberof embb_mtapi_scheduler_struct + */ +mtapi_boolean_t embb_mtapi_scheduler_process_tasks( + embb_mtapi_scheduler_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data); + +/** + * Put a Task into one of the queues of the scheduler, the tasks state needs + * to be either MTAPI_TASK_SCHEDULED or MTAPI_TASK_RETAINED. + * \memberof embb_mtapi_scheduler_struct + */ +mtapi_boolean_t embb_mtapi_scheduler_schedule_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_task_t * task, + mtapi_uint_t instance); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_H_ diff --git b/mtapi_c/src/embb_mtapi_scheduler_t_fwd.h a/mtapi_c/src/embb_mtapi_scheduler_t_fwd.h new file mode 100644 index 0000000..09fab76 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_scheduler_t_fwd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Scheduler type. + * \memberof embb_mtapi_scheduler_struct + */ +typedef struct embb_mtapi_scheduler_struct embb_mtapi_scheduler_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_task_context_t.c a/mtapi_c/src/embb_mtapi_task_context_t.c new file mode 100644 index 0000000..9ca279a --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_context_t.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_task_context_initialize_with_thread_context_and_task( + embb_mtapi_task_context_t* that, + embb_mtapi_thread_context_t* thread_context, + embb_mtapi_task_t* task) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != thread_context); + assert(MTAPI_NULL != task); + + that->task = task; + that->thread_context = thread_context; + that->num_instances = task->attributes.num_instances; + that->instance_num = + embb_atomic_fetch_and_add_unsigned_int(&task->current_instance, 1); +} + +void embb_mtapi_task_context_finalize(embb_mtapi_task_context_t* that) { + assert(MTAPI_NULL != that); + + that->instance_num = 0; + that->num_instances = 0; + that->task = MTAPI_NULL; + that->thread_context = MTAPI_NULL; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_context_status_set( + MTAPI_INOUT mtapi_task_context_t* task_context, + MTAPI_IN mtapi_status_t error_code, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_status_set() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + /* for remote actions the result shall be transferred to the + waiting node at the end of the task */ + switch (error_code) { + case MTAPI_SUCCESS: + case MTAPI_ERR_ARG_SIZE: + case MTAPI_ERR_RESULT_SIZE: + case MTAPI_ERR_ACTION_CANCELLED: + case MTAPI_ERR_ACTION_FAILED: + case MTAPI_ERR_ACTION_DELETED: + task_context->task->error_code = error_code; + local_status = MTAPI_SUCCESS; + break; + case MTAPI_ERR_CORE_NUM: + case MTAPI_ERR_RUNTIME_LOADBALANCING_NOTSUPPORTED: + case MTAPI_ERR_RUNTIME_REMOTETASKS_NOTSUPPORTED: + case MTAPI_ERR_ARG_NOT_IMPLEMENTED: + case MTAPI_ERR_FUNC_NOT_IMPLEMENTED: + case MTAPI_ERR_WAIT_PENDING: + case MTAPI_ERR_BUFFER_SIZE: + case MTAPI_ERR_UNKNOWN: + case MTAPI_GROUP_COMPLETED: + case MTAPI_ERR_GROUP_LIMIT: + case MTAPI_ERR_GROUP_INVALID: + case MTAPI_ERR_QUEUE_LIMIT: + case MTAPI_ERR_QUEUE_DISABLED: + case MTAPI_ERR_QUEUE_DELETED: + case MTAPI_ERR_QUEUE_INVALID: + case MTAPI_ERR_JOB_INVALID: + case MTAPI_ERR_TASK_LIMIT: + case MTAPI_ERR_TASK_INVALID: + case MTAPI_ERR_CONTEXT_OUTOFCONTEXT: + case MTAPI_ERR_CONTEXT_INVALID: + case MTAPI_ERR_ACTION_DISABLED: + case MTAPI_ERR_ACTION_NUM_INVALID: + case MTAPI_ERR_ACTION_LIMIT: + case MTAPI_ERR_ACTION_EXISTS: + case MTAPI_ERR_ACTION_INVALID: + case MTAPI_ERR_NODE_NOTINIT: + case MTAPI_ERR_DOMAIN_INVALID: + case MTAPI_ERR_NODE_INVALID: + case MTAPI_ERR_NODE_INITIALIZED: + case MTAPI_ERR_NODE_INITFAILED: + case MTAPI_ERR_ATTR_SIZE: + case MTAPI_ERR_ATTR_READONLY: + case MTAPI_ERR_PARAMETER: + case MTAPI_TIMEOUT: + case MTAPI_ERR_ATTR_NUM: + default: + /* trying to set invalid error code */ + local_status = MTAPI_ERR_PARAMETER; + break; + } + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_context_runtime_notify( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_IN mtapi_notification_t notification, + MTAPI_IN void* data, + MTAPI_IN mtapi_size_t data_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(notification); + EMBB_UNUSED(data); + EMBB_UNUSED(data_size); + + embb_mtapi_log_trace("mtapi_context_runtime_notify() called\n"); + + if (MTAPI_NULL != task_context) { + mtapi_task_context_t* local_context = (mtapi_task_context_t*) embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context) { + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); +} + +mtapi_task_state_t mtapi_context_taskstate_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_task_state_t task_state = MTAPI_TASK_ERROR; + + embb_mtapi_log_trace("mtapi_context_taskstate_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + task_state = (mtapi_task_state_t)embb_atomic_load_int( + &task_context->task->state); + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return task_state; +} + +/* derived from OpenMP's omp_get_thread_num(); */ +mtapi_uint_t mtapi_context_instnum_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t instnum = 0; + + embb_mtapi_log_trace("mtapi_context_instnum_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + instnum = task_context->instance_num; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return instnum; +} + +/* derived from OpenMP's omp_get_num_threads(); */ +mtapi_uint_t mtapi_context_numinst_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t numinst = 0; + + embb_mtapi_log_trace("mtapi_context_numinst_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + numinst = task_context->num_instances; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return numinst; +} + +mtapi_uint_t mtapi_context_corenum_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t corenum = 0; + + embb_mtapi_log_trace("mtapi_context_corenum_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + corenum = task_context->thread_context->core_num; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return corenum; +} diff --git b/mtapi_c/src/embb_mtapi_task_context_t.h a/mtapi_c/src/embb_mtapi_task_context_t.h new file mode 100644 index 0000000..26f33f6 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_context_t.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Task context class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_task_context_struct { + mtapi_uint_t instance_num; + mtapi_uint_t num_instances; + embb_mtapi_task_t* task; + embb_mtapi_thread_context_t* thread_context; +}; + +#include + +/** + * Constructor from a thread_context and a task. + * \memberof embb_mtapi_task_context_struct + */ +void embb_mtapi_task_context_initialize_with_thread_context_and_task( + embb_mtapi_task_context_t* that, + embb_mtapi_thread_context_t* thread_context, + embb_mtapi_task_t* task); + +/** + * Destructor. + * \memberof embb_mtapi_task_context_struct + */ +void embb_mtapi_task_context_finalize(embb_mtapi_task_context_t* that); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_H_ diff --git b/mtapi_c/src/embb_mtapi_task_context_t_fwd.h a/mtapi_c/src/embb_mtapi_task_context_t_fwd.h new file mode 100644 index 0000000..a7c2b42 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_context_t_fwd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Task context type. + * \memberof embb_mtapi_task_context_struct + */ +typedef struct embb_mtapi_task_context_struct embb_mtapi_task_context_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_task_queue_t.c a/mtapi_c/src/embb_mtapi_task_queue_t.c new file mode 100644 index 0000000..8bb9f7f --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_queue_t.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_task_queue_initialize(embb_mtapi_task_queue_t* that) { + assert(MTAPI_NULL != that); + + that->task_buffer = MTAPI_NULL; + that->tasks_available = 0; + that->get_task_position = 0; + that->put_task_position = 0; + mtapi_queueattr_init(&that->attributes, MTAPI_NULL); + embb_spin_init(&that->lock); +} + +void embb_mtapi_task_queue_initialize_with_capacity( + embb_mtapi_task_queue_t* that, + mtapi_uint_t capacity) { + assert(MTAPI_NULL != that); + + that->task_buffer = (embb_mtapi_task_t **) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_task_t *)*capacity); + that->tasks_available = 0; + that->get_task_position = 0; + that->put_task_position = 0; + mtapi_queueattr_init(&that->attributes, MTAPI_NULL); + that->attributes.limit = capacity; + embb_spin_init(&that->lock); +} + +void embb_mtapi_task_queue_finalize(embb_mtapi_task_queue_t* that) { + embb_mtapi_alloc_deallocate(that->task_buffer); + that->task_buffer = MTAPI_NULL; + + embb_mtapi_task_queue_initialize(that); + + embb_spin_destroy(&that->lock); +} + +embb_mtapi_task_t * embb_mtapi_task_queue_pop(embb_mtapi_task_queue_t* that) { + embb_mtapi_task_t * task = MTAPI_NULL; + + assert(MTAPI_NULL != that); + + if (embb_spin_try_lock(&that->lock, 128) == EMBB_SUCCESS) { + if (0 < that->tasks_available) { + /* take away one task */ + that->tasks_available--; + + /* acquire position to fetch task from */ + mtapi_uint_t task_position = that->get_task_position; + that->get_task_position++; + if (that->attributes.limit <= that->get_task_position) { + that->get_task_position = 0; + } + + /* fetch task */ + task = that->task_buffer[task_position]; + + /* make task entry invalid just in case */ + that->task_buffer[task_position] = MTAPI_NULL; + } + embb_spin_unlock(&that->lock); + } + + return task; +} + +mtapi_boolean_t embb_mtapi_task_queue_push( + embb_mtapi_task_queue_t* that, + embb_mtapi_task_t * task) { + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != that); + + if (embb_spin_lock(&that->lock) == EMBB_SUCCESS) { + if (that->attributes.limit > that->tasks_available) { + /* acquire position to put task into */ + mtapi_uint_t task_position = that->put_task_position; + that->put_task_position++; + if (that->attributes.limit <= that->put_task_position) { + that->put_task_position = 0; + } + + /* put task into buffer */ + that->task_buffer[task_position] = task; + + /* make task available */ + that->tasks_available++; + + result = MTAPI_TRUE; + } + embb_spin_unlock(&that->lock); + } + + return result; +} + +mtapi_boolean_t embb_mtapi_task_queue_process( + embb_mtapi_task_queue_t * that, + embb_mtapi_task_visitor_function_t process, + void * user_data) { + mtapi_boolean_t result = MTAPI_TRUE; + mtapi_uint_t ii; + mtapi_uint_t idx; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != process); + + if (embb_spin_lock(&that->lock) == EMBB_SUCCESS) { + idx = that->get_task_position; + for (ii = 0; ii < that->tasks_available; ii++) { + result = process(that->task_buffer[ii], user_data); + if (MTAPI_FALSE == result) { + break; + } + idx = (idx + 1) % that->attributes.limit; + } + embb_spin_unlock(&that->lock); + } + + return result; +} diff --git b/mtapi_c/src/embb_mtapi_task_queue_t.h a/mtapi_c/src/embb_mtapi_task_queue_t.h new file mode 100644 index 0000000..10df771 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_queue_t.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Task queue class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_task_queue_struct { + embb_mtapi_task_t ** task_buffer; + mtapi_uint_t tasks_available; + mtapi_uint_t get_task_position; + mtapi_uint_t put_task_position; + mtapi_queue_attributes_t attributes; + embb_spinlock_t lock; +}; + +#include + +/** + * Default constructor. + * \memberof embb_mtapi_task_queue_struct + */ +void embb_mtapi_task_queue_initialize(embb_mtapi_task_queue_t* that); + +/** + * Constructor with configurable capacity. + * \memberof embb_mtapi_task_queue_struct + */ +void embb_mtapi_task_queue_initialize_with_capacity( + embb_mtapi_task_queue_t* that, + mtapi_uint_t capacity); + +/** + * Destructor. + * \memberof embb_mtapi_task_queue_struct + */ +void embb_mtapi_task_queue_finalize(embb_mtapi_task_queue_t* that); + +/** + * Pop a task from the queue. Returns MTAPI_NULL if the queue is empty. + * \memberof embb_mtapi_task_queue_struct + */ +embb_mtapi_task_t * embb_mtapi_task_queue_pop(embb_mtapi_task_queue_t* that); + +/** + * Push a task into the queue. Returns MTAPI_TRUE if successfull and + * MTAPI_FALSE if the queue is full or cannot be locked in time. + * \memberof embb_mtapi_task_queue_struct + */ +mtapi_boolean_t embb_mtapi_task_queue_push( + embb_mtapi_task_queue_t* that, + embb_mtapi_task_t * task); + + +/** + * Process all elements of the task queue using the given functor. + * \memberof embb_mtapi_task_queue_struct + */ +mtapi_boolean_t embb_mtapi_task_queue_process( + embb_mtapi_task_queue_t * that, + embb_mtapi_task_visitor_function_t process, + void * user_data); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_H_ diff --git b/mtapi_c/src/embb_mtapi_task_queue_t_fwd.h a/mtapi_c/src/embb_mtapi_task_queue_t_fwd.h new file mode 100644 index 0000000..5ad9b0d --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_queue_t_fwd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Task queue type. + * \memberof embb_mtapi_task_queue_struct + */ +typedef struct embb_mtapi_task_queue_struct embb_mtapi_task_queue_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_task_t.c a/mtapi_c/src/embb_mtapi_task_t.c new file mode 100644 index 0000000..9abc3ca --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_t.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +#include +embb_mtapi_pool_implementation(task) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +embb_mtapi_task_t* embb_mtapi_task_new(embb_mtapi_task_pool_t* pool) { + embb_mtapi_task_t* that; + + assert(MTAPI_NULL != pool); + + that = embb_mtapi_task_pool_allocate(pool); + if (MTAPI_NULL != that) { + embb_mtapi_task_initialize(that); + } + + return that; +} + +void embb_mtapi_task_delete( + embb_mtapi_task_t* that, + embb_mtapi_task_pool_t* pool) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != pool); + + embb_mtapi_task_finalize(that); + embb_mtapi_task_pool_deallocate(pool, that); +} + +void embb_mtapi_task_initialize(embb_mtapi_task_t* that) { + assert(MTAPI_NULL != that); + + that->action.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->job.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + embb_atomic_store_int(&that->state, MTAPI_TASK_ERROR); + that->task_id = MTAPI_TASK_ID_NONE; + that->group.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->queue.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->error_code = MTAPI_SUCCESS; + embb_atomic_store_unsigned_int(&that->current_instance, 0); + embb_spin_init(&that->state_lock); +} + +void embb_mtapi_task_finalize(embb_mtapi_task_t* that) { + assert(MTAPI_NULL != that); + + embb_mtapi_task_initialize(that); + embb_spin_destroy(&that->state_lock); +} + +mtapi_boolean_t embb_mtapi_task_execute( + embb_mtapi_task_t* that, + embb_mtapi_task_context_t * context) { + unsigned int todo = that->attributes.num_instances; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != context); + + embb_mtapi_task_set_state(that, MTAPI_TASK_RUNNING); + + /* is the associated action valid? */ + if (embb_mtapi_action_pool_is_handle_valid( + context->thread_context->node->action_pool, that->action)) { + /* fetch action and execute */ + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + context->thread_context->node->action_pool, that->action); + /* only continue if there was no error so far */ + if (context->task->error_code == MTAPI_SUCCESS) { + local_action->action_function( + that->arguments, + that->arguments_size, + that->result_buffer, + that->result_size, + local_action->node_local_data, + local_action->node_local_data_size, + context); + } + embb_atomic_memory_barrier(); + todo = embb_atomic_fetch_and_add_unsigned_int( + &that->instances_todo, (unsigned int)-1); + + if (todo == 1) { + /* task has completed successfully */ + embb_mtapi_task_set_state(that, MTAPI_TASK_COMPLETED); + } + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + } else { + /* action was deleted, task did not complete */ + that->error_code = MTAPI_ERR_ACTION_DELETED; + embb_mtapi_task_set_state(that, MTAPI_TASK_ERROR); + } + + if (todo == 1) { + /* is task associated with a group? */ + if (embb_mtapi_group_pool_is_handle_valid( + context->thread_context->node->group_pool, that->group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + context->thread_context->node->group_pool, that->group); + embb_mtapi_task_queue_push(&local_group->queue, that); + } + return MTAPI_TRUE; + } else { + return MTAPI_FALSE; + } +} + +void embb_mtapi_task_set_state( + embb_mtapi_task_t* that, + mtapi_task_state_t state) { + assert(MTAPI_NULL != that); + + embb_spin_lock(&that->state_lock); + embb_atomic_store_int(&that->state, state); + embb_atomic_memory_barrier(); + embb_spin_unlock(&that->state_lock); +} + +static mtapi_task_hndl_t embb_mtapi_task_start( + MTAPI_IN mtapi_task_id_t task_id, + MTAPI_IN mtapi_job_hndl_t job, + MTAPI_IN void* arguments, + MTAPI_IN mtapi_size_t arguments_size, + MTAPI_OUT void* result_buffer, + MTAPI_IN mtapi_size_t result_size, + MTAPI_IN mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_task_hndl_t task_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("embb_mtapi_task_start() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_job_is_handle_valid(node, job)) { + embb_mtapi_job_t* local_job = + embb_mtapi_job_get_storage_for_id(node, job.id); + embb_mtapi_task_t* task = embb_mtapi_task_pool_allocate(node->task_pool); + if (MTAPI_NULL != task) { + mtapi_uint_t action_index; + + embb_mtapi_task_initialize(task); + embb_mtapi_task_set_state(task, MTAPI_TASK_PRENATAL); + task->task_id = task_id; + task->job = job; + task->arguments = arguments; + task->arguments_size = arguments_size; + task->result_buffer = result_buffer; + task->result_size = result_size; + + if (MTAPI_NULL != attributes) { + task->attributes = *attributes; + } else { + mtapi_taskattr_init(&task->attributes, &local_status); + } + + embb_atomic_store_unsigned_int( + &task->instances_todo, task->attributes.num_instances); + + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + task->group = group; + embb_atomic_fetch_and_add_int(&local_group->num_tasks, 1); + } else { + task->group.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + task->queue = queue; + embb_mtapi_queue_task_started(local_queue); + } else { + task->queue.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + + /* load balancing: choose action with minimum tasks */ + action_index = 0; + for (mtapi_uint_t ii = 0; ii < local_job->num_actions; ii++) { + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_job->actions[ii])) { + embb_mtapi_action_t * act_m = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_job->actions[action_index]); + embb_mtapi_action_t * act_i = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_job->actions[ii]); + if (embb_atomic_load_int(&act_m->num_tasks) > + embb_atomic_load_int(&act_i->num_tasks)) { + action_index = ii; + } + } + } + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_job->actions[action_index])) { + task->action = local_job->actions[action_index]; + embb_mtapi_task_set_state(task, MTAPI_TASK_CREATED); + task_hndl = task->handle; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + + /* check priority for validity */ + if (node->attributes.max_priorities <= task->attributes.priority) { + local_status = MTAPI_ERR_PARAMETER; + } + + if (MTAPI_SUCCESS == local_status) { + embb_mtapi_scheduler_t * scheduler = node->scheduler; + mtapi_boolean_t was_scheduled; + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, task->action); + + embb_mtapi_task_set_state(task, MTAPI_TASK_SCHEDULED); + + if (local_action->is_plugin_action) { + /* schedule plugin task */ + mtapi_status_t plugin_status = MTAPI_ERR_UNKNOWN; + local_action->plugin_task_start_function( + task_hndl, &plugin_status); + was_scheduled = (MTAPI_SUCCESS == plugin_status) ? + MTAPI_TRUE : MTAPI_FALSE; + } else { + /* schedule local task */ + was_scheduled = MTAPI_TRUE; + + for (mtapi_uint_t kk = 0; kk < task->attributes.num_instances; + kk++) { + was_scheduled = (mtapi_boolean_t)(was_scheduled & + embb_mtapi_scheduler_schedule_task(scheduler, task, kk)); + } + } + + if (was_scheduled) { + /* if task is detached, do not return a handle, it will be deleted + on completion */ + if (task->attributes.is_detached) { + task_hndl.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + + local_status = MTAPI_SUCCESS; + } else { + /* task could not be pushed */ + local_status = MTAPI_ERR_TASK_LIMIT; + embb_mtapi_task_set_state(task, MTAPI_TASK_ERROR); + } + } + + if (MTAPI_SUCCESS != local_status) { + embb_mtapi_task_delete(task, node->task_pool); + task_hndl.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + } else { + local_status = MTAPI_ERR_TASK_LIMIT; + } + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return task_hndl; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_task_hndl_t mtapi_task_start( + MTAPI_IN mtapi_task_id_t task_id, + MTAPI_IN mtapi_job_hndl_t job, + MTAPI_IN void* arguments, + MTAPI_IN mtapi_size_t arguments_size, + MTAPI_OUT void* result_buffer, /* pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /* size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT mtapi_status_t* status) { + mtapi_queue_hndl_t queue_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_task_start() called\n"); + + return embb_mtapi_task_start( + task_id, + job, + arguments, + arguments_size, + result_buffer, + result_size, + attributes, + group, + queue_hndl, + status); +} + +mtapi_task_hndl_t mtapi_task_enqueue( + MTAPI_IN mtapi_task_id_t task_id, + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN void* arguments, + MTAPI_IN mtapi_size_t arguments_size, + MTAPI_OUT void* result_buffer, /* pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /* size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_task_hndl_t task_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_task_enqueue() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle(node->queue_pool, queue); + if ((MTAPI_TRUE == embb_atomic_load_char(&local_queue->enabled)) || + local_queue->attributes.retain) { + mtapi_task_attributes_t local_attributes; + if (MTAPI_NULL != attributes) { + local_attributes = *attributes; + } else { + mtapi_taskattr_init(&local_attributes, MTAPI_NULL); + } + local_attributes.priority = local_queue->attributes.priority; + + task_hndl = embb_mtapi_task_start( + task_id, + local_queue->job_handle, + arguments, + arguments_size, + result_buffer, + result_size, + &local_attributes, + group, + queue, + &local_status); + } else { + local_status = MTAPI_ERR_QUEUE_DISABLED; + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return task_hndl; +} + +void mtapi_task_get_attribute( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_task_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t* local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_TASK_DETACHED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_task->attributes.is_detached, attribute, attribute_size); + break; + + case MTAPI_TASK_INSTANCES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_task->attributes.num_instances, attribute, attribute_size); + break; + + case MTAPI_TASK_PRIORITY: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_task->attributes.priority, attribute, attribute_size); + break; + + default: + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_task_wait( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_task_wait() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t* local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + if (embb_mtapi_scheduler_wait_for_task(local_task, timeout)) { + local_status = local_task->error_code; + + /* delete task if it is not in a group, otherwise the group will take + care of deletion */ + if (MTAPI_FALSE == embb_mtapi_group_pool_is_handle_valid( + node->group_pool, local_task->group)) { + embb_mtapi_task_delete(local_task, node->task_pool); + } + } else { + local_status = MTAPI_TIMEOUT; + } + } else { + local_status = MTAPI_ERR_TASK_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_task_cancel() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t* local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + /* call plugin action cancel function */ + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action); + if (local_action->is_plugin_action) { + local_action->plugin_task_cancel_function(task, &local_status); + } else { + embb_mtapi_task_set_state(local_task, MTAPI_TASK_CANCELLED); + local_task->error_code = MTAPI_ERR_ACTION_CANCELLED; + local_status = MTAPI_SUCCESS; + } + } else { + embb_mtapi_task_set_state(local_task, MTAPI_TASK_CANCELLED); + local_task->error_code = MTAPI_ERR_ACTION_CANCELLED; + local_status = MTAPI_SUCCESS; + } + } else { + local_status = MTAPI_ERR_TASK_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/embb_mtapi_task_t.h a/mtapi_c/src/embb_mtapi_task_t.h new file mode 100644 index 0000000..2abf41c --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_t.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Task class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_task_struct { + mtapi_task_hndl_t handle; + + mtapi_task_id_t task_id; + mtapi_job_hndl_t job; + const void * arguments; + mtapi_size_t arguments_size; + void * result_buffer; + mtapi_size_t result_size; + mtapi_task_attributes_t attributes; + mtapi_group_hndl_t group; + mtapi_queue_hndl_t queue; + + mtapi_action_hndl_t action; + embb_spinlock_t state_lock; + embb_atomic_int state; + embb_atomic_unsigned_int current_instance; + embb_atomic_unsigned_int instances_todo; + + mtapi_status_t error_code; +}; + +#include + +/** + * Pooled operator new. + * \memberof embb_mtapi_task_struct + */ +embb_mtapi_task_t* embb_mtapi_task_new(embb_mtapi_task_pool_t* pool); + +/** + * Pooled operator delete. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_delete( + embb_mtapi_task_t* that, + embb_mtapi_task_pool_t* pool); + +/** + * Default constructor. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_initialize(embb_mtapi_task_t* that); + +/** + * Destructor. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_finalize(embb_mtapi_task_t* that); + +/** + * Execute the action function of a task within the given context. Notfies + * the associated task group or queue if set. Deletes the task if it is + * detached. + * \memberof embb_mtapi_task_struct + */ +mtapi_boolean_t embb_mtapi_task_execute( + embb_mtapi_task_t* that, + embb_mtapi_task_context_t * context); + +/** + * Set the current task state. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_set_state( + embb_mtapi_task_t* that, + mtapi_task_state_t state); + + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(task) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_T_H_ diff --git b/mtapi_c/src/embb_mtapi_task_t_fwd.h a/mtapi_c/src/embb_mtapi_task_t_fwd.h new file mode 100644 index 0000000..3c3f170 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_t_fwd.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Task type. + * \memberof embb_mtapi_task_struct + */ +typedef struct embb_mtapi_task_struct embb_mtapi_task_t; + +/** + * Task pool type. + * \memberof embb_mtapi_task_pool_struct + */ +typedef struct embb_mtapi_task_pool_struct embb_mtapi_task_pool_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_T_FWD_H_ diff --git b/mtapi_c/src/embb_mtapi_task_visitor_function_t.h a/mtapi_c/src/embb_mtapi_task_visitor_function_t.h new file mode 100644 index 0000000..ee2b3e4 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_task_visitor_function_t.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_TASK_VISITOR_FUNCTION_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_VISITOR_FUNCTION_T_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include + + +/* ---- TYPE DEFINITIONS --------------------------------------------------- */ + +typedef mtapi_boolean_t(*embb_mtapi_task_visitor_function_t) + (embb_mtapi_task_t * task, void * user_data); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_VISITOR_FUNCTION_T_H_ diff --git b/mtapi_c/src/embb_mtapi_thread_context_t.c a/mtapi_c/src/embb_mtapi_thread_context_t.c new file mode 100644 index 0000000..e791824 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_thread_context_t.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +mtapi_boolean_t embb_mtapi_thread_context_initialize_with_node_worker_and_core( + embb_mtapi_thread_context_t* that, + embb_mtapi_node_t* node, + mtapi_uint_t worker_index, + mtapi_uint_t core_num) { + mtapi_uint_t ii; + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + that->node = node; + that->worker_index = worker_index; + that->core_num = core_num; + that->priorities = node->attributes.max_priorities; + that->is_initialized = MTAPI_FALSE; + that->is_main_thread = (worker_index == 0) ? node->attributes.reuse_main_thread : MTAPI_FALSE; + embb_atomic_store_int(&that->run, 0); + + that->queue = (embb_mtapi_task_queue_t**)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_task_queue_t)*that->priorities); + if (that->queue == NULL) { + that->private_queue = NULL; + return MTAPI_FALSE; + } + for (ii = 0; ii < that->priorities; ii++) { + that->queue[ii] = (embb_mtapi_task_queue_t*) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_task_queue_t)); + if (that->queue[ii] != NULL) { + embb_mtapi_task_queue_initialize_with_capacity( + that->queue[ii], node->attributes.queue_limit); + } else { + result = MTAPI_FALSE; + } + } + if (!result) { + return MTAPI_FALSE; + } + + that->private_queue = (embb_mtapi_task_queue_t**)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_task_queue_t)*that->priorities); + if (that->private_queue == NULL) { + return MTAPI_FALSE; + } + for (ii = 0; ii < that->priorities; ii++) { + that->private_queue[ii] = (embb_mtapi_task_queue_t*) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_task_queue_t)); + if (that->private_queue[ii] != NULL) { + embb_mtapi_task_queue_initialize_with_capacity( + that->private_queue[ii], node->attributes.queue_limit); + } else { + result = MTAPI_FALSE; + } + } + if (!result) { + return MTAPI_FALSE; + } + + embb_mutex_init(&that->work_available_mutex, EMBB_MUTEX_PLAIN); + embb_condition_init(&that->work_available); + embb_atomic_store_int(&that->is_sleeping, 0); + + that->is_initialized = MTAPI_TRUE; + + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_thread_context_start( + embb_mtapi_thread_context_t* that, + embb_mtapi_scheduler_t * scheduler) { + int err; + embb_mtapi_scheduler_worker_func_t * worker_func; + embb_core_set_t core_set; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != scheduler); + + worker_func = embb_mtapi_scheduler_worker_func(scheduler); + + /* pin thread to core */ + embb_core_set_init(&core_set, 0); + embb_core_set_add(&core_set, that->core_num); + + /* create thread */ + if (that->is_main_thread) { + /* reuse main thread */ + that->thread = embb_thread_current(); + err = embb_tss_create(&that->tss_id); + if (EMBB_SUCCESS != err) { + /* report error to scheduler */ + embb_atomic_store_int(&that->run, -1); + return MTAPI_FALSE; + } + embb_tss_set(&(that->tss_id), that); + embb_atomic_store_int(&that->run, 1); + } else { + err = embb_thread_create(&that->thread, &core_set, worker_func, that); + if (EMBB_SUCCESS != err) { + embb_mtapi_log_error( + "embb_mtapi_ThreadContext_initializeWithNodeAndCoreNumber() could not " + "create thread %d on core %d\n", that->worker_index, that->core_num); + return MTAPI_FALSE; + } + /* wait for worker to come up */ + while (0 == embb_atomic_load_int(&that->run)) { + embb_thread_yield(); + } + } + + if (0 < embb_atomic_load_int(&that->run)) { + return MTAPI_TRUE; + } else { + return MTAPI_FALSE; + } +} + +void embb_mtapi_thread_context_stop(embb_mtapi_thread_context_t* that) { + int result; + if (0 < embb_atomic_load_int(&that->run)) { + embb_atomic_store_int(&that->run, 0); + embb_condition_notify_one(&that->work_available); + if (MTAPI_FALSE == that->is_main_thread) { + embb_thread_join(&(that->thread), &result); + } + } +} + +void embb_mtapi_thread_context_finalize(embb_mtapi_thread_context_t* that) { + mtapi_uint_t ii; + + assert(MTAPI_NULL != that); + + embb_mtapi_log_trace("embb_mtapi_thread_context_finalize() called\n"); + + if (that->is_initialized) { + if (that->is_main_thread) { + embb_tss_delete(&that->tss_id); + } + embb_condition_destroy(&that->work_available); + embb_mutex_destroy(&that->work_available_mutex); + } + + if (that->queue != NULL) { + for (ii = 0; ii < that->priorities; ii++) { + if (that->queue[ii] != NULL) { + embb_mtapi_task_queue_finalize(that->queue[ii]); + embb_mtapi_alloc_deallocate(that->queue[ii]); + that->queue[ii] = MTAPI_NULL; + } + } + embb_mtapi_alloc_deallocate(that->queue); + that->queue = MTAPI_NULL; + } + + if (that->private_queue != NULL) { + for (ii = 0; ii < that->priorities; ii++) { + if (that->private_queue[ii] != NULL) { + embb_mtapi_task_queue_finalize(that->private_queue[ii]); + embb_mtapi_alloc_deallocate(that->private_queue[ii]); + that->private_queue[ii] = MTAPI_NULL; + } + } + embb_mtapi_alloc_deallocate(that->private_queue); + that->private_queue = MTAPI_NULL; + } + + that->priorities = 0; + that->is_initialized = MTAPI_FALSE; + + that->node = MTAPI_NULL; +} + +mtapi_boolean_t embb_mtapi_thread_context_process_tasks( + embb_mtapi_thread_context_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data) { + mtapi_uint_t ii; + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != process); + + for (ii = 0; ii < that->priorities; ii++) { + result = embb_mtapi_task_queue_process( + that->private_queue[ii], process, user_data); + if (MTAPI_FALSE == result) { + break; + } + result = embb_mtapi_task_queue_process( + that->queue[ii], process, user_data); + if (MTAPI_FALSE == result) { + break; + } + } + + return result; +} diff --git b/mtapi_c/src/embb_mtapi_thread_context_t.h a/mtapi_c/src/embb_mtapi_thread_context_t.h new file mode 100644 index 0000000..a2ba731 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_thread_context_t.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +#include +#include +#include + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Thread context class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_thread_context_struct { + embb_mutex_t work_available_mutex; + embb_condition_t work_available; + embb_thread_t thread; + embb_tss_t tss_id; + embb_atomic_int is_sleeping; + + embb_mtapi_node_t* node; + embb_mtapi_task_queue_t** queue; + embb_mtapi_task_queue_t** private_queue; + + mtapi_uint_t priorities; + mtapi_uint_t worker_index; + mtapi_uint_t core_num; + embb_atomic_int run; + mtapi_status_t status; + mtapi_boolean_t is_initialized; + mtapi_boolean_t is_main_thread; +}; + +#include + +/** + * Constructor using attributes from node and a given core number. + * \memberof embb_mtapi_thread_context_struct + * \returns MTAPI_TRUE if successful, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_thread_context_initialize_with_node_worker_and_core( + embb_mtapi_thread_context_t* that, + embb_mtapi_node_t* node, + mtapi_uint_t worker_index, + mtapi_uint_t core_num); + +/** + * Destructor. + * \memberof embb_mtapi_thread_context_struct + */ +void embb_mtapi_thread_context_finalize(embb_mtapi_thread_context_t* that); + +/** + * Start worker thread. + * \memberof embb_mtapi_thread_context_struct + * \returns MTAPI_TRUE if successful, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_thread_context_start( + embb_mtapi_thread_context_t* that, + embb_mtapi_scheduler_t * scheduler); + +/** + * Stop worker thread. + * \memberof embb_mtapi_thread_context_struct + */ +void embb_mtapi_thread_context_stop(embb_mtapi_thread_context_t* that); + +/** + * Apply visitor function to all tasks in the queues of the context. + * \memberof embb_mtapi_thread_context_struct + */ +mtapi_boolean_t embb_mtapi_thread_context_process_tasks( + embb_mtapi_thread_context_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_H_ diff --git b/mtapi_c/src/embb_mtapi_thread_context_t_fwd.h a/mtapi_c/src/embb_mtapi_thread_context_t_fwd.h new file mode 100644 index 0000000..cc05d15 --- /dev/null +++ a/mtapi_c/src/embb_mtapi_thread_context_t_fwd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_FWD_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_FWD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Thread context type. + * \memberof embb_mtapi_thread_context_struct + */ +typedef struct embb_mtapi_thread_context_struct embb_mtapi_thread_context_t; + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_FWD_H_ diff --git b/mtapi_c/src/mtapi_action_attributes_t.c a/mtapi_c/src/mtapi_action_attributes_t.c new file mode 100644 index 0000000..e5db243 --- /dev/null +++ a/mtapi_c/src/mtapi_action_attributes_t.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_actionattr_init( + MTAPI_OUT mtapi_action_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_actionattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->domain_shared = MTAPI_TRUE; + attributes->global = MTAPI_TRUE; + mtapi_affinity_init(&attributes->affinity, MTAPI_TRUE, &local_status); + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_actionattr_set( + MTAPI_INOUT mtapi_action_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_actionattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_ACTION_GLOBAL: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->global, attribute, attribute_size); + break; + + case MTAPI_ACTION_AFFINITY: + local_status = embb_mtapi_attr_set_mtapi_affinity_t( + &attributes->affinity, attribute, attribute_size); + break; + + case MTAPI_ACTION_DOMAIN_SHARED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->domain_shared, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + /* this should not happen, if someone calls set, a valid action_attributes + pointer should be supplied */ + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/mtapi_affinity_t.c a/mtapi_c/src/mtapi_affinity_t.c new file mode 100644 index 0000000..3705a4b --- /dev/null +++ a/mtapi_c/src/mtapi_affinity_t.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_affinity_init( + MTAPI_OUT mtapi_affinity_t* mask, + MTAPI_IN mtapi_boolean_t affinity, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_affinity_init() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (MTAPI_NULL != mask) { + embb_bitset_clear_all(mask); + if (affinity) { + embb_bitset_set_n(mask, node->attributes.num_cores); + } + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_affinity_set( + MTAPI_INOUT mtapi_affinity_t* mask, + MTAPI_IN mtapi_uint_t core_num, + MTAPI_IN mtapi_boolean_t affinity, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_affinity_set() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (MTAPI_NULL != mask) { + if (core_num < node->attributes.num_cores) { + if (affinity) { + embb_bitset_set(mask, core_num); + } else { + embb_bitset_clear(mask, core_num); + } + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CORE_NUM; + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +mtapi_boolean_t mtapi_affinity_get( + MTAPI_OUT mtapi_affinity_t* mask, + MTAPI_IN mtapi_uint_t core_num, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_boolean_t affinity = MTAPI_FALSE; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_affinity_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (MTAPI_NULL != mask) { + if (core_num < node->attributes.num_cores) { + affinity = + embb_bitset_is_set(mask, core_num) ? MTAPI_TRUE : MTAPI_FALSE; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CORE_NUM; + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return affinity; +} diff --git b/mtapi_c/src/mtapi_group_attributes_t.c a/mtapi_c/src/mtapi_group_attributes_t.c new file mode 100644 index 0000000..c41d45c --- /dev/null +++ a/mtapi_c/src/mtapi_group_attributes_t.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_groupattr_init( + MTAPI_OUT mtapi_group_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_groupattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->some_value = 0; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_groupattr_set( + MTAPI_INOUT mtapi_group_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(attribute_num); + + embb_mtapi_log_trace("mtapi_groupattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + /* switch is not needed for now, since there are no attributes + switch (attribute_num) { + default:*/ + /* unknown attribute */ + local_status = MTAPI_ERR_ATTR_NUM; + /* break; + }*/ + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/mtapi_node_attributes_t.c a/mtapi_c/src/mtapi_node_attributes_t.c new file mode 100644 index 0000000..791bc37 --- /dev/null +++ a/mtapi_c/src/mtapi_node_attributes_t.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_nodeattr_init( + MTAPI_OUT mtapi_node_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_nodeattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->max_tasks = MTAPI_NODE_MAX_TASKS_DEFAULT; + attributes->type = MTAPI_NODE_TYPE_SMP; + attributes->max_actions = MTAPI_NODE_MAX_ACTIONS_DEFAULT; + attributes->max_groups = MTAPI_NODE_MAX_GROUPS_DEFAULT; + attributes->max_queues = MTAPI_NODE_MAX_QUEUES_DEFAULT; + attributes->queue_limit = MTAPI_NODE_QUEUE_LIMIT_DEFAULT; + attributes->max_jobs = MTAPI_NODE_MAX_JOBS_DEFAULT; + attributes->max_actions_per_job = MTAPI_NODE_MAX_ACTIONS_PER_JOB_DEFAULT; + attributes->max_priorities = MTAPI_NODE_MAX_PRIORITIES_DEFAULT; + attributes->reuse_main_thread = MTAPI_FALSE; + + embb_core_set_init(&attributes->core_affinity, 1); + attributes->num_cores = embb_core_set_count(&attributes->core_affinity); + + assert(embb_core_set_count(&attributes->core_affinity) == + embb_core_count_available()); + + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_nodeattr_set( + MTAPI_INOUT mtapi_node_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_nodeattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_NODE_CORE_AFFINITY: + if (MTAPI_NODE_CORE_AFFINITY_SIZE == attribute_size) { + attributes->core_affinity = *(embb_core_set_t*)attribute; + attributes->num_cores = + embb_core_set_count(&attributes->core_affinity); + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ATTR_SIZE; + } + break; + + case MTAPI_NODE_NUMCORES: + local_status = MTAPI_ERR_ATTR_READONLY; + break; + + case MTAPI_NODE_TYPE: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->type, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_TASKS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_tasks, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_actions, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_GROUPS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_groups, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_QUEUES: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_queues, attribute, attribute_size); + break; + + case MTAPI_NODE_QUEUE_LIMIT: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->queue_limit, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_JOBS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_jobs, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS_PER_JOB: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_actions_per_job, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_PRIORITIES: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_priorities, attribute, attribute_size); + break; + + case MTAPI_NODE_REUSE_MAIN_THREAD: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->reuse_main_thread, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + /* this should not happen, if someone calls set, a valid action_attributes + pointer should be supplied */ + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/mtapi_queue_attributes_t.c a/mtapi_c/src/mtapi_queue_attributes_t.c new file mode 100644 index 0000000..f41fe3c --- /dev/null +++ a/mtapi_c/src/mtapi_queue_attributes_t.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_queueattr_init( + MTAPI_OUT mtapi_queue_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_queueattr_init() called\n"); + + if (MTAPI_NULL != node) { + if (MTAPI_NULL != attributes) { + attributes->global = MTAPI_TRUE; + attributes->priority = 0; + attributes->limit = node->attributes.queue_limit; + attributes->ordered = MTAPI_TRUE; + attributes->retain = MTAPI_FALSE; + attributes->domain_shared = MTAPI_TRUE; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queueattr_set( + MTAPI_INOUT mtapi_queue_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queueattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_QUEUE_GLOBAL: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->global, attribute, attribute_size); + break; + + case MTAPI_QUEUE_PRIORITY: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->priority, attribute, attribute_size); + break; + + case MTAPI_QUEUE_LIMIT: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->limit, attribute, attribute_size); + break; + + case MTAPI_QUEUE_ORDERED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->ordered, attribute, attribute_size); + break; + + case MTAPI_QUEUE_RETAIN: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->retain, attribute, attribute_size); + break; + + case MTAPI_QUEUE_DOMAIN_SHARED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->domain_shared, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/src/mtapi_status_t.h a/mtapi_c/src/mtapi_status_t.h new file mode 100644 index 0000000..c7f3c98 --- /dev/null +++ a/mtapi_c/src/mtapi_status_t.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_SRC_MTAPI_STATUS_T_H_ +#define MTAPI_C_SRC_MTAPI_STATUS_T_H_ + +#include +#include + +EMBB_PLATFORM_INLINE void mtapi_status_set( + mtapi_status_t* status, + mtapi_status_t value) { + if (MTAPI_NULL != status) { + *status = value; + } +} + +#endif // MTAPI_C_SRC_MTAPI_STATUS_T_H_ diff --git b/mtapi_c/src/mtapi_task_attributes_t.c a/mtapi_c/src/mtapi_task_attributes_t.c new file mode 100644 index 0000000..135b948 --- /dev/null +++ a/mtapi_c/src/mtapi_task_attributes_t.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_taskattr_init( + MTAPI_OUT mtapi_task_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_taskattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->num_instances = 1; + attributes->is_detached = MTAPI_FALSE; + attributes->priority = 0; + attributes->complete_func = MTAPI_NULL; + mtapi_affinity_init(&attributes->affinity, MTAPI_TRUE, &local_status); + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_taskattr_set( + MTAPI_INOUT mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_taskattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_TASK_DETACHED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->is_detached, attribute, attribute_size); + break; + + case MTAPI_TASK_INSTANCES: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->num_instances, attribute, attribute_size); + break; + + case MTAPI_TASK_PRIORITY: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->priority, attribute, attribute_size); + break; + + case MTAPI_TASK_AFFINITY: + local_status = embb_mtapi_attr_set_mtapi_affinity_t( + &attributes->affinity, attribute, attribute_size); + break; + + case MTAPI_TASK_USER_DATA: + attributes->user_data = (void*)attribute; + local_status = MTAPI_SUCCESS; + break; + + case MTAPI_TASK_COMPLETE_FUNCTION: + memcpy(&attributes->complete_func, &attribute, sizeof(void*)); + local_status = MTAPI_SUCCESS; + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git b/mtapi_c/test/embb_mtapi_test_config.h a/mtapi_c/test/embb_mtapi_test_config.h new file mode 100644 index 0000000..6577c16 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_config.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_CONFIG_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_CONFIG_H_ + +#include +#include +#include + +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#define MTAPI_CHECK_STATUS(status) \ +if (MTAPI_SUCCESS != status) { \ + embb_mtapi_log_error("...error %d\n\n", status); \ +} \ +PT_ASSERT(MTAPI_SUCCESS == status) + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_CONFIG_H_ diff --git b/mtapi_c/test/embb_mtapi_test_error.cc a/mtapi_c/test/embb_mtapi_test_error.cc new file mode 100644 index 0000000..d7d8d91 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_error.cc @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 +#define INVALID_ATTRIBUTE 255 + +static embb_atomic_int wait; + +static void testErrorAction( + const void* /*args*/, + mtapi_size_t /*arg_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* /*task_context*/) { + while (1 == embb_atomic_load_int(&wait)) + embb_thread_yield(); + embb_atomic_store_int(&wait, 2); +} + +ErrorTest::ErrorTest() { + CreateUnit("mtapi error test").Add(&ErrorTest::TestBasic, this); +} + +static void TestNodeNotInit() { + mtapi_status_t status; + mtapi_affinity_t affinity; + mtapi_job_hndl_t job_hndl; + mtapi_queue_hndl_t queue_hndl; + mtapi_group_hndl_t group_hndl; + mtapi_action_hndl_t action_hndl; + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_node_id_get(&status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_node_get_attribute(THIS_NODE_ID, + MTAPI_NODE_MAX_ACTIONS, MTAPI_NULL, 0, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_domain_id_get(&status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + + status = MTAPI_ERR_UNKNOWN; + action_hndl = mtapi_action_create(1, testErrorAction, MTAPI_NULL, 0, + MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_set_attribute(action_hndl, + MTAPI_ACTION_GLOBAL, + MTAPI_ATTRIBUTE_VALUE(MTAPI_TRUE), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_get_attribute(action_hndl, + MTAPI_ACTION_GLOBAL, MTAPI_NULL, 0, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_enable(action_hndl, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_disable(action_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + + status = MTAPI_ERR_UNKNOWN; + job_hndl = mtapi_job_get(1, THIS_DOMAIN_ID, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + + status = MTAPI_ERR_UNKNOWN; + queue_hndl = mtapi_queue_create(1, job_hndl, + MTAPI_DEFAULT_QUEUE_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_enable(queue_hndl, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_disable(queue_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_delete(queue_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_get_attribute(queue_hndl, + MTAPI_QUEUE_DOMAIN_SHARED, MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_set_attribute(queue_hndl, + MTAPI_QUEUE_DOMAIN_SHARED, MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + + status = MTAPI_ERR_UNKNOWN; + group_hndl = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_group_delete(group_hndl, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_group_get_attribute(group_hndl, + 0, MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_group_set_attribute(group_hndl, + 0, MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_affinity_init(&affinity, MTAPI_TRUE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_affinity_set(&affinity, 0, MTAPI_TRUE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_affinity_get(&affinity, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_NOTINIT); + + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} + +static void TestLimits() { + mtapi_status_t status; + mtapi_node_attributes_t node_attr; + mtapi_action_hndl_t action_hndl, action_hndl_invalid; + mtapi_job_hndl_t job_hndl, job_hndl_invalid; + mtapi_task_hndl_t task_hndl, task_hndl_invalid; + mtapi_group_hndl_t group_hndl, group_hndl_invalid; + mtapi_queue_hndl_t queue_hndl, queue_hndl_invalid; + char buffer[128]; + + /* initialize a node with all limits set to 1 */ + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_ACTIONS, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_ACTIONS_PER_JOB, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_GROUPS, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_QUEUES, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_PRIORITIES, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_JOBS, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_MAX_TASKS, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_QUEUE_LIMIT, + MTAPI_ATTRIBUTE_VALUE(1), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + &node_attr, MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + + /* try to get an attribute with invalid attribute pointer */ + status = MTAPI_ERR_UNKNOWN; + mtapi_node_get_attribute(THIS_NODE_ID, + MTAPI_NODE_MAX_ACTIONS, MTAPI_NULL, 0, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + + /* create our test action */ + status = MTAPI_ERR_UNKNOWN; + action_hndl = mtapi_action_create(1, testErrorAction, MTAPI_NULL, 0, + MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_disable(action_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_enable(action_hndl, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_set_attribute(action_hndl, MTAPI_ACTION_GLOBAL, + MTAPI_ATTRIBUTE_VALUE(MTAPI_TRUE), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_get_attribute(action_hndl, + MTAPI_ACTION_GLOBAL, &buffer, MTAPI_ACTION_GLOBAL_SIZE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try to get an attribute of that action with invalid attribute pointer */ + status = MTAPI_ERR_UNKNOWN; + mtapi_action_get_attribute(action_hndl, + MTAPI_ACTION_GLOBAL, MTAPI_NULL, 0, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + /* try to get an attribute of that action with invalid attribute number */ + status = MTAPI_ERR_UNKNOWN; + mtapi_action_get_attribute(action_hndl, + INVALID_ATTRIBUTE, &buffer, 0, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + /* try to create another action, since the limit is one this will fail */ + status = MTAPI_ERR_UNKNOWN; + action_hndl_invalid = mtapi_action_create(1, testErrorAction, MTAPI_NULL, 0, + MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_LIMIT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_enable(action_hndl_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_disable(action_hndl_invalid, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action_hndl_invalid, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_set_attribute(action_hndl_invalid, MTAPI_ACTION_DOMAIN_SHARED, + MTAPI_ATTRIBUTE_VALUE(MTAPI_TRUE), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_get_attribute(action_hndl_invalid, MTAPI_ACTION_DOMAIN_SHARED, + MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_INVALID); + + + /* get handle for job number 1, associated with our action */ + status = MTAPI_ERR_UNKNOWN; + job_hndl = mtapi_job_get(1, THIS_DOMAIN_ID, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try to get the invalid job number 2, limit is 1 */ + status = MTAPI_ERR_UNKNOWN; + job_hndl_invalid = mtapi_job_get(2, THIS_DOMAIN_ID, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_JOB_INVALID); + + + /* start a task with the invalid job, this will fail */ + status = MTAPI_ERR_UNKNOWN; + mtapi_task_start(MTAPI_TASK_ID_NONE, job_hndl_invalid, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_JOB_INVALID); + + + /* test if executing a task works, so do not wait */ + embb_atomic_store_int(&wait, 0); + + status = MTAPI_ERR_UNKNOWN; + task_hndl = mtapi_task_start(MTAPI_TASK_ID_NONE, job_hndl, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* task should have executed, wait contains 2 */ + PT_EXPECT_EQ(embb_atomic_load_int(&wait), 2); + + + /* this time wait, so we can test the task limit */ + embb_atomic_store_int(&wait, 1); + + /* this task will wait until wait is set to 0 */ + status = MTAPI_ERR_UNKNOWN; + task_hndl = mtapi_task_start(MTAPI_TASK_ID_NONE, job_hndl, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* we cannot start another task since the limit is 1 */ + status = MTAPI_ERR_UNKNOWN; + task_hndl_invalid = mtapi_task_start(MTAPI_TASK_ID_NONE, job_hndl, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_TASK_LIMIT); + + /* let the waiting task do its work */ + embb_atomic_store_int(&wait, 0); + + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* task should have executed, wait contains 2 */ + PT_EXPECT_EQ(embb_atomic_load_int(&wait), 2); + + + /* create a group */ + status = MTAPI_ERR_UNKNOWN; + group_hndl = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try to create another group, this will fail since the limit is 1 */ + status = MTAPI_ERR_UNKNOWN; + group_hndl_invalid = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_GROUP_LIMIT); + + /* try to delete the invalid group, this will fail */ + status = MTAPI_ERR_UNKNOWN; + mtapi_group_delete(group_hndl_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_GROUP_INVALID); + + /* delete the valid group */ + status = MTAPI_ERR_UNKNOWN; + mtapi_group_delete(group_hndl, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try to get an attribute of the deleted group */ + status = MTAPI_ERR_UNKNOWN; + mtapi_group_get_attribute(group_hndl, 0, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_GROUP_INVALID); + + /* try to set an attribute of the deleted group */ + status = MTAPI_ERR_UNKNOWN; + mtapi_group_set_attribute(group_hndl, 0, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_GROUP_INVALID); + + + /* create a queue with invalid job */ + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_create(MTAPI_QUEUE_ID_NONE, job_hndl_invalid, + MTAPI_DEFAULT_QUEUE_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_JOB_INVALID); + + /* create a queue */ + status = MTAPI_ERR_UNKNOWN; + queue_hndl = mtapi_queue_create(MTAPI_QUEUE_ID_NONE, job_hndl, + MTAPI_DEFAULT_QUEUE_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_disable(queue_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_enable(queue_hndl, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try to create another queue, this will fail since the limit is 1 */ + status = MTAPI_ERR_UNKNOWN; + queue_hndl_invalid = mtapi_queue_create(MTAPI_QUEUE_ID_NONE, job_hndl, + MTAPI_DEFAULT_QUEUE_ATTRIBUTES, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_LIMIT); + + /* try to enable the invalid queue */ + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_enable(queue_hndl_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_INVALID); + + /* try to disable the invalid queue */ + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_disable(queue_hndl_invalid, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_INVALID); + + /* try to delete the invalid queue */ + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_delete(queue_hndl_invalid, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_set_attribute(queue_hndl_invalid, MTAPI_QUEUE_DOMAIN_SHARED, + MTAPI_ATTRIBUTE_VALUE(MTAPI_TRUE), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_get_attribute(queue_hndl_invalid, MTAPI_QUEUE_DOMAIN_SHARED, + MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_INVALID); + + + /* now test the task limit via queues, let the first one wait */ + embb_atomic_store_int(&wait, 1); + + /* enqueue the task */ + status = MTAPI_ERR_UNKNOWN; + task_hndl = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue_hndl, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* enqueue another one, this will fail since the limit is one */ + status = MTAPI_ERR_UNKNOWN; + mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue_hndl, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_TASK_LIMIT); + + /* let the valid task do its work */ + embb_atomic_store_int(&wait, 0); + + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* the task has completed, wait should contain 2 */ + PT_EXPECT_EQ(embb_atomic_load_int(&wait), 2); + + + /* disable our queue */ + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_disable(queue_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_set_attribute(queue_hndl, MTAPI_QUEUE_DOMAIN_SHARED, + MTAPI_ATTRIBUTE_VALUE(MTAPI_TRUE), MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_get_attribute(queue_hndl, MTAPI_QUEUE_DOMAIN_SHARED, + MTAPI_NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_get_attribute(queue_hndl, INVALID_ATTRIBUTE, + &buffer, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_get_attribute(queue_hndl, MTAPI_QUEUE_DOMAIN_SHARED, + &buffer, MTAPI_QUEUE_DOMAIN_SHARED_SIZE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try to enqueue another task, this will fail since the queue is disabled */ + status = MTAPI_ERR_UNKNOWN; + mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue_hndl, + MTAPI_NULL, 0, MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_QUEUE_DISABLED); + + /* disable our queue */ + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_delete(queue_hndl, MTAPI_INFINITE, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + + /* and we're done */ + mtapi_finalize(&status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} + +static void TestParameter() { + mtapi_status_t status; + mtapi_node_attributes_t node_attr; + mtapi_action_attributes_t action_attr; + mtapi_task_attributes_t task_attr; + mtapi_queue_attributes_t queue_attr; + mtapi_group_attributes_t group_attr; + mtapi_info_t info; + + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_set(&node_attr, INVALID_ATTRIBUTE, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + /* try second init */ + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, &info, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_NODE_INITIALIZED); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_init(MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_init(&action_attr, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_set(&action_attr, INVALID_ATTRIBUTE, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_taskattr_init(MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + status = MTAPI_ERR_UNKNOWN; + mtapi_taskattr_init(&task_attr, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_taskattr_set(&task_attr, INVALID_ATTRIBUTE, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_queueattr_init(MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queueattr_init(&queue_attr, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queueattr_set(&queue_attr, INVALID_ATTRIBUTE, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_groupattr_init(MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_PARAMETER); + + status = MTAPI_ERR_UNKNOWN; + mtapi_groupattr_init(&group_attr, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + status = MTAPI_ERR_UNKNOWN; + mtapi_groupattr_set(&group_attr, INVALID_ATTRIBUTE, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_ATTR_NUM); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} + +void TestContext() { + mtapi_status_t status; + mtapi_task_context_t* task_ctx_invalid = MTAPI_NULL; + + /* the following context is considered valid although it is not a real one, + but is checked against the stored pointers and will lead to + MTAPI_ERR_CONTEXT_OUTOFCONTEXT */ + embb_mtapi_thread_context_t thread_ctx_storage; + embb_tss_create(&thread_ctx_storage.tss_id); + embb_mtapi_task_context_t task_ctx_storage; + task_ctx_storage.thread_context = &thread_ctx_storage; + mtapi_task_context_t* task_ctx = &task_ctx_storage; + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, MTAPI_NULL, &status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_corenum_get(task_ctx_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_instnum_get(task_ctx_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_numinst_get(task_ctx_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_status_set(task_ctx_invalid, MTAPI_SUCCESS, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_runtime_notify(task_ctx_invalid, + MTAPI_NOTIF_EXECUTE_NEXT, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_INVALID); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_taskstate_get(task_ctx_invalid, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_INVALID); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_corenum_get(task_ctx, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_OUTOFCONTEXT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_instnum_get(task_ctx, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_OUTOFCONTEXT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_numinst_get(task_ctx, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_OUTOFCONTEXT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_status_set(task_ctx, MTAPI_SUCCESS, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_OUTOFCONTEXT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_runtime_notify(task_ctx, + MTAPI_NOTIF_EXECUTE_NEXT, NULL, 0, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_OUTOFCONTEXT); + + status = MTAPI_ERR_UNKNOWN; + mtapi_context_taskstate_get(task_ctx, &status); + PT_EXPECT_EQ(status, MTAPI_ERR_CONTEXT_OUTOFCONTEXT); + + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + + embb_tss_delete(&thread_ctx_storage.tss_id); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} + +void ErrorTest::TestBasic() { + TestNodeNotInit(); + TestParameter(); + TestLimits(); + TestContext(); +} diff --git b/mtapi_c/test/embb_mtapi_test_error.h a/mtapi_c/test/embb_mtapi_test_error.h new file mode 100644 index 0000000..3061bb9 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_error.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_ERROR_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_ERROR_H_ + +#include + +class ErrorTest : public partest::TestCase { + public: + ErrorTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_ERROR_H_ diff --git b/mtapi_c/test/embb_mtapi_test_group.cc a/mtapi_c/test/embb_mtapi_test_group.cc new file mode 100644 index 0000000..445f9be --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_group.cc @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 + +struct result_example_struct { + mtapi_uint_t value1; + mtapi_uint_t value2; +}; + +typedef struct result_example_struct result_example_t; + +static void testGroupAction( + const void* args, + mtapi_size_t /*arg_size*/, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context) { + result_example_t * result = + reinterpret_cast(result_buffer); + mtapi_uint_t workload_id = *reinterpret_cast(args); + int ii; + mtapi_uint_t core_num = mtapi_context_corenum_get(task_context, MTAPI_NULL); + srand(core_num); + for (ii = 10; ii < 10 + rand() % 100; ii++) { + embb_thread_yield(); + } + embb_mtapi_log_info( + "testGroupAction %d called from worker %d...\n", workload_id, core_num); + if (MTAPI_NULL != result_buffer && + sizeof(result_example_t) == result_buffer_size) { + result->value1 = workload_id; + result->value2 = core_num; + } +} + +static void testDoSomethingElse() { +} + +GroupTest::GroupTest() { + CreateUnit("mtapi group test").Add(&GroupTest::TestBasic, this, 1, 1000); +} + +void GroupTest::TestBasic() { + mtapi_info_t info; + mtapi_status_t status = MTAPI_ERR_UNKNOWN; + mtapi_action_hndl_t action; + mtapi_job_hndl_t job; + mtapi_task_attributes_t task_attributes; + mtapi_group_hndl_t group; +#define NUM_TASKS 10 + int argument[NUM_TASKS]; + const mtapi_size_t args_size = sizeof(int); + const mtapi_boolean_t att_val_true = MTAPI_TRUE; + const mtapi_size_t bool_size = sizeof(mtapi_boolean_t); + result_example_t results[NUM_TASKS]; + result_example_t* tmp_result; + int ii; + + embb_mtapi_log_info("running testGroup...\n"); + + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, &info, &status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_trace("mtapi successfully initialized...\n"); + embb_mtapi_log_trace( + "hardware concurrency : %d\n", info.hardware_concurrency); + embb_mtapi_log_trace("used memory : %d\n", info.used_memory); + + /* create action */ + action = mtapi_action_create(JOB_TEST_TASK, (testGroupAction), + MTAPI_NULL, 0, MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + MTAPI_CHECK_STATUS(status); + + /* get job */ + job = mtapi_job_get(JOB_TEST_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + /* set task attribute DETACHED because we are not interested in task + handles after the tasks have been started */ + mtapi_taskattr_init(&task_attributes, &status); + MTAPI_CHECK_STATUS(status); + mtapi_taskattr_set(&task_attributes, MTAPI_TASK_DETACHED, + reinterpret_cast(&att_val_true), bool_size, &status); + MTAPI_CHECK_STATUS(status); + + /* ---- mtapi_group_wait_all test ---- */ + + /* prepare group */ + group = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + + /* create several tasks using the same group (in this example we use the + same action for all of them, of course it is possible to use different + actions for the different tasks) */ + for (ii = 0; ii < NUM_TASKS; ii++) { + argument[ii] = ii; + /* start task */ + mtapi_task_start(MTAPI_TASK_ID_NONE, job, + reinterpret_cast(&argument[ii]), + args_size, MTAPI_NULL, 0, &task_attributes, group, &status); + MTAPI_CHECK_STATUS(status); + } + + /* do something else */ + testDoSomethingElse(); + + /* wait for completion of all tasks in the group */ + mtapi_group_wait_all(group, 10000, &status); + MTAPI_CHECK_STATUS(status); + + /* ---- mtapi_group_wait_any test ---- */ + + /* prepare group */ + group = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + + /* create several tasks using the same group (in this example we use the + same action for all of them, of course it is possible to use different + actions for the different tasks) */ + for (ii = 0; ii < NUM_TASKS; ii++) { + argument[ii] = ii; + /* start task */ + mtapi_task_start(MTAPI_TASK_ID_NONE, job, + reinterpret_cast(&argument[ii]), + args_size, &results[ii], sizeof(result_example_t), + &task_attributes, group, &status); + MTAPI_CHECK_STATUS(status); + } + + /* do something else */ + testDoSomethingElse(); + + /* wait for completion of tasks in the group and handle results */ + bool run = true; + while (run) { + mtapi_group_wait_any(group, reinterpret_cast(&tmp_result), + 10000, &status); + if (status == MTAPI_TIMEOUT) { + embb_mtapi_log_error("wait timed out\n"); + status = MTAPI_SUCCESS; + } + /* status will be MTAPI_ERR_RESULT_SIZE on result size mismatch */ + if (status != MTAPI_SUCCESS) { + /* MTAPI_GROUP_COMPLETED */ + run = false; + } else { + /* ... process 'tmp_result' here ... + temp_result contains the pointer that was passed at mtapi_task_start + to the task which just returned */ + embb_mtapi_log_trace( + "result.value1 = %i, result.value2 = %i\n", + tmp_result->value1, tmp_result->value2); + } + } + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + PT_EXPECT(embb_get_bytes_allocated() == 0); + + embb_mtapi_log_info("...done\n\n"); +} diff --git b/mtapi_c/test/embb_mtapi_test_group.h a/mtapi_c/test/embb_mtapi_test_group.h new file mode 100644 index 0000000..e6501d6 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_group.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_GROUP_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_GROUP_H_ + +#include + +class GroupTest : public partest::TestCase { + public: + GroupTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_GROUP_H_ diff --git b/mtapi_c/test/embb_mtapi_test_id_pool.cc a/mtapi_c/test/embb_mtapi_test_id_pool.cc new file mode 100644 index 0000000..0ba6e62 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_id_pool.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +IdPoolTest::IdPoolTest() { + CreateUnit("mtapi id pool test single threaded"). + Add(&IdPoolTest::TestBasic, this, 1, 1000). + Pre(&IdPoolTest::TestBasicPre, this). + Post(&IdPoolTest::TestBasicPost, this); + + CreateUnit("mtapi id pool test concurrent"). + Add(&IdPoolTest::TestParallel, this, concurrent_accessors_id_pool_2 + , 20). + Post(&IdPoolTest::TestParallelPost, this). + Pre(&IdPoolTest::TestParallelPre, this); +} + +void IdPoolTest::TestParallel() { + // allocate ID_ELEMENTS_PER_ACCESSOR elements. Each test thread is + // guaranteed to be able to allocate this amount of elements. + TestAllocateDeallocateNElementsFromPool(id_pool_parallel, + id_elements_per_accessor); +} + +void IdPoolTest::TestParallelPre() { + // create second id pool with CONCURRENT_ACCESSORS_ID_POOL_2* + // ID_ELEMENTS_PER_ACCESSOR elements + embb_mtapi_id_pool_initialize(&id_pool_parallel, + concurrent_accessors_id_pool_2*id_elements_per_accessor); +} + +void IdPoolTest::TestParallelPost() { + // after the parallel tests, try to again allocate and deallocate all + // elements sequentially. + TestAllocateDeallocateNElementsFromPool(id_pool_parallel, + concurrent_accessors_id_pool_2*id_elements_per_accessor, true); + + // finalize pool + embb_mtapi_id_pool_finalize(&id_pool_parallel); +} + +void IdPoolTest::TestBasic() { + TestAllocateDeallocateNElementsFromPool(id_pool, id_pool_size_1, true); +} + +void IdPoolTest::TestBasicPre() { + // create id pool with ID_POOL_SIZE_1 elements + embb_mtapi_id_pool_initialize(&id_pool, id_pool_size_1); +} + +void IdPoolTest::TestBasicPost() { + // finalize pool + embb_mtapi_id_pool_finalize(&id_pool); +} + +void IdPoolTest::TestAllocateDeallocateNElementsFromPool( + embb_mtapi_id_pool_t &pool, + int count_elements, + bool empty_check) { + std::vector allocated; + + for (int i = 0; i != count_elements; ++i) { + allocated.push_back(embb_mtapi_id_pool_allocate(&pool)); + } + + // the allocated elements should be disjunctive, and never invalid element + for (unsigned int x = 0; x != allocated.size(); ++x) { + PT_ASSERT(allocated[x] != EMBB_MTAPI_IDPOOL_INVALID_ID); + for (unsigned int y = 0; y != allocated.size(); ++y) { + if (x == y) { + continue; + } + PT_ASSERT(allocated[x] != allocated[y]); + } + } + + // now the id pool should be empty... try ten times to get an id, + // we should always get the invalid element + if (empty_check) { + for (int i = 0; i != 10; ++i) { + PT_ASSERT_EQ(embb_mtapi_id_pool_allocate(&pool), + static_cast(EMBB_MTAPI_IDPOOL_INVALID_ID) + ) + } + } + + // now return allocated elements in a shuffled manner. + ::std::random_shuffle(allocated.begin(), allocated.end()); + + for (int i = 0; i != count_elements; ++i) { + embb_mtapi_id_pool_deallocate(&pool, + allocated[static_cast(i)]); + } +} + diff --git b/mtapi_c/test/embb_mtapi_test_id_pool.h a/mtapi_c/test/embb_mtapi_test_id_pool.h new file mode 100644 index 0000000..626bb4f --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_id_pool.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_ + +#include +#include + +// for shuffling a vector +#include + +class IdPoolTest : public partest::TestCase { + public: + embb_mtapi_id_pool_t id_pool; + embb_mtapi_id_pool_t id_pool_parallel; + + IdPoolTest(); + + private: + static const unsigned int id_pool_size_1 = 100; + static const unsigned int concurrent_accessors_id_pool_2 = 10; + static const unsigned int id_elements_per_accessor = 10; + + /** + * We create a pool of size number_accessors*elements_per_accessor, so + * at each time we can guarantee each thread to be able to allocate + * elements_per_accessor elements. + * We create number_accessor threads, where each thread iteratively + * allocates and frees elements_per_accessor elements, which in each case + * has to be successful. Additionally, the sanity checks from the basic tests + * are repeated. The TestParallelPost function also repeats all + * sequential tests. + */ + void TestParallel(); + void TestParallelPre(); + void TestParallelPost(); + + /** + * Create a pool of size N. We repeatedly allocate and free N elements, check + * if the pool always returns disjunctive ids and check that the pool never + * returns the invalid element, if the pool is not empty. Check that the + * invalid element is returned if the pool is empty. + */ + void TestBasic(); + void TestBasicPre(); + void TestBasicPost(); + + static void TestAllocateDeallocateNElementsFromPool( + embb_mtapi_id_pool_t &pool, + int count_elements, + bool empty_check = false); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_ diff --git b/mtapi_c/test/embb_mtapi_test_init_finalize.cc a/mtapi_c/test/embb_mtapi_test_init_finalize.cc new file mode 100644 index 0000000..226919f --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_init_finalize.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +InitFinalizeTest::InitFinalizeTest() { + CreateUnit("mtapi init/finalize test"). + Add(&InitFinalizeTest::TestBasic, this); +} + +void InitFinalizeTest::TestBasic() { + embb_mtapi_log_info("running testInitFinalize...\n"); + + mtapi_node_attributes_t node_attr; + mtapi_info_t info; + mtapi_status_t status; + + for (int ii = 0; ii < 100; ii++) { + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + } + + PT_EXPECT(embb_get_bytes_allocated() == 0); + + embb_mtapi_log_info("...done\n\n"); +} diff --git b/mtapi_c/test/embb_mtapi_test_init_finalize.h a/mtapi_c/test/embb_mtapi_test_init_finalize.h new file mode 100644 index 0000000..974237c --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_init_finalize.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_INIT_FINALIZE_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_INIT_FINALIZE_H_ + +#include + +class InitFinalizeTest : public partest::TestCase { + public: + InitFinalizeTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_INIT_FINALIZE_H_ diff --git b/mtapi_c/test/embb_mtapi_test_plugin.cc a/mtapi_c/test/embb_mtapi_test_plugin.cc new file mode 100644 index 0000000..2a13fee --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_plugin.cc @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define PLUGIN_JOB_ID 2 + +embb_thread_t plugin_thread; +embb_atomic_int plugin_running; +mtapi_task_hndl_t plugin_task; +embb_atomic_int plugin_task_available; + +int plugin_thread_function(void * args) { + EMBB_UNUSED(args); + while (embb_atomic_load_int(&plugin_running)) { + /* wait for incoming task */ + while (embb_atomic_load_int(&plugin_running) && + !embb_atomic_load_int(&plugin_task_available)) + embb_thread_yield(); + + if (embb_atomic_load_int(&plugin_running)) { + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid( + node->task_pool, plugin_task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle( + node->task_pool, plugin_task); + embb_mtapi_task_set_state(local_task, MTAPI_TASK_COMPLETED); + } + } + embb_atomic_store_int(&plugin_task_available, 0); + } + } + + return 0; +} + +void plugin_initialize( + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_IN mtapi_node_t node_id, + MTAPI_OUT mtapi_status_t* status + ) { + EMBB_UNUSED(domain_id); + EMBB_UNUSED(node_id); + + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + int err; + + plugin_task.id = 0; + plugin_task.tag = 0; + embb_atomic_store_int(&plugin_running, 1); + embb_atomic_store_int(&plugin_task_available, 0); + + err = embb_thread_create(&plugin_thread, NULL, plugin_thread_function, NULL); + if (EMBB_SUCCESS == err) { + local_status = MTAPI_SUCCESS; + } + + mtapi_status_set(status, local_status); +} + +void plugin_finalize( + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + int result = 0; + int err; + + embb_atomic_store_int(&plugin_running, 0); + err = embb_thread_join(&plugin_thread, &result); + if (EMBB_SUCCESS == err) { + local_status = MTAPI_SUCCESS; + } + + mtapi_status_set(status, local_status); +} + +void plugin_task_start( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + embb_mtapi_task_set_state(local_task, MTAPI_TASK_RUNNING); + + plugin_task = task; + embb_atomic_store_int(&plugin_task_available, 1); + } + } + + local_status = MTAPI_SUCCESS; + + mtapi_status_set(status, local_status); +} + +void plugin_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status + ) { + EMBB_UNUSED(task); + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + mtapi_status_set(status, local_status); +} + +void plugin_action_finalize( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status + ) { + EMBB_UNUSED(action); + mtapi_status_set(status, MTAPI_SUCCESS); +} + +PluginTest::PluginTest() { + CreateUnit("mtapi plugin test"). + Add(&PluginTest::TestBasic, this); +} + +void PluginTest::TestBasic() { + embb_mtapi_log_info("running plugin test...\n"); + + mtapi_node_attributes_t node_attr; + mtapi_info_t info; + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_action_hndl_t action; + mtapi_task_hndl_t task; + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + plugin_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + action = mtapi_ext_plugin_action_create( + PLUGIN_JOB_ID, + plugin_task_start, + plugin_task_cancel, + plugin_action_finalize, + MTAPI_NULL, + MTAPI_NULL, + 0, + MTAPI_DEFAULT_ACTION_ATTRIBUTES, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get( + PLUGIN_JOB_ID, + THIS_DOMAIN_ID, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + MTAPI_NULL, 0, + MTAPI_NULL, 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + plugin_finalize(&status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); + + embb_mtapi_log_info("...done\n\n"); +} diff --git b/mtapi_c/test/embb_mtapi_test_plugin.h a/mtapi_c/test/embb_mtapi_test_plugin.h new file mode 100644 index 0000000..57c119a --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_plugin.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_PLUGIN_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_PLUGIN_H_ + +#include + +class PluginTest : public partest::TestCase { + public: + PluginTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_PLUGIN_H_ diff --git b/mtapi_c/test/embb_mtapi_test_queue.cc a/mtapi_c/test/embb_mtapi_test_queue.cc new file mode 100644 index 0000000..4de48f9 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_queue.cc @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 +#define QUEUE_TEST_ID 17 + +static void testQueueAction( + const void* args, + mtapi_size_t /*arg_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context) { + int workload_id = *reinterpret_cast(args); + int ii; + mtapi_uint_t core_num = mtapi_context_corenum_get(task_context, MTAPI_NULL); + srand(core_num); + for (ii = 1000; ii < rand()%1000000; ii ++) { + } + embb_mtapi_log_info("testQueueAction %d called from worker %d...\n", + workload_id, core_num); + EMBB_UNUSED(workload_id); +} + +static void testDoSomethingElse() { +} + +QueueTest::QueueTest() { + CreateUnit("mtapi queue test").Add(&QueueTest::TestBasic, this); +} + +void QueueTest::TestBasic() { + mtapi_info_t info; + mtapi_status_t status; + mtapi_action_hndl_t action; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_queue_hndl_t queue; + const mtapi_size_t args_size = sizeof(int); + const int args = 42; + + embb_mtapi_log_info("running testQueue...\n"); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, &info, &status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_trace("mtapi successfully initialized...\n"); + embb_mtapi_log_trace( + "hardware concurrency : %d\n", info.hardware_concurrency); + embb_mtapi_log_trace("used memory : %d\n", info.used_memory); + + /* create action */ + status = MTAPI_ERR_UNKNOWN; + action = mtapi_action_create(JOB_TEST_TASK, (testQueueAction), + MTAPI_NULL, 0, MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + MTAPI_CHECK_STATUS(status); + + /* get job */ + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(JOB_TEST_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + /* create queue */ + status = MTAPI_ERR_UNKNOWN; + queue = mtapi_queue_create(QUEUE_TEST_ID, job, + MTAPI_DEFAULT_QUEUE_ATTRIBUTES, &status); + MTAPI_CHECK_STATUS(status); + + /* enqueue task in queue (repeat this for all tasks that should be + processed by that queue) */ + status = MTAPI_ERR_UNKNOWN; + task = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue, + reinterpret_cast(&args), + args_size, MTAPI_NULL, 0, MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, &status); + MTAPI_CHECK_STATUS(status); + + /* do something else */ + testDoSomethingElse(); + + /* wait for task completion */ + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_delete(queue, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + PT_EXPECT(embb_get_bytes_allocated() == 0); + + embb_mtapi_log_info("...done\n\n"); +} diff --git b/mtapi_c/test/embb_mtapi_test_queue.h a/mtapi_c/test/embb_mtapi_test_queue.h new file mode 100644 index 0000000..91aea99 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_queue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_QUEUE_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_QUEUE_H_ + +#include + +class QueueTest : public partest::TestCase { + public: + QueueTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_QUEUE_H_ diff --git b/mtapi_c/test/embb_mtapi_test_task.cc a/mtapi_c/test/embb_mtapi_test_task.cc new file mode 100644 index 0000000..9431862 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_task.cc @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#define JOB_TEST_TASK 42 +#define JOB_TEST_MULTIINSTANCE_TASK 43 +#define TASK_TEST_ID 23 + +static void testTaskAction( + const void* args, + mtapi_size_t /*arg_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context) { + int ii; + mtapi_status_t status; + mtapi_uint_t core_num = mtapi_context_corenum_get(task_context, &status); + MTAPI_CHECK_STATUS(status); + srand(core_num); + for (ii = 1000; ii < rand()%1000000; ii ++) { + } + embb_mtapi_log_info("testTaskAction %d called from worker %d...\n", + *reinterpret_cast(args), core_num); + EMBB_UNUSED(args); +} + +void testMultiInstanceTaskAction( + const void* args, + mtapi_size_t arg_size, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* node_local_data, + mtapi_size_t node_local_data_size, + mtapi_task_context_t* task_context) { + EMBB_UNUSED(args); + EMBB_UNUSED(arg_size); + + EMBB_UNUSED(node_local_data); + EMBB_UNUSED(node_local_data_size); + + mtapi_status_t status; + mtapi_uint_t this_instance, num_instances; + mtapi_uint_t* result; + num_instances = mtapi_context_numinst_get(task_context, &status); + this_instance = mtapi_context_instnum_get(task_context, &status); + + /* check result buffer size... */ + if (result_buffer_size == sizeof(int) * num_instances) { + /* ... and cast the result buffer */ + result = reinterpret_cast(result_buffer); + } else { + mtapi_context_status_set(task_context, MTAPI_ERR_RESULT_SIZE, &status); + MTAPI_CHECK_STATUS(status); + return; + } + + /* dummy for calculating result */ + result[this_instance] = this_instance; +} + + +static void testDoSomethingElse() { +} + +TaskTest::TaskTest() { + CreateUnit("mtapi task test").Add(&TaskTest::TestBasic, this); +} + +void TaskTest::TestBasic() { + mtapi_node_attributes_t node_attr; + mtapi_action_attributes_t action_attr; + mtapi_affinity_t affinity; + mtapi_info_t info; + mtapi_status_t status; + mtapi_action_hndl_t action; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task[100]; + mtapi_uint_t ii; + + embb_mtapi_log_info("running testTask...\n"); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_set( + &node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_trace("mtapi successfully initialized...\n"); + embb_mtapi_log_trace( + "hardware concurrency : %d\n", info.hardware_concurrency); + embb_mtapi_log_trace("used memory : %d\n", info.used_memory); + + status = MTAPI_ERR_UNKNOWN; + mtapi_affinity_init(&affinity, MTAPI_TRUE, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_init(&action_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_set( + &action_attr, + MTAPI_ACTION_AFFINITY, + &affinity, + MTAPI_ACTION_AFFINITY_SIZE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + action = mtapi_action_create( + JOB_TEST_TASK, + testTaskAction, + MTAPI_NULL, + 0, + &action_attr, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(JOB_TEST_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + for (ii = 0; ii < 100u; ii++) { + status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t arg = ii; + task[ii] = mtapi_task_start( + TASK_TEST_ID, + job, + reinterpret_cast(&arg), + 0, + MTAPI_NULL, + 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + } + + testDoSomethingElse(); + + for (ii = 0; ii < 100u; ii++) { + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task[ii], 100000, &status); + MTAPI_CHECK_STATUS(status); + } + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_hndl_t multiinstance_action = mtapi_action_create( + JOB_TEST_MULTIINSTANCE_TASK, + testMultiInstanceTaskAction, + MTAPI_NULL, + 0, + &action_attr, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_job_hndl_t multiinstance_job = mtapi_job_get( + JOB_TEST_MULTIINSTANCE_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_attributes_t task_attr; + + status = MTAPI_ERR_UNKNOWN; + mtapi_taskattr_init(&task_attr, &status); + MTAPI_CHECK_STATUS(status); + + const mtapi_uint_t kTaskInstances = 5; + + status = MTAPI_ERR_UNKNOWN; + mtapi_taskattr_set(&task_attr, MTAPI_TASK_INSTANCES, + &kTaskInstances, sizeof(mtapi_uint_t), + &status); + MTAPI_CHECK_STATUS(status); + + mtapi_uint_t result[kTaskInstances]; + for (ii = 0; ii < kTaskInstances; ii++) { + result[ii] = kTaskInstances + 1; + } + + status = MTAPI_ERR_UNKNOWN; + mtapi_task_hndl_t multiinstance_task = + mtapi_task_start(MTAPI_TASK_ID_NONE, multiinstance_job, + MTAPI_NULL, 0, + &result[0], sizeof(mtapi_uint_t) * kTaskInstances, + &task_attr, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(multiinstance_task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + for (ii = 0; ii < kTaskInstances; ii++) { + PT_EXPECT_EQ(result[ii], ii); + } + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(multiinstance_action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); + + embb_mtapi_log_info("...done\n\n"); +} diff --git b/mtapi_c/test/embb_mtapi_test_task.h a/mtapi_c/test/embb_mtapi_test_task.h new file mode 100644 index 0000000..bc379e7 --- /dev/null +++ a/mtapi_c/test/embb_mtapi_test_task.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_TASK_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_TASK_H_ + +#include + +class TaskTest : public partest::TestCase { + public: + TaskTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_TASK_H_ diff --git b/mtapi_c/test/main.cc a/mtapi_c/test/main.cc new file mode 100644 index 0000000..6fb351e --- /dev/null +++ a/mtapi_c/test/main.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +PT_MAIN("MTAPI C") { + embb_log_set_log_level(EMBB_LOG_LEVEL_NONE); + embb_thread_set_max_count(1024); + + PT_RUN(TaskTest); + PT_RUN(PluginTest); + PT_RUN(ErrorTest); + PT_RUN(InitFinalizeTest); + PT_RUN(GroupTest); + PT_RUN(QueueTest); + PT_RUN(IdPoolTest); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/mtapi_cpp/CMakeLists.txt a/mtapi_cpp/CMakeLists.txt new file mode 100644 index 0000000..3a343f2 --- /dev/null +++ a/mtapi_cpp/CMakeLists.txt @@ -0,0 +1,40 @@ +project (project_mtapi_cpp) + +file(GLOB_RECURSE EMBB_MTAPI_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_MTAPI_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_MTAPI_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) + set(MTAPI_CPP_AUTOMATIC_INITIALIZE 1) +else() + set(MTAPI_CPP_AUTOMATIC_INITIALIZE 0) +endif() + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_MTAPI_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_MTAPI_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include) + +add_library (embb_mtapi_cpp ${EMBB_MTAPI_CPP_SOURCES} ${EMBB_MTAPI_CPP_HEADERS}) +target_link_libraries(embb_mtapi_cpp embb_mtapi_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_mtapi_cpp_test ${EMBB_MTAPI_CPP_TEST_SOURCES}) + target_link_libraries(embb_mtapi_cpp_test embb_mtapi_cpp embb_mtapi_c partest + embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_mtapi_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_mtapi_cpp DESTINATION lib) diff --git b/mtapi_cpp/include/embb/mtapi/action.h a/mtapi_cpp/include/embb/mtapi/action.h new file mode 100644 index 0000000..564ab49 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/action.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_ACTION_H_ +#define EMBB_MTAPI_ACTION_H_ + +#include +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Holds the actual worker function used to execute a Task. + * + * \ingroup CPP_MTAPI + */ +class Action { + public: + /** + * Constructs an Action. + */ + Action( + mtapi_job_id_t job_id, /**< Job ID the Action belongs to */ + mtapi_action_function_t func, /**< The action function */ + const void * node_local_data, /**< Node local data available to all + Tasks using this Action */ + mtapi_size_t node_local_data_size, /**< Size of node local data */ + ActionAttributes const & attributes + /**< Attributes of the Action */ + ) { + Create(job_id, func, node_local_data, node_local_data_size, + &attributes.GetInternal()); + } + + /** + * Constructs an Action. + */ + Action( + mtapi_job_id_t job_id, /**< Job ID the Action belongs to */ + mtapi_action_function_t func, /**< The action function */ + const void * node_local_data, /**< Node local data available to all + Tasks using this Action */ + mtapi_size_t node_local_data_size /**< Size of node local data */ + ) { + Create(job_id, func, node_local_data, node_local_data_size, + MTAPI_DEFAULT_ACTION_ATTRIBUTES); + } + + /** + * Constructs an Action. + */ + Action( + mtapi_job_id_t job_id, /**< Job ID the Action belongs to */ + mtapi_action_function_t func, /**< The action function */ + ActionAttributes const & attributes + /**< Attributes of the Action */ + ) { + Create(job_id, func, MTAPI_NULL, 0, &attributes.GetInternal()); + } + + /** + * Constructs an Action. + */ + Action( + mtapi_job_id_t job_id, /**< Job ID the Action belongs to */ + mtapi_action_function_t func /**< The action function */ + ) { + Create(job_id, func, MTAPI_NULL, 0, MTAPI_DEFAULT_ACTION_ATTRIBUTES); + } + + /** + * Destroys an Action. + */ + ~Action() { + mtapi_action_delete(handle_, MTAPI_INFINITE, MTAPI_NULL); + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns The internal mtapi_action_hndl_t. + * \waitfree + */ + mtapi_action_hndl_t GetInternal() const { + return handle_; + } + + private: + // no default constructor + Action(); + + // not copyable + Action(Action const & other); + void operator=(Action const & other); + + void Create( + mtapi_job_id_t job_id, + mtapi_action_function_t func, + const void * node_local_data, + mtapi_size_t node_local_data_size, + mtapi_action_attributes_t const * attributes + ) { + mtapi_status_t status; + handle_ = mtapi_action_create(job_id, func, + node_local_data, node_local_data_size, + attributes, &status); + internal::CheckStatus(status); + } + + mtapi_action_hndl_t handle_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_ACTION_H_ diff --git b/mtapi_cpp/include/embb/mtapi/action_attributes.h a/mtapi_cpp/include/embb/mtapi/action_attributes.h new file mode 100644 index 0000000..f4bbf4b --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/action_attributes.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_ACTION_ATTRIBUTES_H_ +#define EMBB_MTAPI_ACTION_ATTRIBUTES_H_ + +#include +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Contains attributes of an Action. + * + * \ingroup CPP_MTAPI + */ +class ActionAttributes { + public: + /** + * Constructs an ActionAttributes object. + */ + ActionAttributes() { + mtapi_status_t status; + mtapi_actionattr_init(&attributes_, &status); + internal::CheckStatus(status); + } + + /** + * Sets the global property of an Action. + * This determines whether the object will be visible across nodes. + * + * \returns Reference to this object. + */ + ActionAttributes & SetGlobal( + bool state /**< The state to set */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_actionattr_set(&attributes_, MTAPI_ACTION_GLOBAL, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the affinity of an Action. + * + * \returns Reference to this object. + */ + ActionAttributes & SetAffinity( + Affinity const & affinity /**< The Affinity to set. */ + ) { + mtapi_status_t status; + mtapi_affinity_t af = affinity.GetInternal(); + mtapi_actionattr_set(&attributes_, MTAPI_ACTION_AFFINITY, + &af, sizeof(af), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the domain shared property of an Action. + * This determines whether the object will be visible across domains. + * + * \returns Reference to this object. + */ + ActionAttributes & SetDomainShared( + bool state /**< The state to set */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_actionattr_set(&attributes_, MTAPI_ACTION_DOMAIN_SHARED, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns A reference to the internal mtapi_action_attributes_t structure. + * \waitfree + */ + mtapi_action_attributes_t const & GetInternal() const { + return attributes_; + } + + private: + mtapi_action_attributes_t attributes_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_ACTION_ATTRIBUTES_H_ diff --git b/mtapi_cpp/include/embb/mtapi/affinity.h a/mtapi_cpp/include/embb/mtapi/affinity.h new file mode 100644 index 0000000..1496d48 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/affinity.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_AFFINITY_H_ +#define EMBB_MTAPI_AFFINITY_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Describes the affinity of an Action or Task to a worker thread of a Node. + * + * \ingroup CPP_MTAPI + */ +class Affinity { + public: + /** + * Constructs an Affinity object. + */ + Affinity() { + Init(true); + } + + /** + * Copies an Affinity object. + */ + Affinity( + Affinity const & other /**< The Affinity to copy from */ + ) + : affinity_(other.affinity_) { + // empty + } + + /** + * Copies an Affinity object. + */ + void operator=( + Affinity const & other /**< The Affinity to copy from */ + ) { + affinity_ = other.affinity_; + } + + /** + * Constructs an Affinity object with the given initial affinity. + * If \c initial_affinity is \c true the Affinity will map to all worker + * threads, otherwise it will map to no worker threads. + */ + Affinity( + bool initial_affinity /**< The initial affinity to set. */ + ) { + Init(initial_affinity); + } + + /** + * Initializes an Affinity object with the given initial affinity. + * If \c initial_affinity is \c true the Affinity will map to all worker + * threads, otherwise it will map to no worker threads. + * + * \notthreadsafe + */ + void Init( + bool initial_affinity /**< The initial affinity to set. */ + ) { + mtapi_status_t status; + mtapi_boolean_t ia = initial_affinity ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_affinity_init(&affinity_, ia, &status); + internal::CheckStatus(status); + } + + /** + * Sets affinity to the given worker. + * + * \notthreadsafe + */ + void Set( + mtapi_uint_t worker, /**< The worker to set affinity to. */ + bool state /**< The state of the affinity. */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_affinity_set(&affinity_, worker, st, &status); + internal::CheckStatus(status); + } + + /** + * Gets affinity to the given worker. + * + * \returns \c true, if the Affinity maps to the worker, \c false otherwise. + * \waitfree + */ + bool Get( + mtapi_uint_t worker /**< The worker to get affinity of. */ + ) { + mtapi_status_t status; + mtapi_boolean_t state = + mtapi_affinity_get(&affinity_, worker, &status); + internal::CheckStatus(status); + return (state != MTAPI_FALSE) ? true : false; + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns The internal mtapi_affinity_t. + * \waitfree + */ + mtapi_affinity_t GetInternal() const { + return affinity_; + } + + private: + mtapi_affinity_t affinity_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_AFFINITY_H_ diff --git b/mtapi_cpp/include/embb/mtapi/group.h a/mtapi_cpp/include/embb/mtapi/group.h new file mode 100644 index 0000000..6d47598 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/group.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_GROUP_H_ +#define EMBB_MTAPI_GROUP_H_ + +#include +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace mtapi { + +/** + * Represents a facility to wait for multiple related + * \link Task Tasks\endlink. + * + * \ingroup CPP_MTAPI + */ +class Group { + public: + /** + * Constructs a Group object with default attributes. + * Requires an initialized Node. + */ + Group() { + Create(MTAPI_GROUP_ID_NONE, MTAPI_DEFAULT_GROUP_ATTRIBUTES); + } + + /** + * Constructs a Group object using the given Attributes. + * Requires an initialized Node. + */ + Group( + GroupAttributes const & attr /**< The GroupAttributes to use. */ + ) { + Create(MTAPI_GROUP_ID_NONE, &attr.GetInternal()); + } + + /** + * Constructs a Group object with default attributes and the given ID. + * Requires an initialized Node. + */ + Group( + mtapi_group_id_t id /**< A user defined ID of the Group. */ + ) { + Create(id, MTAPI_DEFAULT_GROUP_ATTRIBUTES); + } + + /** + * Constructs a Group object with given attributes and ID. + * Requires an initialized Node. + */ + Group( + mtapi_group_id_t id, /**< A user defined ID of the Group. */ + GroupAttributes const & attr /**< The GroupAttributes to use. */ + ) { + Create(id, &attr.GetInternal()); + } + + /** + * Destroys a Group object. + */ + ~Group() { + // delete the group, ignore status + mtapi_group_delete(handle_, MTAPI_NULL); + } + + /** + * Starts a new Task in this Group. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes /**< Attributes of the Task */ + ) { + return Start(task_id, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal()); + } + + /** + * Starts a new Task in this Group. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results /**< Pointer to the results. */ + ) { + return Start(task_id, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES); + } + + /** + * Starts a new Task in this Group. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes /**< Attributes of the Task */ + ) { + return Start(MTAPI_TASK_ID_NONE, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal()); + } + + /** + * Starts a new Task in this Group. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results /**< Pointer to the results. */ + ) { + return Start(MTAPI_TASK_ID_NONE, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES); + } + + /** + * Waits for any Task in the Group to finish for \c timeout milliseconds and + * retrieves the result buffer given in Start(). + * \return The status of the Task that finished execution, \c MTAPI_TIMEOUT + * or \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t WaitAny( + mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + void ** result /**< [out] The result buffer given in + Node::Start, Group::Start or + Queue::Enqueue */ + ) { + mtapi_status_t status; + mtapi_group_wait_any(handle_, result, timeout, &status); + needs_delete_ = status != MTAPI_GROUP_COMPLETED; + return status; + } + + /** + * Waits for any Task in the Group to finish and + * retrieves the result buffer given in Start(). + * \return The status of the Task that finished execution or \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t WaitAny( + void ** result /**< [out] The result buffer given in + Node::Start, Group::Start or + Queue::Enqueue */ + ) { + return WaitAny(MTAPI_INFINITE, result); + } + + /** + * Waits for any Task in the Group to finish for \c timeout milliseconds. + * \return The status of the Task that finished execution + * \threadsafe + */ + mtapi_status_t WaitAny( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ) { + return WaitAny(timeout, MTAPI_NULL); + } + + /** + * Waits for any Task in the Group to finish. + * \return The status of the Task that finished execution + * \threadsafe + */ + mtapi_status_t WaitAny() { + return WaitAny(MTAPI_INFINITE, MTAPI_NULL); + } + + /** + * Waits for all Task in the Group to finish for \c timeout milliseconds. + * \return \c MTAPI_SUCCESS, \c MTAPI_TIMEOUT, \c MTAPI_ERR_* or the status + * of any failed Task + * \threadsafe + */ + mtapi_status_t WaitAll( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ) { + mtapi_status_t status; + mtapi_group_wait_all(handle_, timeout, &status); + needs_delete_ = status != MTAPI_SUCCESS; + return status; + } + + /** + * Waits for all Task in the Group to finish. + * \return \c MTAPI_SUCCESS, \c MTAPI_TIMEOUT, \c MTAPI_ERR_* or the status + * of any failed Task + * \threadsafe + */ + mtapi_status_t WaitAll() { + return WaitAll(MTAPI_INFINITE); + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns The internal mtapi_group_hndl_t. + * \waitfree + */ + mtapi_group_hndl_t GetInternal() const { + return handle_; + } + + friend class embb::base::Allocation; + + private: + // not copyable + Group(Group const & other); + void operator=(Group const & other); + + void Create( + mtapi_group_id_t group_id, + mtapi_group_attributes_t const * attributes + ) { + needs_delete_ = false; + mtapi_status_t status; + handle_ = mtapi_group_create(group_id, attributes, &status); + internal::CheckStatus(status); + needs_delete_ = true; + } + + Task Start( + mtapi_task_id_t task_id, + mtapi_job_hndl_t job, + const void * arguments, + mtapi_size_t arguments_size, + void * results, + mtapi_size_t results_size, + mtapi_task_attributes_t const * attributes + ) { + mtapi_status_t status; + mtapi_task_hndl_t task_hndl = + mtapi_task_start(task_id, job, arguments, arguments_size, + results, results_size, attributes, handle_, + &status); + internal::CheckStatus(status); + return Task(task_hndl); + } + + mtapi_group_hndl_t handle_; + bool needs_delete_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_GROUP_H_ diff --git b/mtapi_cpp/include/embb/mtapi/group_attributes.h a/mtapi_cpp/include/embb/mtapi/group_attributes.h new file mode 100644 index 0000000..e77d661 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/group_attributes.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_GROUP_ATTRIBUTES_H_ +#define EMBB_MTAPI_GROUP_ATTRIBUTES_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Contains attributes of a Group. + * + * \ingroup CPP_MTAPI + */ +class GroupAttributes { + public: + /** + * Constructs a GroupAttributes object. + */ + GroupAttributes() { + mtapi_status_t status; + mtapi_groupattr_init(&attributes_, &status); + internal::CheckStatus(status); + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns A reference to the internal mtapi_group_attributes_t structure. + * \waitfree + */ + mtapi_group_attributes_t const & GetInternal() const { + return attributes_; + } + + private: + mtapi_group_attributes_t attributes_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_GROUP_ATTRIBUTES_H_ diff --git b/mtapi_cpp/include/embb/mtapi/internal/check_status.h a/mtapi_cpp/include/embb/mtapi/internal/check_status.h new file mode 100644 index 0000000..3a7b47a --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/internal/check_status.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_INTERNAL_CHECK_STATUS_H_ +#define EMBB_MTAPI_INTERNAL_CHECK_STATUS_H_ + +#include + +namespace embb { +namespace mtapi { +namespace internal { + +void CheckStatus(mtapi_status_t status); + +template +inline mtapi_size_t SizeOfType() { + return sizeof(T); +} + +template <> +inline mtapi_size_t SizeOfType() { + return 0u; +} + +} // namespace internal +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_INTERNAL_CHECK_STATUS_H_ diff --git b/mtapi_cpp/include/embb/mtapi/job.h a/mtapi_cpp/include/embb/mtapi/job.h new file mode 100644 index 0000000..7b4c0f9 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/job.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_JOB_H_ +#define EMBB_MTAPI_JOB_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Represents a collection of Actions. + * + * \ingroup CPP_MTAPI + */ +class Job { + public: + /** + * Constructs a Job. + * The Job object will be invalid. + */ + Job() { + handle_.id = 0; + handle_.tag = 0; + } + + /** + * Constructs a Job with the given \c job_id and \c domain_id. + * Requires an initialized Node. + */ + Job( + mtapi_job_id_t job_id, /**< Job ID to use. */ + mtapi_domain_t domain_id /**< Domain ID to use. */ + ) { + mtapi_status_t status; + handle_ = mtapi_job_get(job_id, domain_id, &status); + internal::CheckStatus(status); + } + + /** + * Copies a Job object. + */ + Job( + Job const & other /**< The Job to copy from */ + ) : handle_(other.handle_) { + // empty + } + + /** + * Copies a Job object. + */ + void operator=( + Job const & other /**< The Job to copy from */ + ) { + handle_ = other.handle_; + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns The internal mtapi_job_hndl_t. + * \waitfree + */ + mtapi_job_hndl_t GetInternal() const { + return handle_; + } + + private: + mtapi_job_hndl_t handle_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_JOB_H_ diff --git b/mtapi_cpp/include/embb/mtapi/mtapi.h a/mtapi_cpp/include/embb/mtapi/mtapi.h new file mode 100644 index 0000000..35e6d26 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/mtapi.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_MTAPI_H_ +#define EMBB_MTAPI_MTAPI_H_ + +/** + * \defgroup CPP_MTAPI MTAPI + * C++ wrapper around C implementation of MTAPI. + * For a description of the basic concepts, see the + * \ref C_MTAPI "C implementation of MTAPI". + * \ingroup CPP + */ + +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_MTAPI_MTAPI_H_ diff --git b/mtapi_cpp/include/embb/mtapi/node.h a/mtapi_cpp/include/embb/mtapi/node.h new file mode 100644 index 0000000..115e48c --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/node.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_NODE_H_ +#define EMBB_MTAPI_NODE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace mtapi { + +/** + * A singleton representing the MTAPI runtime. + * + * \ingroup CPP_MTAPI + */ +class Node { + public: + /** + * Initializes the runtime singleton using default values: + * - all available cores will be used + * - maximum number of tasks is 1024 + * - maximum number of groups is 128 + * - maximum number of queues is 16 + * - maximum queue capacity is 1024 + * - maximum number of priorities is 4. + * + * \notthreadsafe + * \throws ErrorException if the singleton was already initialized or the + * Node could not be initialized. + * \memory Allocates about 200kb of memory. + */ + static void Initialize( + mtapi_domain_t domain_id, /**< [in] The domain id to use */ + mtapi_node_t node_id /**< [in] The node id to use */ + ) { + if (IsInitialized()) { + EMBB_THROW(StatusException, + "MTAPI: node was already initialized."); + } else { + NodeAttributes attributes; // default attributes + node_instance_ = embb::base::Allocation::New( + domain_id, node_id, attributes); + } + } + + /** + * Initializes the runtime singleton. + * \notthreadsafe + * \throws ErrorException if the singleton was already initialized or the + * Node could not be initialized. + * \memory Allocates some memory depending on the values given. + */ + static void Initialize( + mtapi_domain_t domain_id, /**< [in] The domain id to use */ + mtapi_node_t node_id, /**< [in] The node id to use */ + NodeAttributes const & attributes /**< [in] Attributes to use */ + ) { + if (IsInitialized()) { + EMBB_THROW(StatusException, + "MTAPI: node was already initialized."); + } else { + node_instance_ = embb::base::Allocation::New( + domain_id, node_id, attributes); + } + } + + /** + * Checks if runtime is initialized. + * \return \c true if the Node singleton is already initialized, false + * otherwise + * \waitfree + */ + static bool IsInitialized() { + return NULL != node_instance_; + } + + /** + * Gets the instance of the runtime system. + * \return Reference to the Node singleton + * \threadsafe + */ + static Node & GetInstance() { + if (IsInitialized()) { + return *node_instance_; + } else { + EMBB_THROW(StatusException, + "MTAPI: node is not initialized."); + } + } + + /** + * Shuts the runtime system down. + * \throws ErrorException if the singleton is not initialized. + * \notthreadsafe + */ + static void Finalize() { + if (IsInitialized()) { + embb::base::Allocation::Delete(node_instance_); + node_instance_ = NULL; + } else { + EMBB_THROW(StatusException, + "MTAPI: node is not initialized."); + } + } + + /** + * Returns the number of available cores. + * \return The number of available cores + * \waitfree + */ + mtapi_uint_t GetCoreCount() const { + return core_count_; + } + + /** + * Returns the number of worker threads. + * \return The number of worker threads. + * \waitfree + */ + mtapi_uint_t GetWorkerThreadCount() const { + return worker_thread_count_; + } + + /** + * Starts a new Task. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes /**< Attributes of the Task */ + ) { + return Start(task_id, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal()); + } + + /** + * Starts a new Task. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results /**< Pointer to the results. */ + ) { + return Start(task_id, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES); + } + + /** + * Starts a new Task. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes /**< Attributes of the Task */ + ) { + return Start(MTAPI_TASK_ID_NONE, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal()); + } + + /** + * Starts a new Task. + * + * \returns The handle to the started Task. + * \threadsafe + */ + template + Task Start( + Job const & job, /**< The Job to execute. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results /**< Pointer to the results. */ + ) { + return Start(MTAPI_TASK_ID_NONE, + job.GetInternal(), + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES); + } + + friend class embb::base::Allocation; + + private: + // not copyable + Node(Node const & node); + Node const & operator=(Node const & other); + + Node( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + NodeAttributes const & attr) { + mtapi_status_t status; + mtapi_info_t info; + mtapi_initialize(domain_id, node_id, &attr.GetInternal(), &info, &status); + needs_finalize_ = status == MTAPI_SUCCESS; + internal::CheckStatus(status); + + core_count_ = info.hardware_concurrency; + worker_thread_count_ = embb_core_set_count( + &attr.GetInternal().core_affinity); + } + + ~Node() { + if (needs_finalize_) { + mtapi_status_t status; + mtapi_finalize(&status); + internal::CheckStatus(status); + } + } + + Task Start( + mtapi_task_id_t task_id, + mtapi_job_hndl_t job, + const void * arguments, + mtapi_size_t arguments_size, + void * results, + mtapi_size_t results_size, + mtapi_task_attributes_t const * attributes + ) { + mtapi_status_t status; + mtapi_task_hndl_t task_hndl = + mtapi_task_start(task_id, job, arguments, arguments_size, + results, results_size, attributes, MTAPI_GROUP_NONE, + &status); + internal::CheckStatus(status); + return Task(task_hndl); + } + + static embb::mtapi::Node * node_instance_; + + mtapi_uint_t core_count_; + mtapi_uint_t worker_thread_count_; + bool needs_finalize_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_NODE_H_ diff --git b/mtapi_cpp/include/embb/mtapi/node_attributes.h a/mtapi_cpp/include/embb/mtapi/node_attributes.h new file mode 100644 index 0000000..ae03fa7 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/node_attributes.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_NODE_ATTRIBUTES_H_ +#define EMBB_MTAPI_NODE_ATTRIBUTES_H_ + +#include +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Contains attributes of a Node. + * + * \ingroup CPP_MTAPI + */ +class NodeAttributes { + public: + /** + * Constructs a NodeAttributes object. + */ + NodeAttributes() { + mtapi_status_t status; + mtapi_nodeattr_init(&attributes_, &status); + internal::CheckStatus(status); + } + + /** + * Copies a NodeAttributes object. + */ + NodeAttributes( + NodeAttributes const & other /**< The NodeAttributes to copy. */ + ) + : attributes_(other.attributes_) { + // empty + } + + /** + * Copies a NodeAttributes object. + */ + void operator=( + NodeAttributes const & other /**< The NodeAttributes to copy. */ + ) { + attributes_ = other.attributes_; + } + + /** + * Sets the core affinity of the Node. This also determines the number of + * worker threads. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetCoreAffinity( + embb::base::CoreSet const & cores /**< The cores to use. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_CORE_AFFINITY, + &cores.GetInternal(), sizeof(embb_core_set_t), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of concurrently active tasks. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxTasks( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_TASKS, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of actions. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxActions( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_ACTIONS, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of groups. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxGroups( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_GROUPS, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of queues. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxQueues( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_QUEUES, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the default limit (capacity) of all queues. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetQueueLimit( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_QUEUE_LIMIT, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of available jobs. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxJobs( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_JOBS, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of actions per job. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxActionsPerJob( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_ACTIONS_PER_JOB, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the maximum number of available priorities. The priority values + * will range from 0 to \c value - 1 with 0 being the highest priority. + * + * \returns Reference to this object. + * \notthreadsafe + */ + NodeAttributes & SetMaxPriorities( + mtapi_uint_t value /**< The value to set. */ + ) { + mtapi_status_t status; + mtapi_nodeattr_set(&attributes_, MTAPI_NODE_MAX_PRIORITIES, + &value, sizeof(value), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns A reference to the internal mtapi_node_attributes_t structure. + * \waitfree + */ + mtapi_node_attributes_t const & GetInternal() const { + return attributes_; + } + + private: + mtapi_node_attributes_t attributes_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_NODE_ATTRIBUTES_H_ diff --git b/mtapi_cpp/include/embb/mtapi/queue.h a/mtapi_cpp/include/embb/mtapi/queue.h new file mode 100644 index 0000000..718fac3 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/queue.h @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_QUEUE_H_ +#define EMBB_MTAPI_QUEUE_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace mtapi { + +/** + * Allows for stream processing, either ordered or unordered. + * + * \ingroup CPP_MTAPI + */ +class Queue { + public: + /** + * Constructs a Queue with the given Job and default attributes. + * Requires an initialized Node. + */ + Queue( + Job const & job /**< The Job to use for the Queue. */ + ) { + Create(MTAPI_QUEUE_ID_NONE, job, MTAPI_DEFAULT_QUEUE_ATTRIBUTES); + } + + /** + * Constructs a Queue with the given Job and QueueAttributes. + * Requires an initialized Node. + */ + Queue( + Job const & job, /**< The Job to use for the Queue. */ + QueueAttributes const & attr /**< The attributes to use. */ + ) { + Create(MTAPI_QUEUE_ID_NONE, job, &attr.GetInternal()); + } + + /** + * Destroys a Queue object. + */ + ~Queue() { + mtapi_queue_delete(handle_, MTAPI_INFINITE, MTAPI_NULL); + } + + /** + * Enables the Queue. \link Task Tasks \endlink enqueued while the Queue was + * disabled are executed. + * \waitfree + */ + void Enable() { + mtapi_status_t status; + mtapi_queue_enable(handle_, &status); + internal::CheckStatus(status); + } + + /** + * Disables the Queue. Running \link Task Tasks \endlink are canceled. The + * Queue waits for the Tasks to finish for \c timout milliseconds. + * \waitfree + */ + void Disable( + mtapi_timeout_t timeout /**< The timeout in milliseconds. */ + ) { + mtapi_status_t status; + mtapi_queue_disable(handle_, timeout, &status); + internal::CheckStatus(status); + } + + /** + * Disables the Queue. Running \link Task Tasks \endlink are canceled. The + * Queue waits for the Tasks to finish. + * \waitfree + */ + void Disable() { + Disable(MTAPI_INFINITE); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes, /**< Attributes of the Task */ + Group const & group /**< The Group to start the Task in */ + ) { + return Enqueue(task_id, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal(), group.GetInternal()); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + Group const & group /**< The Group to start the Task in */ + ) { + return Enqueue(task_id, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES, group.GetInternal()); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes /**< Attributes of the Task */ + ) { + return Enqueue(task_id, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal(), MTAPI_GROUP_NONE); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + mtapi_task_id_t task_id, /**< A user defined ID of the Task. */ + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results /**< Pointer to the results. */ + ) { + return Enqueue(task_id, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes, /**< Attributes of the Task */ + Group const & group /**< The Group to start the Task in */ + ) { + return Enqueue(MTAPI_TASK_ID_NONE, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal(), group.GetInternal()); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + Group const & group /**< The Group to start the Task in */ + ) { + return Enqueue(MTAPI_TASK_ID_NONE, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES, group.GetInternal()); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results, /**< Pointer to the results. */ + TaskAttributes const & attributes /**< Attributes of the Task */ + ) { + return Enqueue(MTAPI_TASK_ID_NONE, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + &attributes.GetInternal(), MTAPI_GROUP_NONE); + } + + /** + * Enqueues a new Task. + * + * \returns The handle to the enqueued Task. + * \threadsafe + */ + template + Task Enqueue( + const ARGS * arguments, /**< Pointer to the arguments. */ + RES * results /**< Pointer to the results. */ + ) { + return Enqueue(MTAPI_TASK_ID_NONE, + arguments, internal::SizeOfType(), + results, internal::SizeOfType(), + MTAPI_DEFAULT_TASK_ATTRIBUTES, MTAPI_GROUP_NONE); + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns The internal mtapi_queue_hndl_t. + * \waitfree + */ + mtapi_queue_hndl_t GetInternal() const { + return handle_; + } + + friend class embb::base::Allocation; + + private: + // no default constructor + Queue(); + + // not copyable + Queue(Queue const & other); + void operator=(Queue const & other); + + void Create( + mtapi_queue_id_t queue_id, + Job const & job, + mtapi_queue_attributes_t const * attributes + ) { + mtapi_status_t status; + handle_ = mtapi_queue_create(queue_id, job.GetInternal(), + attributes, &status); + internal::CheckStatus(status); + } + + Task Enqueue( + mtapi_task_id_t task_id, + const void * arguments, + mtapi_size_t arguments_size, + void * results, + mtapi_size_t results_size, + mtapi_task_attributes_t const * attributes, + mtapi_group_hndl_t group + ) { + mtapi_status_t status; + mtapi_task_hndl_t task_hndl = + mtapi_task_enqueue(task_id, handle_, arguments, arguments_size, + results, results_size, attributes, group, + &status); + internal::CheckStatus(status); + return Task(task_hndl); + } + + mtapi_queue_hndl_t handle_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_QUEUE_H_ diff --git b/mtapi_cpp/include/embb/mtapi/queue_attributes.h a/mtapi_cpp/include/embb/mtapi/queue_attributes.h new file mode 100644 index 0000000..ee701d7 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/queue_attributes.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_QUEUE_ATTRIBUTES_H_ +#define EMBB_MTAPI_QUEUE_ATTRIBUTES_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Contains attributes of a Queue. + * + * \ingroup CPP_MTAPI + */ +class QueueAttributes { + public: + /** + * Constructs a QueueAttributes object. + */ + QueueAttributes() { + mtapi_status_t status; + mtapi_queueattr_init(&attributes_, &status); + internal::CheckStatus(status); + } + + /** + * Sets the global property of a Queue. + * This determines whether the object will be visible across nodes. + * + * \returns Reference to this object. + * \notthreadsafe + */ + QueueAttributes & SetGlobal( + bool state /**< The state to set. */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_queueattr_set(&attributes_, MTAPI_QUEUE_GLOBAL, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the ordered property of a Queue. + * If set to \c true, tasks enqueued will be executed in order. + * + * \returns Reference to this object. + * \notthreadsafe + */ + QueueAttributes & SetOrdered( + bool state /**< The state to set. */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_queueattr_set(&attributes_, MTAPI_QUEUE_ORDERED, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the retain property of a Queue. + * If set to \c true, tasks will be retained while a queue is disabled. + * Otherwise the will be canceled. + * + * \returns Reference to this object. + * \notthreadsafe + */ + QueueAttributes & SetRetain( + bool state /**< The state to set. */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_queueattr_set(&attributes_, MTAPI_QUEUE_RETAIN, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the domain shared property of a Queue. + * This determines whether the object will be visible across domains. + * + * \returns Reference to this object. + * \notthreadsafe + */ + QueueAttributes & SetDomainShared( + bool state /**< The state to set. */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_queueattr_set(&attributes_, MTAPI_QUEUE_DOMAIN_SHARED, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the priority of a Queue. + * The priority influences the order in which tasks are chosen for execution. + * + * \returns Reference to this object. + * \notthreadsafe + */ + QueueAttributes & SetPriority( + mtapi_uint_t priority /**< The priority to set. */ + ) { + mtapi_status_t status; + mtapi_queueattr_set(&attributes_, MTAPI_QUEUE_PRIORITY, + &priority, sizeof(priority), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the limit (capacity) of a Queue. + * + * \returns Reference to this object. + * \notthreadsafe + */ + QueueAttributes & SetLimit( + mtapi_uint_t limit /**< The limit to set. */ + ) { + mtapi_status_t status; + mtapi_queueattr_set(&attributes_, MTAPI_QUEUE_LIMIT, + &limit, sizeof(limit), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns A reference to the internal mtapi_queue_attributes_t structure. + * \waitfree + */ + mtapi_queue_attributes_t const & GetInternal() const { + return attributes_; + } + + private: + mtapi_queue_attributes_t attributes_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_QUEUE_ATTRIBUTES_H_ diff --git b/mtapi_cpp/include/embb/mtapi/status_exception.h a/mtapi_cpp/include/embb/mtapi/status_exception.h new file mode 100644 index 0000000..95e7ad5 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/status_exception.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_STATUS_EXCEPTION_H_ +#define EMBB_MTAPI_STATUS_EXCEPTION_H_ + +#include + +namespace embb { +namespace mtapi { + +/** + * Represents an MTAPI error state and is thrown by almost all mtapi_cpp + * methods. + * + * \ingroup CPP_MTAPI + */ +class StatusException : public embb::base::Exception { + public: + /** + * Constructs a StatusException. + */ + explicit StatusException( + const char* message /**< The message to use. */ + ) + : embb::base::Exception(message) { + // empty + } + + /** + * Code associated with this exception + * + * \returns An integer representing the code of the exception + * + * \waitfree + */ + virtual int Code() const { return EMBB_ERROR; } +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_STATUS_EXCEPTION_H_ diff --git b/mtapi_cpp/include/embb/mtapi/task.h a/mtapi_cpp/include/embb/mtapi/task.h new file mode 100644 index 0000000..eaacc0c --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/task.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_TASK_H_ +#define EMBB_MTAPI_TASK_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * A Task represents a running Action of a specific Job. + * + * \ingroup CPP_MTAPI + */ +class Task { + public: + /** + * Constructs an invalid Task. + */ + Task() { + handle_.id = 0; + handle_.tag = 0; + } + + /** + * Copies a Task. + */ + Task( + Task const & other /**< The task to copy. */ + ) : handle_(other.handle_) { + // emtpy + } + + /** + * Copies a Task. + */ + void operator=( + Task const & other /**< The task to copy. */ + ) { + handle_ = other.handle_; + } + + /** + * Destroys a Task. + */ + ~Task() { + // empty + } + + /** + * Waits for Task to finish for \c timeout milliseconds. + * \return The status of the finished Task, \c MTAPI_TIMEOUT or + * \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t Wait( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ) { + mtapi_status_t status; + mtapi_task_wait(handle_, timeout, &status); + return status; + } + + /** + * Waits for Task to finish. + * \return The status of the finished Task or \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t Wait() { + mtapi_status_t status; + mtapi_task_wait(handle_, MTAPI_INFINITE, &status); + return status; + } + + /** + * Signals the Task to cancel computation. + * \waitfree + */ + void Cancel() { + mtapi_status_t status; + mtapi_task_cancel(handle_, &status); + internal::CheckStatus(status); + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns The internal mtapi_task_hndl_t. + * \waitfree + */ + mtapi_task_hndl_t GetInternal() const { + return handle_; + } + + private: + explicit Task(mtapi_task_hndl_t handle) + : handle_(handle) { + // empty + } + + mtapi_task_hndl_t handle_; + + friend class Node; + friend class Group; + friend class Queue; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_TASK_H_ diff --git b/mtapi_cpp/include/embb/mtapi/task_attributes.h a/mtapi_cpp/include/embb/mtapi/task_attributes.h new file mode 100644 index 0000000..7c16094 --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/task_attributes.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_TASK_ATTRIBUTES_H_ +#define EMBB_MTAPI_TASK_ATTRIBUTES_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Contains attributes of a Task. + * + * \ingroup CPP_MTAPI + */ +class TaskAttributes { + public: + /** + * Constructs a TaskAttributes object. + */ + TaskAttributes() { + mtapi_status_t status; + mtapi_taskattr_init(&attributes_, &status); + internal::CheckStatus(status); + } + + /** + * Sets the detached property of a Task. + * If set to \c true, the started Task will have no handle and cannot be + * waited for. + * + * \returns Reference to this object. + * \notthreadsafe + */ + TaskAttributes & SetDetached( + bool state /**< The state to set. */ + ) { + mtapi_status_t status; + mtapi_boolean_t st = state ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_taskattr_set(&attributes_, MTAPI_TASK_DETACHED, + &st, sizeof(st), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the priority of a Task. + * The priority influences the order in which tasks are chosen for execution. + * + * \returns Reference to this object. + * \notthreadsafe + */ + TaskAttributes & SetPriority( + mtapi_uint_t priority /**< The priority to set. */ + ) { + mtapi_status_t status; + mtapi_taskattr_set(&attributes_, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Sets the number of instances in a Task. + * The Task will be launched \c instances times. In the action function, + * the number of instances and the current instance can be queried from + * the TaskContext. + * + * \returns Reference to this object. + * \notthreadsafe + */ + TaskAttributes & SetInstances( + mtapi_uint_t instances /**< Number of instances to set. */ + ) { + mtapi_status_t status; + mtapi_taskattr_set(&attributes_, MTAPI_TASK_INSTANCES, + &instances, sizeof(instances), &status); + internal::CheckStatus(status); + return *this; + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns A reference to the internal mtapi_task_attributes_t structure. + * \waitfree + */ + mtapi_task_attributes_t const & GetInternal() const { + return attributes_; + } + + private: + mtapi_task_attributes_t attributes_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_TASK_ATTRIBUTES_H_ diff --git b/mtapi_cpp/include/embb/mtapi/task_context.h a/mtapi_cpp/include/embb/mtapi/task_context.h new file mode 100644 index 0000000..3b8982d --- /dev/null +++ a/mtapi_cpp/include/embb/mtapi/task_context.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_TASK_CONTEXT_H_ +#define EMBB_MTAPI_TASK_CONTEXT_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Provides information about the status of the currently running Task. + * + * \ingroup CPP_MTAPI + */ +class TaskContext { + public: + /** + * Constructs a TaskContext from the given C representation. + */ + explicit TaskContext( + mtapi_task_context_t * task_context + /**< The C task context to wrap. */ + ) + : context_(task_context) { + // empty + } + + /** + * Queries whether the Task running in the TaskContext should finish. + * \return \c true if the Task should finish, otherwise \c false + * \notthreadsafe + */ + bool ShouldCancel() { + return MTAPI_TASK_CANCELLED == GetTaskState(); + } + + /** + * Queries the current Task state. + * \return The current Task state. + * \notthreadsafe + */ + mtapi_task_state_t GetTaskState() { + mtapi_status_t status; + mtapi_task_state_t result = + mtapi_context_taskstate_get(context_, &status); + internal::CheckStatus(status); + return result; + } + + /** + * Queries the index of the worker thread the Task is running on. + * \return The worker thread index the Task is running on + * \notthreadsafe + */ + mtapi_uint_t GetCurrentWorkerNumber() { + mtapi_status_t status; + mtapi_uint_t result = mtapi_context_corenum_get(context_, &status); + internal::CheckStatus(status); + return result; + } + + /** + * Queries the current instance of the currently executing Task. + * \return The current instance number + * \notthreadsafe + */ + mtapi_uint_t GetInstanceNumber() { + mtapi_status_t status; + mtapi_uint_t result = mtapi_context_instnum_get(context_, &status); + internal::CheckStatus(status); + return result; + } + + /** + * Queries the number of instances of the currently executing Task. + * \return The number of instances + * \notthreadsafe + */ + mtapi_uint_t GetNumberOfInstances() { + mtapi_status_t status; + mtapi_uint_t result = mtapi_context_numinst_get(context_, &status); + internal::CheckStatus(status); + return result; + } + + /** + * Sets the return status of the running Task. This will be returned by + * Task::Wait() and is set to \c MTAPI_SUCCESS by default. + * \notthreadsafe + */ + void SetStatus( + mtapi_status_t error_code /**< [in] The status to return by + Task::Wait(), Group::WaitAny(), + Group::WaitAll() */ + ) { + mtapi_status_t status; + mtapi_context_status_set(context_, error_code, &status); + internal::CheckStatus(status); + } + + /** + * Returns the internal representation of this object. + * Allows for interoperability with the C interface. + * + * \returns A pointer to a mtapi_task_context_t. + * \notthreadsafe + */ + mtapi_task_context_t * GetInternal() const { + return context_; + } + + private: + // no default constructor + TaskContext(); + + mtapi_task_context_t * context_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_TASK_CONTEXT_H_ diff --git b/mtapi_cpp/src/check_status.cc a/mtapi_cpp/src/check_status.cc new file mode 100644 index 0000000..50c1c2d --- /dev/null +++ a/mtapi_cpp/src/check_status.cc @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +namespace embb { +namespace mtapi { +namespace internal { + +void CheckStatus(mtapi_status_t status) { + if (MTAPI_SUCCESS != status) { + switch (status) { + case MTAPI_SUCCESS: + case MTAPI_TIMEOUT: + case MTAPI_GROUP_COMPLETED: + // these are no errors + break; + + case MTAPI_ERR_NODE_INITIALIZED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: node already initialized."); + break; + case MTAPI_ERR_NODE_NOTINIT: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: node not initialized."); + break; + case MTAPI_ERR_PARAMETER: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: invalid parameter."); + break; + case MTAPI_ERR_CORE_NUM: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: invalid worker number."); + break; + case MTAPI_ERR_RUNTIME_LOADBALANCING_NOTSUPPORTED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: load balancing not supported."); + break; + case MTAPI_ERR_RUNTIME_REMOTETASKS_NOTSUPPORTED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: remote tasks not supported."); + break; + case MTAPI_ERR_ARG_NOT_IMPLEMENTED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: argument not implemented."); + break; + case MTAPI_ERR_FUNC_NOT_IMPLEMENTED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: function not implemented."); + break; + case MTAPI_ERR_WAIT_PENDING: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: wait pending."); + break; + case MTAPI_ERR_ARG_SIZE: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: argument size mismatch."); + break; + case MTAPI_ERR_RESULT_SIZE: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: result buffer size mismatch."); + break; + case MTAPI_ERR_BUFFER_SIZE: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: buffer size mismatch."); + break; + case MTAPI_ERR_GROUP_LIMIT: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: group limit exceeded."); + break; + case MTAPI_ERR_GROUP_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: group invalid."); + break; + case MTAPI_ERR_QUEUE_LIMIT: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: queue limit exceeded."); + break; + case MTAPI_ERR_QUEUE_DISABLED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: queue disabled."); + break; + case MTAPI_ERR_QUEUE_DELETED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: queue deleted."); + break; + case MTAPI_ERR_QUEUE_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: queue invalid."); + break; + case MTAPI_ERR_JOB_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: job invalid."); + break; + case MTAPI_ERR_TASK_LIMIT: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: task limit exceeded."); + break; + case MTAPI_ERR_TASK_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: task invalid."); + break; + case MTAPI_ERR_CONTEXT_OUTOFCONTEXT: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: task context used outside of worker thread."); + break; + case MTAPI_ERR_CONTEXT_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: task context invalid."); + break; + case MTAPI_ERR_ACTION_DISABLED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action disabled."); + break; + case MTAPI_ERR_ACTION_DELETED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action deleted."); + break; + case MTAPI_ERR_ACTION_CANCELLED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action canceled."); + break; + case MTAPI_ERR_ACTION_FAILED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action failed."); + break; + case MTAPI_ERR_ACTION_LIMIT: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action limit exceeded."); + break; + case MTAPI_ERR_ACTION_EXISTS: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action already exists."); + break; + case MTAPI_ERR_ACTION_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: action invalid."); + break; + case MTAPI_ERR_DOMAIN_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: domain invalid."); + break; + case MTAPI_ERR_NODE_INVALID: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: node invalid."); + break; + case MTAPI_ERR_NODE_INITFAILED: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: node initialization failed."); + break; + case MTAPI_ERR_ATTR_SIZE: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: attribute size mismatch."); + break; + case MTAPI_ERR_ATTR_NUM: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: invalid attribute."); + break; + case MTAPI_ERR_ATTR_READONLY: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: attribute is read only."); + break; + + case MTAPI_ERR_ACTION_NUM_INVALID: + case MTAPI_ERR_UNKNOWN: + default: + EMBB_THROW(embb::mtapi::StatusException, + "MTAPI: unknown error."); + break; + } + } +} + +} // namespace internal +} // namespace mtapi +} // namespace embb diff --git b/mtapi_cpp/src/node.cc a/mtapi_cpp/src/node.cc new file mode 100644 index 0000000..9b77f2c --- /dev/null +++ a/mtapi_cpp/src/node.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace embb { +namespace mtapi { + +embb::mtapi::Node * embb::mtapi::Node::node_instance_ = NULL; + +} // namespace mtapi +} // namespace embb diff --git b/mtapi_cpp/test/main.cc a/mtapi_cpp/test/main.cc new file mode 100644 index 0000000..2e5d7f5 --- /dev/null +++ a/mtapi_cpp/test/main.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include + + +PT_MAIN("MTAPI C++") { + embb_thread_set_max_count(1024); + + PT_RUN(TaskTest); + PT_RUN(GroupTest); + PT_RUN(QueueTest); +} diff --git b/mtapi_cpp/test/mtapi_cpp_test_config.h a/mtapi_cpp/test/mtapi_cpp_test_config.h new file mode 100644 index 0000000..31c5f2c --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_config.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_CPP_TEST_MTAPI_CPP_TEST_CONFIG_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_CONFIG_H_ + +#include +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_CONFIG_H_ diff --git b/mtapi_cpp/test/mtapi_cpp_test_group.cc a/mtapi_cpp/test/mtapi_cpp_test_group.cc new file mode 100644 index 0000000..1378311 --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_group.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#define JOB_TEST_GROUP 5 +#define TASK_COUNT 4 + +struct result_example_struct { + int value1; + int value2; +}; + +typedef struct result_example_struct result_example_t; + +static void testGroupAction( + const void* args, + mtapi_size_t /*args_size*/, + void* results, + mtapi_size_t /*results_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * /*context*/) { + result_example_t const * in = static_cast(args); + result_example_t * out = static_cast(results); + out->value2 = in->value1; +} + +static void testDoSomethingElse() { +} + +GroupTest::GroupTest() { + CreateUnit("mtapi_cpp group test").Add(&GroupTest::TestBasic, this); +} + +void GroupTest::TestBasic() { + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::mtapi::Job job(JOB_TEST_GROUP, THIS_DOMAIN_ID); + embb::mtapi::Action action(JOB_TEST_GROUP, testGroupAction); + + { + embb::mtapi::Group group; + + result_example_t buffer[TASK_COUNT]; + for (int ii = 0; ii < TASK_COUNT; ii++) { + buffer[ii].value1 = ii; + buffer[ii].value2 = -1; + group.Start(job, &buffer[ii], &buffer[ii]); + } + + testDoSomethingElse(); + + group.WaitAll(); + + for (int ii = 0; ii < TASK_COUNT; ii++) { + PT_EXPECT_EQ(buffer[ii].value1, ii); + PT_EXPECT_EQ(buffer[ii].value2, ii); + } + } + + { + embb::mtapi::Group group; + + result_example_t buffer[TASK_COUNT]; + for (int ii = 0; ii < 4; ii++) { + buffer[ii].value1 = ii; + buffer[ii].value2 = -1; + group.Start(job, &buffer[ii], &buffer[ii]); + } + + testDoSomethingElse(); + + mtapi_status_t status; + result_example_t* result; + while (MTAPI_SUCCESS == + (status = group.WaitAny(reinterpret_cast(&result)))) { + PT_EXPECT(result != MTAPI_NULL); + PT_EXPECT_EQ(result->value1, result->value2); + } + PT_EXPECT_EQ(status, MTAPI_GROUP_COMPLETED); + } + + embb::mtapi::Node::Finalize(); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} diff --git b/mtapi_cpp/test/mtapi_cpp_test_group.h a/mtapi_cpp/test/mtapi_cpp_test_group.h new file mode 100644 index 0000000..d80973f --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_group.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_CPP_TEST_MTAPI_CPP_TEST_GROUP_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_GROUP_H_ + +#include + +class GroupTest : public partest::TestCase { + public: + GroupTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_GROUP_H_ diff --git b/mtapi_cpp/test/mtapi_cpp_test_queue.cc a/mtapi_cpp/test/mtapi_cpp_test_queue.cc new file mode 100644 index 0000000..8e17abf --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_queue.cc @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#define JOB_TEST_QUEUE 42 + +static void testQueueAction( + const void* /*args*/, + mtapi_size_t /*args_size*/, + void* results, + mtapi_size_t /*results_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * context) { + embb::mtapi::TaskContext ctx(context); + int * out = reinterpret_cast(results); + *out = 1; + ctx.SetStatus(MTAPI_ERR_ACTION_CANCELLED); +} + +static void testDoSomethingElse() { +} + +QueueTest::QueueTest() { + CreateUnit("mtapi_cpp queue test").Add(&QueueTest::TestBasic, this); +} + +void QueueTest::TestBasic() { + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::mtapi::Job job(JOB_TEST_QUEUE, THIS_DOMAIN_ID); + embb::mtapi::Action action(JOB_TEST_QUEUE, testQueueAction, + MTAPI_NULL, 0); + + { + embb::mtapi::Queue queue(job); + + int result = 0; + embb::mtapi::Task task = queue.Enqueue(MTAPI_NULL, &result); + + testDoSomethingElse(); + + mtapi_status_t status = task.Wait(); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_CANCELLED); + PT_EXPECT_EQ(result, 1); + } + + embb::mtapi::Node::Finalize(); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} diff --git b/mtapi_cpp/test/mtapi_cpp_test_queue.h a/mtapi_cpp/test/mtapi_cpp_test_queue.h new file mode 100644 index 0000000..42c6c8e --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_queue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_CPP_TEST_MTAPI_CPP_TEST_QUEUE_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_QUEUE_H_ + +#include + +class QueueTest : public partest::TestCase { + public: + QueueTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_QUEUE_H_ diff --git b/mtapi_cpp/test/mtapi_cpp_test_task.cc a/mtapi_cpp/test/mtapi_cpp_test_task.cc new file mode 100644 index 0000000..fa3de4e --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_task.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include + +#define JOB_TEST_TASK 42 +#define JOB_TEST_ERROR 17 + +static void testTaskAction( + const void* args, + mtapi_size_t /*args_size*/, + void* results, + mtapi_size_t /*results_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * context) { + embb::mtapi::TaskContext ctx(context); + const char * msg = static_cast(args); + std::string* out = static_cast(results); + *out = msg; +} + +static void testErrorAction( + const void* /*args*/, + mtapi_size_t /*args_size*/, + void* /*results*/, + mtapi_size_t /*results_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * context) { + embb::mtapi::TaskContext ctx(context); + ctx.SetStatus(MTAPI_ERR_ACTION_FAILED); +} + +static void testDoSomethingElse() { +} + +TaskTest::TaskTest() { + CreateUnit("mtapi_cpp task test").Add(&TaskTest::TestBasic, this); +} + +void TaskTest::TestBasic() { + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + + { + embb::mtapi::NodeAttributes attr; + attr + .SetMaxActions(1024) + .SetMaxActionsPerJob(2) + .SetMaxPriorities(4); + } + + { + embb::mtapi::Affinity affinity(false); + PT_EXPECT_EQ(affinity.GetInternal(), 0u); + affinity.Set(0u, true); + PT_EXPECT_EQ(affinity.GetInternal(), 1u); + affinity.Set(1u, true); + PT_EXPECT_EQ(affinity.GetInternal(), 3u); + affinity.Set(0u, false); + PT_EXPECT_EQ(affinity.GetInternal(), 2u); + PT_EXPECT_EQ(affinity.Get(0), false); + PT_EXPECT_EQ(affinity.Get(1), true); + } + + { + embb::mtapi::Job job_task(JOB_TEST_TASK, THIS_DOMAIN_ID); + + embb::mtapi::Action action_task(JOB_TEST_TASK, testTaskAction); + + std::string test; + embb::mtapi::Task task = node.Start(job_task, "simple", &test); + testDoSomethingElse(); + mtapi_status_t status = task.Wait(); + PT_EXPECT_EQ(status, MTAPI_SUCCESS); + PT_EXPECT(test == "simple"); + } + + { + embb::mtapi::Job job_error(JOB_TEST_ERROR, THIS_DOMAIN_ID); + + embb::mtapi::Action action_error(JOB_TEST_ERROR, testErrorAction, + embb::mtapi::ActionAttributes()); + + std::string test; + embb::mtapi::Task task = node.Start(job_error, "simple", &test); + testDoSomethingElse(); + mtapi_status_t status = task.Wait(); + PT_EXPECT_EQ(status, MTAPI_ERR_ACTION_FAILED); + } + + embb::mtapi::Node::Finalize(); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} diff --git b/mtapi_cpp/test/mtapi_cpp_test_task.h a/mtapi_cpp/test/mtapi_cpp_test_task.h new file mode 100644 index 0000000..f40c3bd --- /dev/null +++ a/mtapi_cpp/test/mtapi_cpp_test_task.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_CPP_TEST_MTAPI_CPP_TEST_TASK_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_TASK_H_ + +#include + +class TaskTest : public partest::TestCase { + public: + TaskTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_TASK_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/CMakeLists.txt a/mtapi_plugins_c/mtapi_network_c/CMakeLists.txt new file mode 100644 index 0000000..a23a903 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/CMakeLists.txt @@ -0,0 +1,47 @@ +project (project_embb_mtapi_network_c) + +file(GLOB_RECURSE EMBB_MTAPI_NETWORK_C_SOURCES "src/*.c" "src/*.h") +file(GLOB_RECURSE EMBB_MTAPI_NETWORK_C_HEADERS "include/*.h") + +file(GLOB_RECURSE EMBB_MTAPI_NETWORK_TEST_SOURCES "test/*.cc" "test/*.h") + +IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) +FOREACH(src_tmp ${EMBB_MTAPI_NETWORK_TEST_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +FOREACH(src_tmp ${EMBB_MTAPI_NETWORK_C_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +ENDIF() + +IF(MSVC) +set (EMBB_MTAPI_NETWORK_C_LIBS ws2_32) +ENDIF() + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_MTAPI_NETWORK_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_MTAPI_NETWORK_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/src + ) + +add_library(embb_mtapi_network_c ${EMBB_MTAPI_NETWORK_C_SOURCES} ${EMBB_MTAPI_NETWORK_C_HEADERS}) +target_link_libraries(embb_mtapi_network_c embb_mtapi_c embb_base_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../partest/include) + add_executable (embb_mtapi_network_c_test ${EMBB_MTAPI_NETWORK_TEST_SOURCES}) + target_link_libraries(embb_mtapi_network_c_test embb_mtapi_network_c embb_mtapi_c partest embb_base_c ${compiler_libs} ${EMBB_MTAPI_NETWORK_C_LIBS}) + CopyBin(BIN embb_mtapi_network_c_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_mtapi_network_c DESTINATION lib) diff --git b/mtapi_plugins_c/mtapi_network_c/include/embb/mtapi/c/mtapi_network.h a/mtapi_plugins_c/mtapi_network_c/include/embb/mtapi/c/mtapi_network.h new file mode 100644 index 0000000..43385ee --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/include/embb/mtapi/c/mtapi_network.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_C_MTAPI_NETWORK_H_ +#define EMBB_MTAPI_C_MTAPI_NETWORK_H_ + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * \defgroup C_MTAPI_NETWORK MTAPI Network Plugin + * + * \ingroup C_MTAPI_EXT + * + * Provides functionality to distribute tasks across nodes in a TCP/IP network. + */ + + +/** + * Initializes the MTAPI network environment on a previously initialized MTAPI + * node. + * + * It must be called on all nodes using the MTAPI network plugin. + * + * Application software using MTAPI network must call + * mtapi_network_plugin_initialize() once per node. It is an error to call + * mtapi_network_plugin_initialize() multiple times + * from a given node, unless mtapi_network_plugin_finalize() is called in + * between. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_UNKNOWN | MTAPI network couldn't be initialized. + * + * \see mtapi_network_plugin_finalize() + * + * \notthreadsafe + * \ingroup C_MTAPI_NETWORK + */ +void mtapi_network_plugin_initialize( + MTAPI_IN char * host, /**< [in] The interface to listen on, if + MTAPI_NULL is given the plugin will + listen on all available + interfaces. */ + MTAPI_IN mtapi_uint16_t port, /**< [in] The port to listen on. */ + MTAPI_IN mtapi_uint16_t max_connections, + /**< [in] Maximum concurrent connections + accepted by the plugin. */ + MTAPI_IN mtapi_size_t buffer_size, /**< [in] Capacity of the transfer + buffers, this should be chosen big + enough to hold argument and result + buffers.*/ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + +/** + * Finalizes the MTAPI network environment on the local MTAPI node. + * + * It has to be called by each node using MTAPI network. It is an error to call + * mtapi_network_plugin_finalize() without first calling + * mtapi_network_plugin_initialize(). An MTAPI node can call + * mtapi_network_plugin_finalize() once for each call to + * mtapi_network_plugin_initialize(), but it is an error to call + * mtapi_network_plugin_finalize() multiple times from a given node + * unless mtapi_network_plugin_initialize() has been called prior to each + * mtapi_network_plugin_finalize() call. + * + * All network tasks that have not completed and that have been started on the + * node where mtapi_network_plugin_finalize() is called will be canceled + * (see mtapi_task_cancel()). mtapi_network_plugin_finalize() blocks until all + * tasks that have been started on the same node return. Tasks that execute + * actions on the node where mtapi_network_plugin_finalize() is called, also + * block finalization of the MTAPI network system on that node. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_UNKNOWN | MTAPI network couldn't be finalized. + * + * \see mtapi_network_plugin_initialize(), mtapi_task_cancel() + * + * \notthreadsafe + * \ingroup C_MTAPI_NETWORK + */ +void mtapi_network_plugin_finalize( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + +/** + * This function creates a network action. + * + * It is called on the node where the user wants to execute an action on a + * remote node where the actual action is implemented. A network action + * contains a reference to a local job, a remote job and a remote domain as + * well as a host and port to connect to. + * After a network action is created, it is referenced by the application using + * a node-local handle of type \c mtapi_action_hndl_t, or indirectly through a + * node-local job handle of type \c mtapi_job_hndl_t. A network action's + * life-cycle begins with mtapi_network_action_create(), and ends when + * mtapi_action_delete() or mtapi_finalize() is called. + * + * To create an action, the application must supply the domain-wide job ID of + * the job associated with the action. Job IDs must be predefined in the + * application and runtime, of type \c mtapi_job_id_t, which is an + * implementation-defined type. The job ID is unique in the sense that it is + * unique for the job implemented by the action. However several actions may + * implement the same job for load balancing purposes. + * + * A network action defines no node local data, instead the node local data of + * the remote action is used. The user has to make sure that the remote node + * local data matches what he expects the remote action to use if invoked + * through the network. + * + * On success, an action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the action already exists, \c status will + * be set to \c MTAPI_ERR_ACTION_EXISTS and the handle returned will not be a + * valid handle. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_JOB_INVALIDThe \c job_id is not a valid job ID, i.e., no action was created for + * that ID or the action has been deleted.
\c MTAPI_ERR_ACTION_EXISTSThis action is already created.
\c MTAPI_ERR_ACTION_LIMITExceeded maximum number of actions allowed.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_ERR_UNKNOWNThe remote node could not be reached or there was no local + * interface available.
+ * + * \see mtapi_action_delete(), mtapi_finalize() + * + * \returns Handle to newly created network action, invalid handle on error + * \threadsafe + * \ingroup C_MTAPI_NETWORK + */ +mtapi_action_hndl_t mtapi_network_action_create( + MTAPI_IN mtapi_domain_t domain_id, /**< [in] The domain the action is + associated with */ + MTAPI_IN mtapi_job_id_t local_job_id, + /**< [in] The ID of the local job */ + MTAPI_IN mtapi_job_id_t remote_job_id, + /**< [in] The ID of the remote job */ + MTAPI_IN char * host, /**< [in] The host to connect to */ + MTAPI_IN mtapi_uint16_t port, /**< [in] The port the host is listening + on */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + + +#ifdef __cplusplus +} +#endif + + +#endif // EMBB_MTAPI_C_MTAPI_NETWORK_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network.c a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network.c new file mode 100644 index 0000000..1be4cdc --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network.c @@ -0,0 +1,1025 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +int embb_mtapi_network_initialize() { +#ifdef _WIN32 + WORD ver_request; + WSADATA wsa_data; + int err; + + ver_request = MAKEWORD(2, 2); + + err = WSAStartup(ver_request, &wsa_data); + if (err != 0) { + return 0; + } else { + return 1; + } +#else + return 1; +#endif +} + +void embb_mtapi_network_finalize() { +#ifdef _WIN32 + WSACleanup(); +#endif +} + +enum embb_mtapi_network_operation_enum { + EMBB_MTAPI_NETWORK_START_TASK = 0x01AFFE01, + EMBB_MTAPI_NETWORK_RETURN_RESULT = 0x02AFFE02, + EMBB_MTAPI_NETWORK_RETURN_FAILURE = 0x03AFFE03, + EMBB_MTAPI_NETWORK_CANCEL_TASK = 0x04AFFE04 +}; + +struct embb_mtapi_network_plugin_struct { + embb_thread_t thread; + embb_mtapi_network_socket_t *sockets; // sockets[0] is the listening socket + int socket_count; + embb_atomic_int run; + mtapi_size_t buffer_size; + + embb_mutex_t send_mutex; + embb_mtapi_network_buffer_t send_buffer; + + embb_mtapi_network_buffer_t recv_buffer; +}; + +typedef struct embb_mtapi_network_plugin_struct embb_mtapi_network_plugin_t; + +static embb_mtapi_network_plugin_t embb_mtapi_network_plugin; + +struct embb_mtapi_network_action_struct { + mtapi_domain_t domain_id; + mtapi_job_id_t job_id; + + char const * host; + mtapi_uint16_t port; + embb_mtapi_network_socket_t socket; + + embb_mutex_t send_mutex; + embb_mtapi_network_buffer_t send_buffer; +}; + +typedef struct embb_mtapi_network_action_struct embb_mtapi_network_action_t; + +struct embb_mtapi_network_task_struct { + embb_mtapi_network_socket_t socket; + int32_t remote_task_id; + int32_t remote_task_tag; +}; + +typedef struct embb_mtapi_network_task_struct embb_mtapi_network_task_t; + +static void embb_mtapi_network_return_failure( + int32_t remote_task_id, + int32_t remote_task_tag, + mtapi_status_t status, + embb_mtapi_network_socket_t * socket, + embb_mtapi_network_buffer_t * buffer) { + embb_mtapi_network_buffer_clear(buffer); + + // packet size + embb_mtapi_network_buffer_push_back_int32( + buffer, 16); + + // operation + embb_mtapi_network_buffer_push_back_int32( + buffer, EMBB_MTAPI_NETWORK_RETURN_FAILURE); + + // task handle + embb_mtapi_network_buffer_push_back_int32( + buffer, remote_task_id); + embb_mtapi_network_buffer_push_back_int32( + buffer, remote_task_tag); + + // status + embb_mtapi_network_buffer_push_back_int32( + buffer, (int32_t)status); + + embb_mtapi_network_socket_sendbuffer( + socket, buffer); +} + +static void embb_mtapi_network_task_complete( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + /* not needed right now + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action);*/ + + embb_mtapi_network_plugin_t * plugin = &embb_mtapi_network_plugin; + embb_mtapi_network_task_t * network_task = + (embb_mtapi_network_task_t*)local_task->attributes.user_data; + embb_mtapi_network_buffer_t * send_buf = &plugin->send_buffer; + + embb_atomic_memory_barrier(); + local_task->attributes.complete_func = NULL; + embb_atomic_memory_barrier(); + + // serialize sending of results + embb_mutex_lock(&plugin->send_mutex); + embb_mtapi_network_buffer_clear(send_buf); + + if (local_task->error_code == MTAPI_SUCCESS) { + // actual counts bytes actually put into the buffer + int actual = 0; + // expected counts bytes we intended to put into the buffer + int expected = + 4 + // operation + 4 + 4 + // remote task handle + 4 + // status + 4 + (int)local_task->result_size; // result buffer + + // packet size + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, expected); + expected += 4; + + // operation is "return result" + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, EMBB_MTAPI_NETWORK_RETURN_RESULT); + + // remote task id + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, network_task->remote_task_id); + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, network_task->remote_task_tag); + + // status + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, local_task->error_code); + + // result size + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->result_size); + actual += embb_mtapi_network_buffer_push_back_rawdata( + send_buf, (int32_t)local_task->result_size, + local_task->result_buffer); + + if (expected == actual) { + int sent = embb_mtapi_network_socket_sendbuffer( + &network_task->socket, send_buf); + assert(sent == send_buf->size); + EMBB_UNUSED_IN_RELEASE(sent); + } else { + embb_mtapi_network_return_failure( + network_task->remote_task_id, + network_task->remote_task_tag, + MTAPI_ERR_UNKNOWN, + &network_task->socket, send_buf); + } + } else { + embb_mtapi_network_return_failure( + network_task->remote_task_id, + network_task->remote_task_tag, + local_task->error_code, + &network_task->socket, send_buf); + } + + // sending done + embb_mutex_unlock(&plugin->send_mutex); + + // we allocated arguments and results on receive, so free them here + embb_free((void*)local_task->arguments); + embb_free(local_task->result_buffer); + + void * data = local_task->attributes.user_data; + + embb_atomic_memory_barrier(); + local_task->attributes.user_data = NULL; + embb_atomic_memory_barrier(); + + embb_free(data); + + local_status = MTAPI_SUCCESS; + } + } + } + + mtapi_status_set(status, local_status); +} + +static mtapi_status_t embb_mtapi_network_handle_start_task( + embb_mtapi_network_socket_t * socket, + embb_mtapi_network_buffer_t * buffer, + int packet_size) { + int32_t domain_id; + int32_t job_id; + int32_t results_size; + void * results; + int err; + EMBB_UNUSED_IN_RELEASE(err); + + int32_t arguments_size; + int32_t remote_task_id; + int32_t remote_task_tag; + mtapi_uint_t priority = 0; + mtapi_job_hndl_t job_hndl; + mtapi_task_attributes_t task_attr; + void * arguments; + mtapi_task_complete_function_t func = embb_mtapi_network_task_complete; + void * func_void; + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + // check if we have at least 28 bytes + if (packet_size >= 28) { + // domain id + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &domain_id); + assert(err == 4); + // job id + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &job_id); + assert(err == 4); + // priority + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, (int32_t*)&priority); + assert(err == 4); + // remote task handle + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &remote_task_id); + assert(err == 4); + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &remote_task_tag); + assert(err == 4); + // result size + err = embb_mtapi_network_buffer_pop_front_int32(buffer, + &results_size); + assert(err == 4); + // arguments size + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &arguments_size); + assert(err == 4); + + embb_mtapi_network_task_t * network_task = + (embb_mtapi_network_task_t*)embb_alloc( + sizeof(embb_mtapi_network_task_t)); + + if (network_task == NULL) { + embb_mtapi_network_return_failure( + remote_task_id, remote_task_tag, MTAPI_ERR_UNKNOWN, + socket, buffer); + return MTAPI_ERR_UNKNOWN; + } + network_task->remote_task_id = remote_task_id; + network_task->remote_task_tag = remote_task_tag; + + // check packet_size again + if (packet_size == 28 + arguments_size) { + // allocate buffers + results = embb_alloc((size_t)results_size); + if (results == NULL) { + embb_mtapi_network_return_failure( + remote_task_id, remote_task_tag, MTAPI_ERR_UNKNOWN, + socket, buffer); + return MTAPI_ERR_UNKNOWN; + } + arguments = embb_alloc((size_t)arguments_size); + if (arguments == NULL) { + embb_free(results); + embb_mtapi_network_return_failure( + remote_task_id, remote_task_tag, MTAPI_ERR_UNKNOWN, + socket, buffer); + return MTAPI_ERR_UNKNOWN; + } + + // arguments + err = embb_mtapi_network_buffer_pop_front_rawdata( + buffer, arguments_size, arguments); + assert(err == arguments_size); + + network_task->socket = *socket; + mtapi_taskattr_init(&task_attr, &local_status); + assert(local_status == MTAPI_SUCCESS); + mtapi_taskattr_set(&task_attr, MTAPI_TASK_USER_DATA, + (void*)network_task, 0, &local_status); + assert(local_status == MTAPI_SUCCESS); + mtapi_boolean_t task_detached = MTAPI_TRUE; + mtapi_taskattr_set(&task_attr, MTAPI_TASK_DETACHED, + (void*)&task_detached, sizeof(mtapi_boolean_t), &local_status); + assert(local_status == MTAPI_SUCCESS); + mtapi_taskattr_set(&task_attr, MTAPI_TASK_PRIORITY, + (void*)&priority, sizeof(mtapi_uint_t), &local_status); + assert(local_status == MTAPI_SUCCESS); + memcpy(&func_void, &func, sizeof(void*)); + mtapi_taskattr_set(&task_attr, MTAPI_TASK_COMPLETE_FUNCTION, + func_void, 0, &local_status); + assert(local_status == MTAPI_SUCCESS); + job_hndl = mtapi_job_get((mtapi_job_id_t)job_id, + (mtapi_domain_t)domain_id, &local_status); + if (local_status == MTAPI_SUCCESS) { + mtapi_task_start( + MTAPI_TASK_ID_NONE, job_hndl, + arguments, (mtapi_size_t)arguments_size, + results, (mtapi_size_t)results_size, + &task_attr, MTAPI_GROUP_NONE, + &local_status); + } + if (local_status != MTAPI_SUCCESS) { + embb_free(arguments); + embb_free(results); + embb_mtapi_network_return_failure( + remote_task_id, remote_task_tag, local_status, socket, buffer); + } + } + } + + return local_status; +} + +static mtapi_status_t embb_mtapi_network_handle_return_result( + embb_mtapi_network_buffer_t * buffer, + int packet_size) { + int32_t task_status; + int32_t task_id; + int32_t task_tag; + + int32_t results_size; + int err; + EMBB_UNUSED_IN_RELEASE(err); + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + mtapi_task_hndl_t task; + + // do we have at least 16 bytes? + if (packet_size >= 16) { + // local task id + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &task_id); + assert(err == 4); + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &task_tag); + assert(err == 4); + // task status + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &task_status); + assert(err == 4); + // result size + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &results_size); + assert(err == 4); + + // check packet_size again + if (packet_size == 16 + results_size) { + task.id = (mtapi_task_id_t)task_id; + task.tag = (mtapi_uint_t)task_tag; + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle( + node->task_pool, task); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action); + + /* not needed right now + embb_mtapi_network_action_t * network_action = + (embb_mtapi_network_action_t*)local_action->plugin_data;*/ + + err = embb_mtapi_network_buffer_pop_front_rawdata( + buffer, results_size, local_task->result_buffer); + assert(err == results_size); + + local_task->error_code = (mtapi_status_t)task_status; + embb_atomic_store_int(&local_task->state, MTAPI_TASK_COMPLETED); + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + + /* is task associated with a group? */ + if (embb_mtapi_group_pool_is_handle_valid( + node->group_pool, local_task->group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, local_task->group); + embb_mtapi_task_queue_push(&local_group->queue, local_task); + } + + local_status = MTAPI_SUCCESS; + } + } + } + } + } + + return local_status; +} + +static mtapi_status_t embb_mtapi_network_handle_return_failure( + embb_mtapi_network_buffer_t * buffer, + int packet_size) { + int32_t task_status; + int32_t task_id; + int32_t task_tag; + + int err; + EMBB_UNUSED_IN_RELEASE(err); + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + mtapi_task_hndl_t task; + + // do we have 12 bytes? + if (packet_size == 12) { + // local task id + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &task_id); + assert(err == 4); + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &task_tag); + assert(err == 4); + // task status + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &task_status); + assert(err == 4); + + task.id = (mtapi_task_id_t)task_id; + task.tag = (mtapi_uint_t)task_tag; + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle( + node->task_pool, task); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action); + + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + local_task->error_code = (mtapi_status_t)task_status; + if (MTAPI_ERR_ACTION_CANCELLED == task_status) { + embb_atomic_store_int(&local_task->state, MTAPI_TASK_CANCELLED); + } else { + embb_atomic_store_int(&local_task->state, MTAPI_TASK_ERROR); + } + + /* is task associated with a group? */ + if (embb_mtapi_group_pool_is_handle_valid( + node->group_pool, local_task->group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, local_task->group); + embb_mtapi_task_queue_push(&local_group->queue, local_task); + } + + local_status = MTAPI_SUCCESS; + } + } + } + } + + return local_status; +} + +static mtapi_status_t embb_mtapi_network_handle_cancel_task( + embb_mtapi_network_buffer_t * buffer, + int packet_size) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + int32_t remote_task_id; + int32_t remote_task_tag; + int err; + EMBB_UNUSED_IN_RELEASE(err); + + // do we have 8 bytes? + if (packet_size == 8) { + // get task handle + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &remote_task_id); + assert(err == 4); + err = embb_mtapi_network_buffer_pop_front_int32(buffer, &remote_task_tag); + assert(err == 4); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + // search for task to cancel + for (mtapi_uint_t ii = 0; ii < node->attributes.max_tasks; ii++) { + embb_mtapi_task_t * task = &node->task_pool->storage[ii]; + // is this our task? + if (embb_mtapi_network_task_complete == + task->attributes.complete_func) { + embb_mtapi_network_task_t * network_task = + (embb_mtapi_network_task_t*)task->attributes.user_data; + // is this task the one matching the given remote task? + if (remote_task_id == network_task->remote_task_id && + remote_task_tag == network_task->remote_task_tag) { + mtapi_task_cancel(task->handle, &local_status); + break; + } + } + } + } + } + + return local_status; +} + +static int embb_mtapi_network_thread(void * args) { + embb_mtapi_network_plugin_t * plugin = &embb_mtapi_network_plugin; + embb_mtapi_network_buffer_t * buffer = &plugin->recv_buffer; + int err; + + EMBB_UNUSED(args); + + while (embb_atomic_load_int(&plugin->run)) { + err = embb_mtapi_network_socket_select( + plugin->sockets, plugin->socket_count, 100); + if (0 == err) { + // listening socket, accept connection + embb_mtapi_network_socket_t accept_socket; + err = embb_mtapi_network_socket_accept( + &plugin->sockets[0], &accept_socket); + if (0 < err) { + // add socket to socket list + plugin->sockets[plugin->socket_count] = accept_socket; + plugin->socket_count++; + } + } else if (0 < err) { + int32_t operation; + int32_t packet_size; + embb_mtapi_network_socket_t * socket = &plugin->sockets[err]; + + embb_mtapi_network_buffer_clear(buffer); + + err = embb_mtapi_network_socket_recvbuffer_sized( + socket, buffer, 4); + if (err == 4) { + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &packet_size); + assert(err == 4); + + embb_mtapi_network_buffer_clear(buffer); + err = embb_mtapi_network_socket_recvbuffer_sized( + socket, buffer, packet_size); + if (err == packet_size) { + err = embb_mtapi_network_buffer_pop_front_int32( + buffer, &operation); + assert(err == 4); + packet_size -= 4; + + switch (operation) { + case EMBB_MTAPI_NETWORK_START_TASK: + embb_mtapi_network_handle_start_task(socket, buffer, packet_size); + break; + case EMBB_MTAPI_NETWORK_RETURN_RESULT: + embb_mtapi_network_handle_return_result(buffer, packet_size); + break; + case EMBB_MTAPI_NETWORK_RETURN_FAILURE: + embb_mtapi_network_handle_return_failure(buffer, packet_size); + break; + case EMBB_MTAPI_NETWORK_CANCEL_TASK: + embb_mtapi_network_handle_cancel_task(buffer, packet_size); + break; + default: + // invalid, ignore + break; + } + } + } + + embb_mtapi_network_buffer_clear(buffer); + } + } + + return EMBB_SUCCESS; +} + +void mtapi_network_plugin_initialize( + MTAPI_IN char * host, + MTAPI_IN mtapi_uint16_t port, + MTAPI_IN mtapi_uint16_t max_connections, + MTAPI_IN mtapi_size_t buffer_size, + MTAPI_OUT mtapi_status_t* status) { + embb_mtapi_network_plugin_t * plugin = &embb_mtapi_network_plugin; + int err; + + mtapi_status_set(status, MTAPI_ERR_UNKNOWN); + + plugin->socket_count = 0; + plugin->buffer_size = 0; + plugin->sockets = NULL; + embb_atomic_store_int(&plugin->run, 0); + + err = embb_mtapi_network_initialize(); + if (0 == err) return; + + err = embb_mtapi_network_buffer_initialize( + &plugin->recv_buffer, (int)buffer_size); + if (0 == err) { + embb_mtapi_network_finalize(); + return; + } + + err = embb_mtapi_network_buffer_initialize( + &plugin->send_buffer, (int)buffer_size); + if (0 == err) { + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + embb_mtapi_network_finalize(); + return; + } + + plugin->buffer_size = buffer_size; + + // 1 listening socket and max_connections connections + // (2 sockets each if local) + plugin->sockets = (embb_mtapi_network_socket_t*)embb_alloc( + sizeof(embb_mtapi_network_socket_t) * (1 + max_connections * 2)); + if (NULL == plugin->sockets) { + embb_mtapi_network_buffer_finalize(&plugin->send_buffer); + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + plugin->buffer_size = 0; + embb_mtapi_network_finalize(); + return; + } + + err = embb_mutex_init(&plugin->send_mutex, 0); + if (EMBB_SUCCESS != err) { + embb_free(plugin->sockets); + plugin->sockets = NULL; + embb_mtapi_network_buffer_finalize(&plugin->send_buffer); + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + plugin->buffer_size = 0; + embb_mtapi_network_finalize(); + return; + } + + err = embb_mtapi_network_socket_initialize(&plugin->sockets[0]); + if (0 == err) { + embb_mutex_destroy(&plugin->send_mutex); + embb_free(plugin->sockets); + plugin->sockets = NULL; + embb_mtapi_network_buffer_finalize(&plugin->send_buffer); + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + plugin->buffer_size = 0; + embb_mtapi_network_finalize(); + return; + } + plugin->socket_count = 1; + + err = embb_mtapi_network_socket_bind_and_listen( + &plugin->sockets[0], host, port, max_connections); + if (0 == err) { + embb_mtapi_network_socket_finalize(&plugin->sockets[0]); + plugin->socket_count = 0; + embb_mutex_destroy(&plugin->send_mutex); + embb_free(plugin->sockets); + plugin->sockets = NULL; + embb_mtapi_network_buffer_finalize(&plugin->send_buffer); + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + plugin->buffer_size = 0; + embb_mtapi_network_finalize(); + return; + } + + embb_atomic_store_int(&plugin->run, 1); + + err = embb_thread_create( + &plugin->thread, NULL, embb_mtapi_network_thread, NULL); + if (EMBB_SUCCESS != err) { + embb_atomic_store_int(&plugin->run, 0); + embb_mtapi_network_socket_finalize(&plugin->sockets[0]); + plugin->socket_count = 0; + embb_mutex_destroy(&plugin->send_mutex); + embb_free(plugin->sockets); + plugin->sockets = NULL; + embb_mtapi_network_buffer_finalize(&plugin->send_buffer); + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + plugin->buffer_size = 0; + embb_mtapi_network_finalize(); + return; + } + + mtapi_status_set(status, MTAPI_SUCCESS); +} + +void mtapi_network_plugin_finalize( + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_SUCCESS; + embb_mtapi_network_plugin_t * plugin = &embb_mtapi_network_plugin; + int err; + + embb_atomic_store_int(&plugin->run, 0); + embb_thread_join(&plugin->thread, &err); + + embb_mutex_destroy(&plugin->send_mutex); + embb_mtapi_network_buffer_finalize(&plugin->send_buffer); + + embb_mtapi_network_buffer_finalize(&plugin->recv_buffer); + + embb_mtapi_network_socket_finalize(&plugin->sockets[0]); + embb_free(plugin->sockets); + embb_mtapi_network_finalize(); + + mtapi_status_set(status, local_status); +} + +static void network_task_start( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + // assume failure + mtapi_status_set(status, MTAPI_ERR_UNKNOWN); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action); + + embb_mtapi_network_action_t * network_action = + (embb_mtapi_network_action_t*)local_action->plugin_data; + embb_mtapi_network_buffer_t * send_buf = &network_action->send_buffer; + + // serialize sending + embb_mutex_lock(&network_action->send_mutex); + embb_mtapi_network_buffer_clear(send_buf); + + // actual counts bytes actually put into the buffer + int actual = 0; + // expected counts bytes we intended to put into the buffer + int expected = + 4 + // operation + 4 + // domain_id + 4 + // job_id + 4 + // priority + 4 + 4 + // task handle + 4 + // result_size + 4 + local_task->arguments_size; // arguments buffer + + // packet size + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)expected); + expected += 4; + + // operation is "start task" + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, EMBB_MTAPI_NETWORK_START_TASK); + + // domain_id + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)network_action->domain_id); + + // job_id + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)network_action->job_id); + + // priority + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->attributes.priority); + + // task handle + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->handle.id); + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->handle.tag); + + // result size + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->result_size); + + // arguments buffer + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->arguments_size); + actual += embb_mtapi_network_buffer_push_back_rawdata( + send_buf, (int32_t)local_task->arguments_size, local_task->arguments); + + // check if everything fit into the buffer + if (actual == expected) { + embb_atomic_fetch_and_add_int(&local_action->num_tasks, 1); + embb_atomic_store_int(&local_task->state, MTAPI_TASK_RUNNING); + int sent = embb_mtapi_network_socket_sendbuffer( + &network_action->socket, send_buf); + // was everything sent? + if (sent == send_buf->size) { + // we've done it, success! + mtapi_status_set(status, MTAPI_SUCCESS); + } else { + // could not send the whole task, this will fail on the remote side, + // so we can safely assume that the task is in error + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + embb_atomic_store_int(&local_task->state, MTAPI_TASK_ERROR); + } + } + + embb_mtapi_network_buffer_clear(send_buf); + embb_mutex_unlock(&network_action->send_mutex); + } + } + } +} + +static void network_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + // assume failure + mtapi_status_set(status, MTAPI_ERR_UNKNOWN); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action); + + embb_mtapi_network_action_t * network_action = + (embb_mtapi_network_action_t*)local_action->plugin_data; + embb_mtapi_network_buffer_t * send_buf = &network_action->send_buffer; + + // serialize sending + embb_mutex_lock(&network_action->send_mutex); + embb_mtapi_network_buffer_clear(send_buf); + + // actual counts bytes actually put into the buffer + int actual = 0; + // expected counts bytes we intended to put into the buffer + int expected = + 4 + // operation + 4 + 4; // task handle + + // packet size + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)expected); + expected += 4; + + // operation is "cancel task" + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, EMBB_MTAPI_NETWORK_CANCEL_TASK); + + // task handle + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->handle.id); + actual += embb_mtapi_network_buffer_push_back_int32( + send_buf, (int32_t)local_task->handle.tag); + + // check if everything fit into the buffer + if (actual == expected) { + int sent = embb_mtapi_network_socket_sendbuffer( + &network_action->socket, send_buf); + // was everything sent? + if (sent == send_buf->size) { + // we've done it, success! + mtapi_status_set(status, MTAPI_SUCCESS); + } else { + embb_atomic_store_int(&local_task->state, MTAPI_TASK_ERROR); + } + } else { + embb_atomic_store_int(&local_task->state, MTAPI_TASK_ERROR); + } + + embb_mtapi_network_buffer_clear(send_buf); + embb_mutex_unlock(&network_action->send_mutex); + } + } + } +} + +static void network_action_finalize( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + embb_mtapi_network_action_t * network_action = + (embb_mtapi_network_action_t *)local_action->plugin_data; + + embb_mutex_destroy(&network_action->send_mutex); + embb_mtapi_network_buffer_finalize(&network_action->send_buffer); + embb_mtapi_network_socket_finalize(&network_action->socket); + + embb_free(network_action); + local_status = MTAPI_SUCCESS; + } + } + + mtapi_status_set(status, local_status); +} + +mtapi_action_hndl_t mtapi_network_action_create( + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_IN mtapi_job_id_t local_job_id, + MTAPI_IN mtapi_job_id_t remote_job_id, + MTAPI_IN char * host, + MTAPI_IN mtapi_uint16_t port, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_network_plugin_t * plugin = &embb_mtapi_network_plugin; + embb_mtapi_network_action_t * action = + (embb_mtapi_network_action_t*)embb_alloc( + sizeof(embb_mtapi_network_action_t)); + mtapi_action_hndl_t action_hndl = { 0, 0 }; + int err; + + if (NULL != action) { + action->domain_id = domain_id; + action->job_id = remote_job_id; + + err = embb_mtapi_network_buffer_initialize( + &action->send_buffer, (int)plugin->buffer_size); + if (0 != err) { + err = embb_mutex_init(&action->send_mutex, 0); + if (EMBB_SUCCESS == err) { + action->host = host; + action->port = port; + embb_mtapi_network_socket_initialize(&action->socket); + err = embb_mtapi_network_socket_connect(&action->socket, host, port); + if (0 != err) { + // store socket for select + plugin->sockets[plugin->socket_count] = action->socket; + plugin->socket_count++; + + action_hndl = mtapi_ext_plugin_action_create( + local_job_id, + network_task_start, + network_task_cancel, + network_action_finalize, + action, + NULL, 0, // no node local data obviously + MTAPI_NULL, + &local_status); + } else { + embb_mutex_destroy(&action->send_mutex); + embb_mtapi_network_buffer_finalize(&action->send_buffer); + embb_mtapi_network_socket_finalize(&action->socket); + embb_free(action); + } + } + } + } + + mtapi_status_set(status, local_status); + return action_hndl; +} diff --git b/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network.h a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network.h new file mode 100644 index 0000000..4bc5b12 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_H_ +#define MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +int embb_mtapi_network_initialize(); + +void embb_mtapi_network_finalize(); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_buffer.c a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_buffer.c new file mode 100644 index 0000000..61b0482 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_buffer.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +int embb_mtapi_network_buffer_initialize( + embb_mtapi_network_buffer_t * that, + int capacity) { + int result = 1; + that->position = 0; + that->size = 0; + that->data = (char*)embb_alloc((size_t)capacity); + if (NULL != that->data) { + that->capacity = capacity; + } else { + that->capacity = 0; + result = 0; + } + return result; +} + +void embb_mtapi_network_buffer_finalize( + embb_mtapi_network_buffer_t * that) { + that->position = 0; + that->size = 0; + that->capacity = 0; + if (NULL != that->data) { + embb_free(that->data); + that->data = NULL; + } +} + +void embb_mtapi_network_buffer_clear( + embb_mtapi_network_buffer_t * that) { + that->position = 0; + that->size = 0; +} + +int embb_mtapi_network_buffer_push_back_int8( + embb_mtapi_network_buffer_t * that, + int8_t value) { + if (that->size + 1 > that->capacity) { + return 0; + } + memcpy(that->data + that->size, &value, 1); + that->size += 1; + return 1; +} + +int embb_mtapi_network_buffer_push_back_int16( + embb_mtapi_network_buffer_t * that, + int16_t value) { + if (that->size + 2 > that->capacity) { + return 0; + } + memcpy(that->data + that->size, &value, 2); + that->size += 2; + return 2; +} + +int embb_mtapi_network_buffer_push_back_int32( + embb_mtapi_network_buffer_t * that, + int32_t value) { + if (that->size + 4 > that->capacity) { + return 0; + } + memcpy(that->data + that->size, &value, 4); + that->size += 4; + return 4; +} + +int embb_mtapi_network_buffer_push_back_rawdata( + embb_mtapi_network_buffer_t * that, + int32_t size, + void const * rawdata) { + if (that->size + size > that->capacity) { + return 0; + } + memcpy(that->data + that->size, rawdata, (size_t)size); + that->size += size; + return size; +} + +int embb_mtapi_network_buffer_pop_front_int8( + embb_mtapi_network_buffer_t * that, + int8_t * value) { + if (that->position + 1 > that->size) { + *value = 0; + return 0; + } + memcpy(value, that->data + that->position, 1); + that->position += 1; + return 1; +} + +int embb_mtapi_network_buffer_pop_front_int16( + embb_mtapi_network_buffer_t * that, + int16_t * value) { + if (that->position + 2 > that->size) { + *value = 0; + return 0; + } + memcpy(value, that->data + that->position, 2); + that->position += 2; + return 2; +} + +int embb_mtapi_network_buffer_pop_front_int32( + embb_mtapi_network_buffer_t * that, + int32_t * value) { + if (that->position + 4 > that->size) { + *value = 0; + return 0; + } + memcpy(value, that->data + that->position, 4); + that->position += 4; + return 4; +} + +int embb_mtapi_network_buffer_pop_front_rawdata( + embb_mtapi_network_buffer_t * that, + int32_t size, + void * rawdata) { + if (that->position + size > that->size) { + memset(rawdata, 0, (size_t)size); + return 0; + } + memcpy(rawdata, that->data + that->position, (size_t)size); + that->position += size; + return size; +} diff --git b/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_buffer.h a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_buffer.h new file mode 100644 index 0000000..f4a6c11 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_buffer.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_BUFFER_H_ +#define MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_BUFFER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct embb_mtapi_network_buffer_struct { + int position; + int size; + int capacity; + char * data; +}; + +typedef struct embb_mtapi_network_buffer_struct embb_mtapi_network_buffer_t; + +int embb_mtapi_network_buffer_initialize( + embb_mtapi_network_buffer_t * that, + int capacity +); + +void embb_mtapi_network_buffer_finalize( + embb_mtapi_network_buffer_t * that +); + +void embb_mtapi_network_buffer_clear( + embb_mtapi_network_buffer_t * that +); + +int embb_mtapi_network_buffer_push_back_int8( + embb_mtapi_network_buffer_t * that, + int8_t value +); + +int embb_mtapi_network_buffer_push_back_int16( + embb_mtapi_network_buffer_t * that, + int16_t value +); + +int embb_mtapi_network_buffer_push_back_int32( + embb_mtapi_network_buffer_t * that, + int32_t value +); + +int embb_mtapi_network_buffer_push_back_rawdata( + embb_mtapi_network_buffer_t * that, + int32_t size, + void const * rawdata +); + +int embb_mtapi_network_buffer_pop_front_int8( + embb_mtapi_network_buffer_t * that, + int8_t * value +); + +int embb_mtapi_network_buffer_pop_front_int16( + embb_mtapi_network_buffer_t * that, + int16_t * value +); + +int embb_mtapi_network_buffer_pop_front_int32( + embb_mtapi_network_buffer_t * that, + int32_t * value +); + +int embb_mtapi_network_buffer_pop_front_rawdata( + embb_mtapi_network_buffer_t * that, + int32_t size, + void * rawdata +); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_BUFFER_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_socket.c a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_socket.c new file mode 100644 index 0000000..bcb46ea --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_socket.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#include +#include +#include +#ifdef _WIN32 +#include +#else +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#include +#include +#include +#include +#include +#include +#include +#endif + +int embb_mtapi_network_socket_initialize( + embb_mtapi_network_socket_t * that) { + that->handle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (INVALID_SOCKET == that->handle) { + return 0; + } else { + return 1; + } +} + +void embb_mtapi_network_socket_finalize( + embb_mtapi_network_socket_t * that) { + if (INVALID_SOCKET != that->handle) { +#ifdef _WIN32 + closesocket(that->handle); +#else + close(that->handle); +#endif + that->handle = INVALID_SOCKET; + } +} + +int embb_mtapi_network_socket_bind_and_listen( + embb_mtapi_network_socket_t * that, + char const * host, + uint16_t port, + uint16_t max_connections) { + struct sockaddr_in in_addr; + + // bind & listen + memset(&in_addr, 0, sizeof(in_addr)); + in_addr.sin_family = AF_INET; + in_addr.sin_addr.s_addr = (NULL == host) ? + htonl(INADDR_ANY) : inet_addr(host); + in_addr.sin_port = htons(port); + + if (SOCKET_ERROR == bind(that->handle, (struct sockaddr *) &in_addr, + sizeof(in_addr))) { + return 0; + } + + if (SOCKET_ERROR == listen(that->handle, max_connections)) { + return 0; + } + + return 1; +} + +int embb_mtapi_network_socket_accept( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_socket_t * sock) { + sock->handle = accept(that->handle, NULL, NULL); + if (INVALID_SOCKET == sock->handle) { + return 0; + } else { + return 1; + } +} + +int embb_mtapi_network_socket_connect( + embb_mtapi_network_socket_t * that, + const char * host, + uint16_t port) { + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(host); + addr.sin_port = htons(port); + + if (SOCKET_ERROR == connect(that->handle, (struct sockaddr *)&addr, + sizeof(addr))) { +#ifdef _WIN32 + int err = WSAGetLastError(); + if (WSAEWOULDBLOCK != err) +#else + if (EAGAIN != errno) +#endif + return 0; + } + + return 1; +} + +int embb_mtapi_network_socket_select( + embb_mtapi_network_socket_t * sockets, + int count, + int timeout + ) { + fd_set read_set; + embb_mtapi_network_socket_t max_fd = { 0 }; + int err; + int ii; + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = timeout % 1000; + + FD_ZERO(&read_set); + for (ii = 0; ii < count; ii++) { +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable: 4548) +#endif + FD_SET(sockets[ii].handle, &read_set); +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + if (sockets[ii].handle > max_fd.handle) + max_fd.handle = sockets[ii].handle; + } + + if (timeout >= 0) { + err = select((int)max_fd.handle + 1, &read_set, NULL, NULL, &tv); + } else { + err = select((int)max_fd.handle + 1, &read_set, NULL, NULL, NULL); + } + if (0 == err) { + // timeout + return -1; + } + if (SOCKET_ERROR == err) { + return -1; + } + + for (ii = 0; ii < count; ii++) { + if (FD_ISSET(sockets[ii].handle, &read_set)) { + return ii; + } + } + + return -1; +} + +int embb_mtapi_network_socket_sendbuffer( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_buffer_t * buffer) { + char * buf = (char*)(buffer->data); + int cnt = 0; + int result = send(that->handle, buf, buffer->size, 0); + while (result > 0) { + buf += result; + cnt += result; + if (cnt == buffer->size) + break; + result = send(that->handle, buf, buffer->size - cnt, 0); + } + if (cnt == buffer->size) { + return buffer->size; + } else { + return 0; + } +} + +int embb_mtapi_network_socket_recvbuffer_sized( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_buffer_t * buffer, + int size) { + int err; + /* +#ifdef _WIN32 + u_long bytes_available = 0; + if (0 != ioctlsocket(that->handle, FIONREAD, &bytes_available)) +#else + int bytes_available = 0; + if (0 != ioctl(that->handle, FIONREAD, &bytes_available)) +#endif + return 0; + */ + if (buffer->capacity < size) + return 0; + /* + if (size > (int)bytes_available) + return 0; + */ + if (0 < size) { + char * buf = (char*)(buffer->data); + int cnt = 0; + err = recv(that->handle, buf, size, 0); + while (err > 0) { + cnt += err; + if (cnt == size) + break; + buf += err; + err = recv(that->handle, buf, size - cnt, 0); + } + if (err > 0) + err = cnt; + } else { + err = 0; + } + if (err != size) + return 0; + buffer->size = size; + return buffer->size; +} + +int embb_mtapi_network_socket_recvbuffer( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_buffer_t * buffer) { + return + embb_mtapi_network_socket_recvbuffer_sized(that, buffer, buffer->capacity); +} diff --git b/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_socket.h a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_socket.h new file mode 100644 index 0000000..db568bf --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/src/embb_mtapi_network_socket.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_SOCKET_H_ +#define MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_SOCKET_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct embb_mtapi_network_socket_struct { +#ifdef _WIN32 +# ifdef _WIN64 + uint64_t handle; +# else + uint32_t handle; +# endif +#else + int handle; +#endif +}; + +typedef struct embb_mtapi_network_socket_struct embb_mtapi_network_socket_t; + +int embb_mtapi_network_socket_initialize( + embb_mtapi_network_socket_t * that +); + +void embb_mtapi_network_socket_finalize( + embb_mtapi_network_socket_t * that +); + +int embb_mtapi_network_socket_bind_and_listen( + embb_mtapi_network_socket_t * that, + char const * host, + uint16_t port, + uint16_t max_connections +); + +int embb_mtapi_network_socket_accept( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_socket_t * sock +); + +int embb_mtapi_network_socket_connect( + embb_mtapi_network_socket_t * that, + const char * host, + uint16_t port +); + +int embb_mtapi_network_socket_select( + embb_mtapi_network_socket_t * sockets, + int count, + int timeout +); + +int embb_mtapi_network_socket_sendbuffer( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_buffer_t * buffer +); + +int embb_mtapi_network_socket_recvbuffer( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_buffer_t * buffer +); + +int embb_mtapi_network_socket_recvbuffer_sized( + embb_mtapi_network_socket_t * that, + embb_mtapi_network_buffer_t * buffer, + int size +); + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_PLUGINS_C_MTAPI_NETWORK_C_SRC_EMBB_MTAPI_NETWORK_SOCKET_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_buffer.cc a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_buffer.cc new file mode 100644 index 0000000..bee2217 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_buffer.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + + +NetworkBufferTest::NetworkBufferTest() { + CreateUnit("mtapi network buffer test").Add( + &NetworkBufferTest::TestBasic, this); +} + +void NetworkBufferTest::TestBasic() { + embb_mtapi_network_buffer_t buffer; + int err; + + embb_mtapi_network_buffer_initialize(&buffer, 1024); + + err = embb_mtapi_network_buffer_push_back_int8(&buffer, -1); + PT_EXPECT(err == 1); + err = embb_mtapi_network_buffer_push_back_int16(&buffer, -2); + PT_EXPECT(err == 2); + + int8_t val8 = 0; + err = embb_mtapi_network_buffer_pop_front_int8(&buffer, &val8); + PT_EXPECT(err == 1); + PT_EXPECT(val8 == -1); + + int32_t val32 = 0; + err = embb_mtapi_network_buffer_pop_front_int32(&buffer, &val32); + PT_EXPECT(err == 0); + PT_EXPECT(val32 == 0); + + int16_t val16 = 0; + err = embb_mtapi_network_buffer_pop_front_int16(&buffer, &val16); + PT_EXPECT(err == 2); + PT_EXPECT(val16 == -2); + + embb_mtapi_network_buffer_finalize(&buffer); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_buffer.h a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_buffer.h new file mode 100644 index 0000000..4c7769d --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_buffer.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_BUFFER_H_ +#define MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_BUFFER_H_ + +#include + +class NetworkBufferTest : public partest::TestCase { + public: + NetworkBufferTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_BUFFER_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_socket.cc a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_socket.cc new file mode 100644 index 0000000..6b31bc4 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_socket.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +NetworkSocketTest::NetworkSocketTest() { + CreateUnit("mtapi network socket test").Add( + &NetworkSocketTest::TestBasic, this); +} + +void NetworkSocketTest::TestBasic() { + int err; + embb_mtapi_network_socket_t server_sock; + embb_mtapi_network_socket_t accept_sock; + embb_mtapi_network_socket_t client_sock; + embb_mtapi_network_buffer_t send_buffer; + embb_mtapi_network_buffer_t recv_buffer; + + embb_mtapi_network_buffer_initialize(&send_buffer, 4); + embb_mtapi_network_buffer_initialize(&recv_buffer, 4); + + err = embb_mtapi_network_initialize(); + PT_EXPECT(err != 0); + + err = embb_mtapi_network_socket_initialize(&server_sock); + PT_EXPECT(err != 0); + uint16_t port = 4700; + do { + port++; + err = embb_mtapi_network_socket_bind_and_listen( + &server_sock, "127.0.0.1", port, 5); + } while (err == 0 && port < 4800); + PT_EXPECT(err != 0); + + err = embb_mtapi_network_socket_select(&server_sock, 1, 1); + PT_EXPECT(err == -1); + + err = embb_mtapi_network_socket_initialize(&client_sock); + PT_EXPECT(err != 0); + err = embb_mtapi_network_socket_connect(&client_sock, "127.0.0.1", port); + PT_EXPECT(err != 0); + + err = embb_mtapi_network_socket_select(&server_sock, 1, -1); + PT_EXPECT(err == 0); + + err = embb_mtapi_network_socket_accept(&server_sock, &accept_sock); + PT_EXPECT(err != 0); + + err = embb_mtapi_network_socket_select(&accept_sock, 1, 1); + PT_EXPECT(err == -1); + + err = embb_mtapi_network_buffer_push_back_int32(&send_buffer, 0x12345678); + PT_EXPECT(err == 4); + err = embb_mtapi_network_socket_sendbuffer(&client_sock, &send_buffer); + PT_EXPECT(err == 4); + + err = embb_mtapi_network_socket_select(&accept_sock, 1, -1); + PT_EXPECT(err == 0); + err = embb_mtapi_network_socket_recvbuffer(&accept_sock, &recv_buffer); + PT_EXPECT(err == 4); + + int32_t result = 0; + err = embb_mtapi_network_buffer_pop_front_int32(&recv_buffer, &result); + PT_EXPECT(err == 4); + PT_EXPECT(result == 0x12345678); + + embb_mtapi_network_socket_finalize(&accept_sock); + embb_mtapi_network_socket_finalize(&client_sock); + embb_mtapi_network_socket_finalize(&server_sock); + + embb_mtapi_network_buffer_finalize(&recv_buffer); + embb_mtapi_network_buffer_finalize(&send_buffer); + + embb_mtapi_network_finalize(); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_socket.h a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_socket.h new file mode 100644 index 0000000..3c9ca02 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_socket.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_SOCKET_H_ +#define MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_SOCKET_H_ + +#include + +class NetworkSocketTest : public partest::TestCase { + public: + NetworkSocketTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_SOCKET_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_task.cc a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_task.cc new file mode 100644 index 0000000..be8c1a1 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_task.cc @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + + +#define MTAPI_CHECK_STATUS(status) PT_ASSERT(MTAPI_SUCCESS == status) + +#define NETWORK_DOMAIN 1 +#define NETWORK_LOCAL_NODE 3 +#define NETWORK_LOCAL_JOB 3 +#define NETWORK_REMOTE_NODE 3 +#define NETWORK_REMOTE_JOB 4 + + +static void test( + void const * arguments, + mtapi_size_t arguments_size, + void * result_buffer, + mtapi_size_t result_buffer_size, + void const * node_local_data, + mtapi_size_t node_local_data_size, + mtapi_task_context_t * context) { + EMBB_UNUSED(context); + EMBB_UNUSED(result_buffer_size); + EMBB_UNUSED(node_local_data_size); + int elements = static_cast(arguments_size / sizeof(float) / 2); + float const * a = reinterpret_cast(arguments); + float const * b = reinterpret_cast(arguments)+elements; + float * c = reinterpret_cast(result_buffer); + float const * d = reinterpret_cast(node_local_data); + for (int ii = 0; ii < elements; ii++) { + c[ii] = a[ii] + b[ii] + d[0]; + } +} + +static void cancel_test( + void const * /*arguments*/, + mtapi_size_t /*arguments_size*/, + void * /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + void const * /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * context) { + mtapi_status_t status; + while (true) { + mtapi_task_state_t state = mtapi_context_taskstate_get(context, &status); + if (status != MTAPI_SUCCESS) { + break; + } else { + if (state == MTAPI_TASK_CANCELLED) { + break; + } + } + } +} + +NetworkTaskTest::NetworkTaskTest() { + CreateUnit("mtapi network task test") + .Add(&NetworkTaskTest::TestBasic, this); +} + +void NetworkTaskTest::TestBasic() { + mtapi_status_t status; + + mtapi_initialize( + NETWORK_DOMAIN, + NETWORK_LOCAL_NODE, + MTAPI_NULL, + MTAPI_NULL, + &status); + MTAPI_CHECK_STATUS(status); + + TestSimple(); + TestCancel(); + + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); +} + +void NetworkTaskTest::TestSimple() { + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_action_hndl_t network_action, local_action; + + const int kElements = 64; + float arguments[kElements * 2]; + float results[kElements]; + + for (int ii = 0; ii < kElements; ii++) { + arguments[ii] = static_cast(ii); + arguments[ii + kElements] = static_cast(ii); + } + + mtapi_network_plugin_initialize("127.0.0.1", 12345, 5, + kElements * 4 * 3 + 32, &status); + MTAPI_CHECK_STATUS(status); + + float node_remote = 1.0f; + local_action = mtapi_action_create( + NETWORK_REMOTE_JOB, + test, + &node_remote, sizeof(float), + MTAPI_DEFAULT_ACTION_ATTRIBUTES, + &status); + MTAPI_CHECK_STATUS(status); + + network_action = mtapi_network_action_create( + NETWORK_DOMAIN, + NETWORK_LOCAL_JOB, + NETWORK_REMOTE_JOB, + "127.0.0.1", 12345, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(NETWORK_LOCAL_JOB, NETWORK_DOMAIN, &status); + MTAPI_CHECK_STATUS(status); + + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + arguments, kElements * 2 * sizeof(float), + results, kElements*sizeof(float), + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + for (int ii = 0; ii < kElements; ii++) { + PT_EXPECT_EQ(results[ii], ii * 2 + 1); + } + + mtapi_action_delete(network_action, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_action_delete(local_action, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_network_plugin_finalize(&status); + MTAPI_CHECK_STATUS(status); +} + +void NetworkTaskTest::TestCancel() { + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_action_hndl_t network_action, local_action; + + float argument = 1.0f; + float result; + + mtapi_network_plugin_initialize("127.0.0.1", 12346, 5, + 4 * 3 + 32, &status); + MTAPI_CHECK_STATUS(status); + + float node_remote = 1.0f; + local_action = mtapi_action_create( + NETWORK_REMOTE_JOB, + cancel_test, + &node_remote, sizeof(float), + MTAPI_DEFAULT_ACTION_ATTRIBUTES, + &status); + MTAPI_CHECK_STATUS(status); + + network_action = mtapi_network_action_create( + NETWORK_DOMAIN, + NETWORK_LOCAL_JOB, + NETWORK_REMOTE_JOB, + "127.0.0.1", 12346, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(NETWORK_LOCAL_JOB, NETWORK_DOMAIN, &status); + MTAPI_CHECK_STATUS(status); + + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + &argument, sizeof(float), + &result, sizeof(float), + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, 1, &status); + PT_ASSERT_EQ(status, MTAPI_TIMEOUT); + + mtapi_task_cancel(task, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, MTAPI_INFINITE, &status); + PT_ASSERT_NE(status, MTAPI_TIMEOUT); + PT_ASSERT_EQ(status, MTAPI_ERR_ACTION_CANCELLED); + + mtapi_action_delete(network_action, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_action_delete(local_action, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_network_plugin_finalize(&status); + MTAPI_CHECK_STATUS(status); +} diff --git b/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_task.h a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_task.h new file mode 100644 index 0000000..aa2573d --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/embb_mtapi_network_test_task.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_TASK_H_ +#define MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_TASK_H_ + +#include + +class NetworkTaskTest : public partest::TestCase { + public: + NetworkTaskTest(); + + private: + void TestBasic(); + + void TestSimple(); + void TestCancel(); +}; + +#endif // MTAPI_PLUGINS_C_MTAPI_NETWORK_C_TEST_EMBB_MTAPI_NETWORK_TEST_TASK_H_ diff --git b/mtapi_plugins_c/mtapi_network_c/test/main.cc a/mtapi_plugins_c/mtapi_network_c/test/main.cc new file mode 100644 index 0000000..081cd66 --- /dev/null +++ a/mtapi_plugins_c/mtapi_network_c/test/main.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +PT_MAIN("MTAPI NETWORK") { + PT_RUN(NetworkBufferTest); + PT_RUN(NetworkSocketTest); + PT_RUN(NetworkTaskTest); +} diff --git b/mtapi_plugins_c/mtapi_opencl_c/CMakeLists.txt a/mtapi_plugins_c/mtapi_opencl_c/CMakeLists.txt new file mode 100644 index 0000000..7dd94de --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/CMakeLists.txt @@ -0,0 +1,47 @@ +project (project_embb_mtapi_opencl_c) + +file(GLOB_RECURSE EMBB_MTAPI_OPENCL_C_SOURCES "src/*.c" "src/*.h") +file(GLOB_RECURSE EMBB_MTAPI_OPENCL_C_HEADERS "include/*.h") + +file(GLOB_RECURSE EMBB_MTAPI_OPENCL_TEST_SOURCES "test/*.cc" "test/*.h") + +IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) +FOREACH(src_tmp ${EMBB_MTAPI_OPENCL_TEST_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +FOREACH(src_tmp ${EMBB_MTAPI_OPENCL_C_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCC) + set (EMBB_MTAPI_OPENCL_C_LIBS dl) +ENDIF() + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_MTAPI_OPENCL_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_MTAPI_OPENCL_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/src + ) + +add_library(embb_mtapi_opencl_c ${EMBB_MTAPI_OPENCL_C_SOURCES} ${EMBB_MTAPI_OPENCL_C_HEADERS}) +target_link_libraries(embb_mtapi_opencl_c embb_mtapi_c embb_base_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../partest/include) + add_executable (embb_mtapi_opencl_c_test ${EMBB_MTAPI_OPENCL_TEST_SOURCES}) + target_link_libraries(embb_mtapi_opencl_c_test embb_mtapi_opencl_c embb_mtapi_c partest embb_base_c ${compiler_libs} ${EMBB_MTAPI_OPENCL_C_LIBS}) + CopyBin(BIN embb_mtapi_opencl_c_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_mtapi_opencl_c DESTINATION lib) diff --git b/mtapi_plugins_c/mtapi_opencl_c/include/embb/mtapi/c/mtapi_opencl.h a/mtapi_plugins_c/mtapi_opencl_c/include/embb/mtapi/c/mtapi_opencl.h new file mode 100644 index 0000000..b14583a --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/include/embb/mtapi/c/mtapi_opencl.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_MTAPI_C_MTAPI_OPENCL_H_ +#define EMBB_MTAPI_C_MTAPI_OPENCL_H_ + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * \defgroup C_MTAPI_OPENCL MTAPI OpenCL Plugin + * + * \ingroup C_MTAPI_EXT + * + * Provides functionality to execute tasks on OpenCL devices. + */ + + +/** + * Initializes the MTAPI OpenCL environment on a previously initialized MTAPI + * node. + * + * It must be called on all nodes using the MTAPI OpenCL plugin. + * + * Application software using MTAPI network must call + * mtapi_opencl_plugin_initialize() once per node. It is an error to call + * mtapi_opencl_plugin_initialize() multiple times + * from a given node, unless mtapi_opencl_plugin_finalize() is called in + * between. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_UNKNOWN | MTAPI OpenCL couldn't be initialized. + * + * \see mtapi_opencl_plugin_finalize() + * + * \notthreadsafe + * \ingroup C_MTAPI_OPENCL + */ +void mtapi_opencl_plugin_initialize( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + +/** + * Finalizes the MTAPI OpenCL environment on the local MTAPI node. + * + * It has to be called by each node using MTAPI OpenCL. It is an error to call + * mtapi_opencl_plugin_finalize() without first calling + * mtapi_opencl_plugin_initialize(). An MTAPI node can call + * mtapi_opencl_plugin_finalize() once for each call to + * mtapi_opencl_plugin_initialize(), but it is an error to call + * mtapi_opencl_plugin_finalize() multiple times from a given node + * unless mtapi_opencl_plugin_initialize() has been called prior to each + * mtapi_opencl_plugin_finalize() call. + * + * All network tasks that have not completed and that have been started on the + * node where mtapi_opencl_plugin_finalize() is called will be canceled + * (see mtapi_task_cancel()). mtapi_opencl_plugin_finalize() blocks until all + * tasks that have been started on the same node return. Tasks that execute + * actions on the node where mtapi_opencl_plugin_finalize() is called, also + * block finalization of the MTAPI OpenCL system on that node. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_UNKNOWN | MTAPI OpenCL couldn't be finalized. + * + * \see mtapi_opencl_plugin_initialize(), mtapi_task_cancel() + * + * \notthreadsafe + * \ingroup C_MTAPI_OPENCL + */ +void mtapi_opencl_plugin_finalize( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + +/** + * This function creates an OpenCL action. + * + * It is called on the node where the user wants to execute an action on an + * OpenCL device. An OpenCL action contains a reference to a local job, the + * kernel source to compile and execute on the OpenCL device, the name of the + * kernel function, a local work size (see OpenCL specification for details) + * and the size of one element in the result buffer. + * After an OpenCL action is created, it is referenced by the application using + * a node-local handle of type \c mtapi_action_hndl_t, or indirectly through a + * node-local job handle of type \c mtapi_job_hndl_t. An OpenCL action's + * life-cycle begins with mtapi_opencl_action_create(), and ends when + * mtapi_action_delete() or mtapi_finalize() is called. + * + * To create an action, the application must supply the domain-wide job ID of + * the job associated with the action. Job IDs must be predefined in the + * application and runtime, of type \c mtapi_job_id_t, which is an + * implementation-defined type. The job ID is unique in the sense that it is + * unique for the job implemented by the action. However several actions may + * implement the same job for load balancing purposes. + * + * If \c node_local_data_size is not zero, \c node_local_data specifies the + * start of node local data shared by kernel functions executed on the same + * node. \c node_local_data_size can be used by the runtime for cache coherency + * operations. + * + * On success, an action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the action already exists, \c status will + * be set to \c MTAPI_ERR_ACTION_EXISTS and the handle returned will not be a + * valid handle. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_JOB_INVALIDThe \c job_id is not a valid job ID, i.e., no action was created for + * that ID or the action has been deleted.
\c MTAPI_ERR_ACTION_EXISTSThis action is already created.
\c MTAPI_ERR_ACTION_LIMITExceeded maximum number of actions allowed.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_ERR_UNKNOWNThe kernel could not be compiled or no OpenCL device was + * available.
+ * + * \see mtapi_action_delete(), mtapi_finalize() + * + * \returns Handle to newly created OpenCL action, invalid handle on error + * \threadsafe + * \ingroup C_MTAPI_OPENCL + */ +mtapi_action_hndl_t mtapi_opencl_action_create( + MTAPI_IN mtapi_job_id_t job_id, /**< [in] Job id */ + MTAPI_IN char* kernel_source, /**< [in] Pointer to kernel source */ + MTAPI_IN char* kernel_name, /**< [in] Name of the kernel function */ + MTAPI_IN mtapi_size_t local_work_size, + /**< [in] Size of local work group */ + MTAPI_IN mtapi_size_t element_size, /**< [in] Size of one element in the + result buffer */ + MTAPI_IN void* node_local_data, /**< [in] Data shared across tasks */ + MTAPI_IN mtapi_size_t node_local_data_size, + /**< [in] Size of shared data */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ +); + + +#ifdef __cplusplus +} +#endif + + +#endif // EMBB_MTAPI_C_MTAPI_OPENCL_H_ diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl.h a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl.h new file mode 100644 index 0000000..6d4a6f2 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl.h @@ -0,0 +1,997 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +/* $Revision: 11708 $ on $Date: 2010-06-14 12:06:24 +0530 (Mon, 14 Jun 2010) $ */ + +#ifndef __OPENCL_CL_H +#define __OPENCL_CL_H + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************/ + +typedef struct _cl_platform_id * cl_platform_id; +typedef struct _cl_device_id * cl_device_id; +typedef struct _cl_context * cl_context; +typedef struct _cl_command_queue * cl_command_queue; +typedef struct _cl_mem * cl_mem; +typedef struct _cl_program * cl_program; +typedef struct _cl_kernel * cl_kernel; +typedef struct _cl_event * cl_event; +typedef struct _cl_sampler * cl_sampler; + +typedef cl_uint cl_bool; /* WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */ +typedef cl_ulong cl_bitfield; +typedef cl_bitfield cl_device_type; +typedef cl_uint cl_platform_info; +typedef cl_uint cl_device_info; +typedef cl_bitfield cl_device_fp_config; +typedef cl_uint cl_device_mem_cache_type; +typedef cl_uint cl_device_local_mem_type; +typedef cl_bitfield cl_device_exec_capabilities; +typedef cl_bitfield cl_command_queue_properties; + +typedef intptr_t cl_context_properties; +typedef cl_uint cl_context_info; +typedef cl_uint cl_command_queue_info; +typedef cl_uint cl_channel_order; +typedef cl_uint cl_channel_type; +typedef cl_bitfield cl_mem_flags; +typedef cl_uint cl_mem_object_type; +typedef cl_uint cl_mem_info; +typedef cl_uint cl_image_info; +typedef cl_uint cl_buffer_create_type; +typedef cl_uint cl_addressing_mode; +typedef cl_uint cl_filter_mode; +typedef cl_uint cl_sampler_info; +typedef cl_bitfield cl_map_flags; +typedef cl_uint cl_program_info; +typedef cl_uint cl_program_build_info; +typedef cl_int cl_build_status; +typedef cl_uint cl_kernel_info; +typedef cl_uint cl_kernel_work_group_info; +typedef cl_uint cl_event_info; +typedef cl_uint cl_command_type; +typedef cl_uint cl_profiling_info; + +typedef struct _cl_image_format { + cl_channel_order image_channel_order; + cl_channel_type image_channel_data_type; +} cl_image_format; + + +typedef struct _cl_buffer_region { + size_t origin; + size_t size; +} cl_buffer_region; + +/******************************************************************************/ + +/* Error Codes */ +#define CL_SUCCESS 0 +#define CL_DEVICE_NOT_FOUND -1 +#define CL_DEVICE_NOT_AVAILABLE -2 +#define CL_COMPILER_NOT_AVAILABLE -3 +#define CL_MEM_OBJECT_ALLOCATION_FAILURE -4 +#define CL_OUT_OF_RESOURCES -5 +#define CL_OUT_OF_HOST_MEMORY -6 +#define CL_PROFILING_INFO_NOT_AVAILABLE -7 +#define CL_MEM_COPY_OVERLAP -8 +#define CL_IMAGE_FORMAT_MISMATCH -9 +#define CL_IMAGE_FORMAT_NOT_SUPPORTED -10 +#define CL_BUILD_PROGRAM_FAILURE -11 +#define CL_MAP_FAILURE -12 +#define CL_MISALIGNED_SUB_BUFFER_OFFSET -13 +#define CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST -14 + +#define CL_INVALID_VALUE -30 +#define CL_INVALID_DEVICE_TYPE -31 +#define CL_INVALID_PLATFORM -32 +#define CL_INVALID_DEVICE -33 +#define CL_INVALID_CONTEXT -34 +#define CL_INVALID_QUEUE_PROPERTIES -35 +#define CL_INVALID_COMMAND_QUEUE -36 +#define CL_INVALID_HOST_PTR -37 +#define CL_INVALID_MEM_OBJECT -38 +#define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR -39 +#define CL_INVALID_IMAGE_SIZE -40 +#define CL_INVALID_SAMPLER -41 +#define CL_INVALID_BINARY -42 +#define CL_INVALID_BUILD_OPTIONS -43 +#define CL_INVALID_PROGRAM -44 +#define CL_INVALID_PROGRAM_EXECUTABLE -45 +#define CL_INVALID_KERNEL_NAME -46 +#define CL_INVALID_KERNEL_DEFINITION -47 +#define CL_INVALID_KERNEL -48 +#define CL_INVALID_ARG_INDEX -49 +#define CL_INVALID_ARG_VALUE -50 +#define CL_INVALID_ARG_SIZE -51 +#define CL_INVALID_KERNEL_ARGS -52 +#define CL_INVALID_WORK_DIMENSION -53 +#define CL_INVALID_WORK_GROUP_SIZE -54 +#define CL_INVALID_WORK_ITEM_SIZE -55 +#define CL_INVALID_GLOBAL_OFFSET -56 +#define CL_INVALID_EVENT_WAIT_LIST -57 +#define CL_INVALID_EVENT -58 +#define CL_INVALID_OPERATION -59 +#define CL_INVALID_GL_OBJECT -60 +#define CL_INVALID_BUFFER_SIZE -61 +#define CL_INVALID_MIP_LEVEL -62 +#define CL_INVALID_GLOBAL_WORK_SIZE -63 + +/* OpenCL Version */ +#define CL_VERSION_1_0 1 +#define CL_VERSION_1_1 1 + +/* cl_bool */ +#define CL_FALSE 0 +#define CL_TRUE 1 + +/* cl_platform_info */ +#define CL_PLATFORM_PROFILE 0x0900 +#define CL_PLATFORM_VERSION 0x0901 +#define CL_PLATFORM_NAME 0x0902 +#define CL_PLATFORM_VENDOR 0x0903 +#define CL_PLATFORM_EXTENSIONS 0x0904 + +/* cl_device_type - bitfield */ +#define CL_DEVICE_TYPE_DEFAULT (1 << 0) +#define CL_DEVICE_TYPE_CPU (1 << 1) +#define CL_DEVICE_TYPE_GPU (1 << 2) +#define CL_DEVICE_TYPE_ACCELERATOR (1 << 3) +#define CL_DEVICE_TYPE_ALL 0xFFFFFFFF + +/* cl_device_info */ +#define CL_DEVICE_TYPE 0x1000 +#define CL_DEVICE_VENDOR_ID 0x1001 +#define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002 +#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003 +#define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004 +#define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B +#define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C +#define CL_DEVICE_ADDRESS_BITS 0x100D +#define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E +#define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F +#define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010 +#define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011 +#define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012 +#define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013 +#define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014 +#define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015 +#define CL_DEVICE_IMAGE_SUPPORT 0x1016 +#define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017 +#define CL_DEVICE_MAX_SAMPLERS 0x1018 +#define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019 +#define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A +#define CL_DEVICE_SINGLE_FP_CONFIG 0x101B +#define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C +#define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D +#define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E +#define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F +#define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020 +#define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021 +#define CL_DEVICE_LOCAL_MEM_TYPE 0x1022 +#define CL_DEVICE_LOCAL_MEM_SIZE 0x1023 +#define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024 +#define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025 +#define CL_DEVICE_ENDIAN_LITTLE 0x1026 +#define CL_DEVICE_AVAILABLE 0x1027 +#define CL_DEVICE_COMPILER_AVAILABLE 0x1028 +#define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029 +#define CL_DEVICE_QUEUE_PROPERTIES 0x102A +#define CL_DEVICE_NAME 0x102B +#define CL_DEVICE_VENDOR 0x102C +#define CL_DRIVER_VERSION 0x102D +#define CL_DEVICE_PROFILE 0x102E +#define CL_DEVICE_VERSION 0x102F +#define CL_DEVICE_EXTENSIONS 0x1030 +#define CL_DEVICE_PLATFORM 0x1031 +/* 0x1032 reserved for CL_DEVICE_DOUBLE_FP_CONFIG */ +/* 0x1033 reserved for CL_DEVICE_HALF_FP_CONFIG */ +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF 0x1034 +#define CL_DEVICE_HOST_UNIFIED_MEMORY 0x1035 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR 0x1036 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT 0x1037 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT 0x1038 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG 0x1039 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT 0x103A +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE 0x103B +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF 0x103C +#define CL_DEVICE_OPENCL_C_VERSION 0x103D + +/* cl_device_fp_config - bitfield */ +#define CL_FP_DENORM (1 << 0) +#define CL_FP_INF_NAN (1 << 1) +#define CL_FP_ROUND_TO_NEAREST (1 << 2) +#define CL_FP_ROUND_TO_ZERO (1 << 3) +#define CL_FP_ROUND_TO_INF (1 << 4) +#define CL_FP_FMA (1 << 5) +#define CL_FP_SOFT_FLOAT (1 << 6) + +/* cl_device_mem_cache_type */ +#define CL_NONE 0x0 +#define CL_READ_ONLY_CACHE 0x1 +#define CL_READ_WRITE_CACHE 0x2 + +/* cl_device_local_mem_type */ +#define CL_LOCAL 0x1 +#define CL_GLOBAL 0x2 + +/* cl_device_exec_capabilities - bitfield */ +#define CL_EXEC_KERNEL (1 << 0) +#define CL_EXEC_NATIVE_KERNEL (1 << 1) + +/* cl_command_queue_properties - bitfield */ +#define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE (1 << 0) +#define CL_QUEUE_PROFILING_ENABLE (1 << 1) + +/* cl_context_info */ +#define CL_CONTEXT_REFERENCE_COUNT 0x1080 +#define CL_CONTEXT_DEVICES 0x1081 +#define CL_CONTEXT_PROPERTIES 0x1082 +#define CL_CONTEXT_NUM_DEVICES 0x1083 + +/* cl_context_info + cl_context_properties */ +#define CL_CONTEXT_PLATFORM 0x1084 + +/* cl_command_queue_info */ +#define CL_QUEUE_CONTEXT 0x1090 +#define CL_QUEUE_DEVICE 0x1091 +#define CL_QUEUE_REFERENCE_COUNT 0x1092 +#define CL_QUEUE_PROPERTIES 0x1093 + +/* cl_mem_flags - bitfield */ +#define CL_MEM_READ_WRITE (1 << 0) +#define CL_MEM_WRITE_ONLY (1 << 1) +#define CL_MEM_READ_ONLY (1 << 2) +#define CL_MEM_USE_HOST_PTR (1 << 3) +#define CL_MEM_ALLOC_HOST_PTR (1 << 4) +#define CL_MEM_COPY_HOST_PTR (1 << 5) + +/* cl_channel_order */ +#define CL_R 0x10B0 +#define CL_A 0x10B1 +#define CL_RG 0x10B2 +#define CL_RA 0x10B3 +#define CL_RGB 0x10B4 +#define CL_RGBA 0x10B5 +#define CL_BGRA 0x10B6 +#define CL_ARGB 0x10B7 +#define CL_INTENSITY 0x10B8 +#define CL_LUMINANCE 0x10B9 +#define CL_Rx 0x10BA +#define CL_RGx 0x10BB +#define CL_RGBx 0x10BC + +/* cl_channel_type */ +#define CL_SNORM_INT8 0x10D0 +#define CL_SNORM_INT16 0x10D1 +#define CL_UNORM_INT8 0x10D2 +#define CL_UNORM_INT16 0x10D3 +#define CL_UNORM_SHORT_565 0x10D4 +#define CL_UNORM_SHORT_555 0x10D5 +#define CL_UNORM_INT_101010 0x10D6 +#define CL_SIGNED_INT8 0x10D7 +#define CL_SIGNED_INT16 0x10D8 +#define CL_SIGNED_INT32 0x10D9 +#define CL_UNSIGNED_INT8 0x10DA +#define CL_UNSIGNED_INT16 0x10DB +#define CL_UNSIGNED_INT32 0x10DC +#define CL_HALF_FLOAT 0x10DD +#define CL_FLOAT 0x10DE + +/* cl_mem_object_type */ +#define CL_MEM_OBJECT_BUFFER 0x10F0 +#define CL_MEM_OBJECT_IMAGE2D 0x10F1 +#define CL_MEM_OBJECT_IMAGE3D 0x10F2 + +/* cl_mem_info */ +#define CL_MEM_TYPE 0x1100 +#define CL_MEM_FLAGS 0x1101 +#define CL_MEM_SIZE 0x1102 +#define CL_MEM_HOST_PTR 0x1103 +#define CL_MEM_MAP_COUNT 0x1104 +#define CL_MEM_REFERENCE_COUNT 0x1105 +#define CL_MEM_CONTEXT 0x1106 +#define CL_MEM_ASSOCIATED_MEMOBJECT 0x1107 +#define CL_MEM_OFFSET 0x1108 + +/* cl_image_info */ +#define CL_IMAGE_FORMAT 0x1110 +#define CL_IMAGE_ELEMENT_SIZE 0x1111 +#define CL_IMAGE_ROW_PITCH 0x1112 +#define CL_IMAGE_SLICE_PITCH 0x1113 +#define CL_IMAGE_WIDTH 0x1114 +#define CL_IMAGE_HEIGHT 0x1115 +#define CL_IMAGE_DEPTH 0x1116 + +/* cl_addressing_mode */ +#define CL_ADDRESS_NONE 0x1130 +#define CL_ADDRESS_CLAMP_TO_EDGE 0x1131 +#define CL_ADDRESS_CLAMP 0x1132 +#define CL_ADDRESS_REPEAT 0x1133 +#define CL_ADDRESS_MIRRORED_REPEAT 0x1134 + +/* cl_filter_mode */ +#define CL_FILTER_NEAREST 0x1140 +#define CL_FILTER_LINEAR 0x1141 + +/* cl_sampler_info */ +#define CL_SAMPLER_REFERENCE_COUNT 0x1150 +#define CL_SAMPLER_CONTEXT 0x1151 +#define CL_SAMPLER_NORMALIZED_COORDS 0x1152 +#define CL_SAMPLER_ADDRESSING_MODE 0x1153 +#define CL_SAMPLER_FILTER_MODE 0x1154 + +/* cl_map_flags - bitfield */ +#define CL_MAP_READ (1 << 0) +#define CL_MAP_WRITE (1 << 1) + +/* cl_program_info */ +#define CL_PROGRAM_REFERENCE_COUNT 0x1160 +#define CL_PROGRAM_CONTEXT 0x1161 +#define CL_PROGRAM_NUM_DEVICES 0x1162 +#define CL_PROGRAM_DEVICES 0x1163 +#define CL_PROGRAM_SOURCE 0x1164 +#define CL_PROGRAM_BINARY_SIZES 0x1165 +#define CL_PROGRAM_BINARIES 0x1166 + +/* cl_program_build_info */ +#define CL_PROGRAM_BUILD_STATUS 0x1181 +#define CL_PROGRAM_BUILD_OPTIONS 0x1182 +#define CL_PROGRAM_BUILD_LOG 0x1183 + +/* cl_build_status */ +#define CL_BUILD_SUCCESS 0 +#define CL_BUILD_NONE -1 +#define CL_BUILD_ERROR -2 +#define CL_BUILD_IN_PROGRESS -3 + +/* cl_kernel_info */ +#define CL_KERNEL_FUNCTION_NAME 0x1190 +#define CL_KERNEL_NUM_ARGS 0x1191 +#define CL_KERNEL_REFERENCE_COUNT 0x1192 +#define CL_KERNEL_CONTEXT 0x1193 +#define CL_KERNEL_PROGRAM 0x1194 + +/* cl_kernel_work_group_info */ +#define CL_KERNEL_WORK_GROUP_SIZE 0x11B0 +#define CL_KERNEL_COMPILE_WORK_GROUP_SIZE 0x11B1 +#define CL_KERNEL_LOCAL_MEM_SIZE 0x11B2 +#define CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x11B3 +#define CL_KERNEL_PRIVATE_MEM_SIZE 0x11B4 + +/* cl_event_info */ +#define CL_EVENT_COMMAND_QUEUE 0x11D0 +#define CL_EVENT_COMMAND_TYPE 0x11D1 +#define CL_EVENT_REFERENCE_COUNT 0x11D2 +#define CL_EVENT_COMMAND_EXECUTION_STATUS 0x11D3 +#define CL_EVENT_CONTEXT 0x11D4 + +/* cl_command_type */ +#define CL_COMMAND_NDRANGE_KERNEL 0x11F0 +#define CL_COMMAND_TASK 0x11F1 +#define CL_COMMAND_NATIVE_KERNEL 0x11F2 +#define CL_COMMAND_READ_BUFFER 0x11F3 +#define CL_COMMAND_WRITE_BUFFER 0x11F4 +#define CL_COMMAND_COPY_BUFFER 0x11F5 +#define CL_COMMAND_READ_IMAGE 0x11F6 +#define CL_COMMAND_WRITE_IMAGE 0x11F7 +#define CL_COMMAND_COPY_IMAGE 0x11F8 +#define CL_COMMAND_COPY_IMAGE_TO_BUFFER 0x11F9 +#define CL_COMMAND_COPY_BUFFER_TO_IMAGE 0x11FA +#define CL_COMMAND_MAP_BUFFER 0x11FB +#define CL_COMMAND_MAP_IMAGE 0x11FC +#define CL_COMMAND_UNMAP_MEM_OBJECT 0x11FD +#define CL_COMMAND_MARKER 0x11FE +#define CL_COMMAND_ACQUIRE_GL_OBJECTS 0x11FF +#define CL_COMMAND_RELEASE_GL_OBJECTS 0x1200 +#define CL_COMMAND_READ_BUFFER_RECT 0x1201 +#define CL_COMMAND_WRITE_BUFFER_RECT 0x1202 +#define CL_COMMAND_COPY_BUFFER_RECT 0x1203 +#define CL_COMMAND_USER 0x1204 + +/* command execution status */ +#define CL_COMPLETE 0x0 +#define CL_RUNNING 0x1 +#define CL_SUBMITTED 0x2 +#define CL_QUEUED 0x3 + +/* cl_buffer_create_type */ +#define CL_BUFFER_CREATE_TYPE_REGION 0x1220 + +/* cl_profiling_info */ +#define CL_PROFILING_COMMAND_QUEUED 0x1280 +#define CL_PROFILING_COMMAND_SUBMIT 0x1281 +#define CL_PROFILING_COMMAND_START 0x1282 +#define CL_PROFILING_COMMAND_END 0x1283 + +/********************************************************************************************************/ + +/* Platform API */ +extern CL_API_ENTRY cl_int CL_API_CALL +clGetPlatformIDs(cl_uint /* num_entries */, + cl_platform_id * /* platforms */, + cl_uint * /* num_platforms */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetPlatformInfo(cl_platform_id /* platform */, + cl_platform_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Device APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clGetDeviceIDs(cl_platform_id /* platform */, + cl_device_type /* device_type */, + cl_uint /* num_entries */, + cl_device_id * /* devices */, + cl_uint * /* num_devices */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetDeviceInfo(cl_device_id /* device */, + cl_device_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Context APIs */ +extern CL_API_ENTRY cl_context CL_API_CALL +clCreateContext(const cl_context_properties * /* properties */, + cl_uint /* num_devices */, + const cl_device_id * /* devices */, + void (CL_CALLBACK * /* pfn_notify */)(const char *, const void *, size_t, void *), + void * /* user_data */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_context CL_API_CALL +clCreateContextFromType(const cl_context_properties * /* properties */, + cl_device_type /* device_type */, + void (CL_CALLBACK * /* pfn_notify*/ )(const char *, const void *, size_t, void *), + void * /* user_data */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetContextInfo(cl_context /* context */, + cl_context_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Command Queue APIs */ +extern CL_API_ENTRY cl_command_queue CL_API_CALL +clCreateCommandQueue(cl_context /* context */, + cl_device_id /* device */, + cl_command_queue_properties /* properties */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetCommandQueueInfo(cl_command_queue /* command_queue */, + cl_command_queue_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS +#warning CL_USE_DEPRECATED_OPENCL_1_0_APIS is defined. These APIs are unsupported and untested in OpenCL 1.1! +/* + * WARNING: + * This API introduces mutable state into the OpenCL implementation. It has been REMOVED + * to better facilitate thread safety. The 1.0 API is not thread safe. It is not tested by the + * OpenCL 1.1 conformance test, and consequently may not work or may not work dependably. + * It is likely to be non-performant. Use of this API is not advised. Use at your own risk. + * + * Software developers previously relying on this API are instructed to set the command queue + * properties when creating the queue, instead. + */ +extern CL_API_ENTRY cl_int CL_API_CALL +clSetCommandQueueProperty(cl_command_queue /* command_queue */, + cl_command_queue_properties /* properties */, + cl_bool /* enable */, + cl_command_queue_properties * /* old_properties */) CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED; +#endif /* CL_USE_DEPRECATED_OPENCL_1_0_APIS */ + +/* Memory Object APIs */ +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateBuffer(cl_context /* context */, + cl_mem_flags /* flags */, + size_t /* size */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateSubBuffer(cl_mem /* buffer */, + cl_mem_flags /* flags */, + cl_buffer_create_type /* buffer_create_type */, + const void * /* buffer_create_info */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateImage2D(cl_context /* context */, + cl_mem_flags /* flags */, + const cl_image_format * /* image_format */, + size_t /* image_width */, + size_t /* image_height */, + size_t /* image_row_pitch */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateImage3D(cl_context /* context */, + cl_mem_flags /* flags */, + const cl_image_format * /* image_format */, + size_t /* image_width */, + size_t /* image_height */, + size_t /* image_depth */, + size_t /* image_row_pitch */, + size_t /* image_slice_pitch */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetSupportedImageFormats(cl_context /* context */, + cl_mem_flags /* flags */, + cl_mem_object_type /* image_type */, + cl_uint /* num_entries */, + cl_image_format * /* image_formats */, + cl_uint * /* num_image_formats */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetMemObjectInfo(cl_mem /* memobj */, + cl_mem_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetImageInfo(cl_mem /* image */, + cl_image_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetMemObjectDestructorCallback( cl_mem /* memobj */, + void (CL_CALLBACK * /*pfn_notify*/)( cl_mem /* memobj */, void* /*user_data*/), + void * /*user_data */ ) CL_API_SUFFIX__VERSION_1_1; + +/* Sampler APIs */ +extern CL_API_ENTRY cl_sampler CL_API_CALL +clCreateSampler(cl_context /* context */, + cl_bool /* normalized_coords */, + cl_addressing_mode /* addressing_mode */, + cl_filter_mode /* filter_mode */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetSamplerInfo(cl_sampler /* sampler */, + cl_sampler_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Program Object APIs */ +extern CL_API_ENTRY cl_program CL_API_CALL +clCreateProgramWithSource(cl_context /* context */, + cl_uint /* count */, + const char ** /* strings */, + const size_t * /* lengths */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_program CL_API_CALL +clCreateProgramWithBinary(cl_context /* context */, + cl_uint /* num_devices */, + const cl_device_id * /* device_list */, + const size_t * /* lengths */, + const unsigned char ** /* binaries */, + cl_int * /* binary_status */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clBuildProgram(cl_program /* program */, + cl_uint /* num_devices */, + const cl_device_id * /* device_list */, + const char * /* options */, + void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), + void * /* user_data */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clUnloadCompiler(void) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetProgramInfo(cl_program /* program */, + cl_program_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetProgramBuildInfo(cl_program /* program */, + cl_device_id /* device */, + cl_program_build_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Kernel Object APIs */ +extern CL_API_ENTRY cl_kernel CL_API_CALL +clCreateKernel(cl_program /* program */, + const char * /* kernel_name */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clCreateKernelsInProgram(cl_program /* program */, + cl_uint /* num_kernels */, + cl_kernel * /* kernels */, + cl_uint * /* num_kernels_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetKernelArg(cl_kernel /* kernel */, + cl_uint /* arg_index */, + size_t /* arg_size */, + const void * /* arg_value */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetKernelInfo(cl_kernel /* kernel */, + cl_kernel_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetKernelWorkGroupInfo(cl_kernel /* kernel */, + cl_device_id /* device */, + cl_kernel_work_group_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Event Object APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clWaitForEvents(cl_uint /* num_events */, + const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetEventInfo(cl_event /* event */, + cl_event_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_event CL_API_CALL +clCreateUserEvent(cl_context /* context */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetUserEventStatus(cl_event /* event */, + cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetEventCallback( cl_event /* event */, + cl_int /* command_exec_callback_type */, + void (CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *), + void * /* user_data */) CL_API_SUFFIX__VERSION_1_1; + +/* Profiling APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clGetEventProfilingInfo(cl_event /* event */, + cl_profiling_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +/* Flush and Finish APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clFlush(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clFinish(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +/* Enqueued Commands APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReadBuffer(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_read */, + size_t /* offset */, + size_t /* cb */, + void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReadBufferRect(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_read */, + const size_t * /* buffer_offset */, + const size_t * /* host_offset */, + const size_t * /* region */, + size_t /* buffer_row_pitch */, + size_t /* buffer_slice_pitch */, + size_t /* host_row_pitch */, + size_t /* host_slice_pitch */, + void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWriteBuffer(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_write */, + size_t /* offset */, + size_t /* cb */, + const void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWriteBufferRect(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_read */, + const size_t * /* buffer_offset */, + const size_t * /* host_offset */, + const size_t * /* region */, + size_t /* buffer_row_pitch */, + size_t /* buffer_slice_pitch */, + size_t /* host_row_pitch */, + size_t /* host_slice_pitch */, + const void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyBuffer(cl_command_queue /* command_queue */, + cl_mem /* src_buffer */, + cl_mem /* dst_buffer */, + size_t /* src_offset */, + size_t /* dst_offset */, + size_t /* cb */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyBufferRect(cl_command_queue /* command_queue */, + cl_mem /* src_buffer */, + cl_mem /* dst_buffer */, + const size_t * /* src_origin */, + const size_t * /* dst_origin */, + const size_t * /* region */, + size_t /* src_row_pitch */, + size_t /* src_slice_pitch */, + size_t /* dst_row_pitch */, + size_t /* dst_slice_pitch */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReadImage(cl_command_queue /* command_queue */, + cl_mem /* image */, + cl_bool /* blocking_read */, + const size_t * /* origin[3] */, + const size_t * /* region[3] */, + size_t /* row_pitch */, + size_t /* slice_pitch */, + void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWriteImage(cl_command_queue /* command_queue */, + cl_mem /* image */, + cl_bool /* blocking_write */, + const size_t * /* origin[3] */, + const size_t * /* region[3] */, + size_t /* input_row_pitch */, + size_t /* input_slice_pitch */, + const void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyImage(cl_command_queue /* command_queue */, + cl_mem /* src_image */, + cl_mem /* dst_image */, + const size_t * /* src_origin[3] */, + const size_t * /* dst_origin[3] */, + const size_t * /* region[3] */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyImageToBuffer(cl_command_queue /* command_queue */, + cl_mem /* src_image */, + cl_mem /* dst_buffer */, + const size_t * /* src_origin[3] */, + const size_t * /* region[3] */, + size_t /* dst_offset */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyBufferToImage(cl_command_queue /* command_queue */, + cl_mem /* src_buffer */, + cl_mem /* dst_image */, + size_t /* src_offset */, + const size_t * /* dst_origin[3] */, + const size_t * /* region[3] */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY void * CL_API_CALL +clEnqueueMapBuffer(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_map */, + cl_map_flags /* map_flags */, + size_t /* offset */, + size_t /* cb */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY void * CL_API_CALL +clEnqueueMapImage(cl_command_queue /* command_queue */, + cl_mem /* image */, + cl_bool /* blocking_map */, + cl_map_flags /* map_flags */, + const size_t * /* origin[3] */, + const size_t * /* region[3] */, + size_t * /* image_row_pitch */, + size_t * /* image_slice_pitch */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueUnmapMemObject(cl_command_queue /* command_queue */, + cl_mem /* memobj */, + void * /* mapped_ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueNDRangeKernel(cl_command_queue /* command_queue */, + cl_kernel /* kernel */, + cl_uint /* work_dim */, + const size_t * /* global_work_offset */, + const size_t * /* global_work_size */, + const size_t * /* local_work_size */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueTask(cl_command_queue /* command_queue */, + cl_kernel /* kernel */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueNativeKernel(cl_command_queue /* command_queue */, + void (*user_func)(void *), + void * /* args */, + size_t /* cb_args */, + cl_uint /* num_mem_objects */, + const cl_mem * /* mem_list */, + const void ** /* args_mem_loc */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueMarker(cl_command_queue /* command_queue */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWaitForEvents(cl_command_queue /* command_queue */, + cl_uint /* num_events */, + const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueBarrier(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +/* Extension function access + * + * Returns the extension function address for the given function name, + * or NULL if a valid function can not be found. The client must + * check to make sure the address is not NULL, before using or + * calling the returned function address. + */ +extern CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(const char * /* func_name */) CL_API_SUFFIX__VERSION_1_0; + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENCL_CL_H */ + diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_ext.h a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_ext.h new file mode 100644 index 0000000..2d6e64e --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_ext.h @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +/* $Revision: 11687 $ on $Date: 2010-06-12 03:47:22 +0530 (Sat, 12 Jun 2010) $ */ + +/* cl_ext.h contains OpenCL extensions which don't have external */ +/* (OpenGL, D3D) dependencies. */ + +#ifndef __CL_EXT_H +#define __CL_EXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __APPLE__ + #include + #include +#else + #include +#endif + +/* cl_khr_fp64 extension - no extension #define since it has no functions */ +#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 + +/* cl_khr_fp16 extension - no extension #define since it has no functions */ +#define CL_DEVICE_HALF_FP_CONFIG 0x1033 + +/* Memory object destruction + * + * Apple extension for use to manage externally allocated buffers used with cl_mem objects with CL_MEM_USE_HOST_PTR + * + * Registers a user callback function that will be called when the memory object is deleted and its resources + * freed. Each call to clSetMemObjectCallbackFn registers the specified user callback function on a callback + * stack associated with memobj. The registered user callback functions are called in the reverse order in + * which they were registered. The user callback functions are called and then the memory object is deleted + * and its resources freed. This provides a mechanism for the application (and libraries) using memobj to be + * notified when the memory referenced by host_ptr, specified when the memory object is created and used as + * the storage bits for the memory object, can be reused or freed. + * + * The application may not call CL api's with the cl_mem object passed to the pfn_notify. + * + * Please check for the "cl_APPLE_SetMemObjectDestructor" extension using clGetDeviceInfo(CL_DEVICE_EXTENSIONS) + * before using. + */ +#define cl_APPLE_SetMemObjectDestructor 1 +cl_int CL_API_ENTRY clSetMemObjectDestructorAPPLE( cl_mem /* memobj */, + void (* /*pfn_notify*/)( cl_mem /* memobj */, void* /*user_data*/), + void * /*user_data */ ) CL_EXT_SUFFIX__VERSION_1_0; + + +/* Context Logging Functions + * + * The next three convenience functions are intended to be used as the pfn_notify parameter to clCreateContext(). + * Please check for the "cl_APPLE_ContextLoggingFunctions" extension using clGetDeviceInfo(CL_DEVICE_EXTENSIONS) + * before using. + * + * clLogMessagesToSystemLog fowards on all log messages to the Apple System Logger + */ +#define cl_APPLE_ContextLoggingFunctions 1 +extern void CL_API_ENTRY clLogMessagesToSystemLogAPPLE( const char * /* errstr */, + const void * /* private_info */, + size_t /* cb */, + void * /* user_data */ ) CL_EXT_SUFFIX__VERSION_1_0; + +/* clLogMessagesToStdout sends all log messages to the file descriptor stdout */ +extern void CL_API_ENTRY clLogMessagesToStdoutAPPLE( const char * /* errstr */, + const void * /* private_info */, + size_t /* cb */, + void * /* user_data */ ) CL_EXT_SUFFIX__VERSION_1_0; + +/* clLogMessagesToStderr sends all log messages to the file descriptor stderr */ +extern void CL_API_ENTRY clLogMessagesToStderrAPPLE( const char * /* errstr */, + const void * /* private_info */, + size_t /* cb */, + void * /* user_data */ ) CL_EXT_SUFFIX__VERSION_1_0; + + +/************************ +* cl_khr_icd extension * +************************/ +#define cl_khr_icd 1 + +/* cl_platform_info */ +#define CL_PLATFORM_ICD_SUFFIX_KHR 0x0920 + +/* Additional Error Codes */ +#define CL_PLATFORM_NOT_FOUND_KHR -1001 + +extern CL_API_ENTRY cl_int CL_API_CALL +clIcdGetPlatformIDsKHR(cl_uint /* num_entries */, + cl_platform_id * /* platforms */, + cl_uint * /* num_platforms */); + +typedef CL_API_ENTRY cl_int (CL_API_CALL *clIcdGetPlatformIDsKHR_fn)( + cl_uint /* num_entries */, + cl_platform_id * /* platforms */, + cl_uint * /* num_platforms */); + + +/****************************************** +* cl_nv_device_attribute_query extension * +******************************************/ +/* cl_nv_device_attribute_query extension - no extension #define since it has no functions */ +#define CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV 0x4000 +#define CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV 0x4001 +#define CL_DEVICE_REGISTERS_PER_BLOCK_NV 0x4002 +#define CL_DEVICE_WARP_SIZE_NV 0x4003 +#define CL_DEVICE_GPU_OVERLAP_NV 0x4004 +#define CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV 0x4005 +#define CL_DEVICE_INTEGRATED_MEMORY_NV 0x4006 + + +/********************************* +* cl_amd_device_attribute_query * +*********************************/ +#define CL_DEVICE_PROFILING_TIMER_OFFSET_AMD 0x4036 + + +#ifdef CL_VERSION_1_1 + /*********************************** + * cl_ext_device_fission extension * + ***********************************/ + #define cl_ext_device_fission 1 + + extern CL_API_ENTRY cl_int CL_API_CALL + clReleaseDeviceEXT( cl_device_id /*device*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + typedef CL_API_ENTRY cl_int + (CL_API_CALL *clReleaseDeviceEXT_fn)( cl_device_id /*device*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + extern CL_API_ENTRY cl_int CL_API_CALL + clRetainDeviceEXT( cl_device_id /*device*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + typedef CL_API_ENTRY cl_int + (CL_API_CALL *clRetainDeviceEXT_fn)( cl_device_id /*device*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + typedef cl_ulong cl_device_partition_property_ext; + extern CL_API_ENTRY cl_int CL_API_CALL + clCreateSubDevicesEXT( cl_device_id /*in_device*/, + const cl_device_partition_property_ext * /* properties */, + cl_uint /*num_entries*/, + cl_device_id * /*out_devices*/, + cl_uint * /*num_devices*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + extern CL_API_ENTRY cl_int + ( CL_API_CALL * clCreateSubDevicesEXT_fn)( cl_device_id /*in_device*/, + const cl_device_partition_property_ext * /* properties */, + cl_uint /*num_entries*/, + cl_device_id * /*out_devices*/, + cl_uint * /*num_devices*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + /* cl_device_partition_property_ext */ + #define CL_DEVICE_PARTITION_EQUALLY_EXT 0x4050 + #define CL_DEVICE_PARTITION_BY_COUNTS_EXT 0x4051 + #define CL_DEVICE_PARTITION_BY_NAMES_EXT 0x4052 + #define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT 0x4053 + + /* clDeviceGetInfo selectors */ + #define CL_DEVICE_PARENT_DEVICE_EXT 0x4054 + #define CL_DEVICE_PARTITION_TYPES_EXT 0x4055 + #define CL_DEVICE_AFFINITY_DOMAINS_EXT 0x4056 + #define CL_DEVICE_REFERENCE_COUNT_EXT 0x4057 + #define CL_DEVICE_PARTITION_STYLE_EXT 0x4058 + + /* error codes */ + #define CL_DEVICE_PARTITION_FAILED_EXT -1057 + #define CL_INVALID_PARTITION_COUNT_EXT -1058 + #define CL_INVALID_PARTITION_NAME_EXT -1059 + + /* CL_AFFINITY_DOMAINs */ + #define CL_AFFINITY_DOMAIN_L1_CACHE_EXT 0x1 + #define CL_AFFINITY_DOMAIN_L2_CACHE_EXT 0x2 + #define CL_AFFINITY_DOMAIN_L3_CACHE_EXT 0x3 + #define CL_AFFINITY_DOMAIN_L4_CACHE_EXT 0x4 + #define CL_AFFINITY_DOMAIN_NUMA_EXT 0x10 + #define CL_AFFINITY_DOMAIN_NEXT_FISSIONABLE_EXT 0x100 + + /* cl_device_partition_property_ext list terminators */ + #define CL_PROPERTIES_LIST_END_EXT ((cl_device_partition_property_ext) 0) + #define CL_PARTITION_BY_COUNTS_LIST_END_EXT ((cl_device_partition_property_ext) 0) + #define CL_PARTITION_BY_NAMES_LIST_END_EXT ((cl_device_partition_property_ext) 0 - 1) + + + +#endif /* CL_VERSION_1_1 */ + +#ifdef __cplusplus +} +#endif + + +#endif /* __CL_EXT_H */ diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_gl.h a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_gl.h new file mode 100644 index 0000000..6eeae1e --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_gl.h @@ -0,0 +1,155 @@ +/********************************************************************************** + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +/* $Revision: 11708 $ on $Date: 2010-06-14 12:06:24 +0530 (Mon, 14 Jun 2010) $ */ + +/* + * cl_gl.h contains Khronos-approved (KHR) OpenCL extensions which have + * OpenGL dependencies. The application is responsible for #including + * OpenGL or OpenGL ES headers before #including cl_gl.h. + */ + +#ifndef __OPENCL_CL_GL_H +#define __OPENCL_CL_GL_H + +#ifdef __APPLE__ +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef cl_uint cl_gl_object_type; +typedef cl_uint cl_gl_texture_info; +typedef cl_uint cl_gl_platform_info; +typedef struct __GLsync *cl_GLsync; + +/* cl_gl_object_type */ +#define CL_GL_OBJECT_BUFFER 0x2000 +#define CL_GL_OBJECT_TEXTURE2D 0x2001 +#define CL_GL_OBJECT_TEXTURE3D 0x2002 +#define CL_GL_OBJECT_RENDERBUFFER 0x2003 + +/* cl_gl_texture_info */ +#define CL_GL_TEXTURE_TARGET 0x2004 +#define CL_GL_MIPMAP_LEVEL 0x2005 + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLBuffer(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLuint /* bufobj */, + int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLTexture2D(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLenum /* target */, + cl_GLint /* miplevel */, + cl_GLuint /* texture */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLTexture3D(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLenum /* target */, + cl_GLint /* miplevel */, + cl_GLuint /* texture */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLRenderbuffer(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLuint /* renderbuffer */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLObjectInfo(cl_mem /* memobj */, + cl_gl_object_type * /* gl_object_type */, + cl_GLuint * /* gl_object_name */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLTextureInfo(cl_mem /* memobj */, + cl_gl_texture_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueAcquireGLObjects(cl_command_queue /* command_queue */, + cl_uint /* num_objects */, + const cl_mem * /* mem_objects */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReleaseGLObjects(cl_command_queue /* command_queue */, + cl_uint /* num_objects */, + const cl_mem * /* mem_objects */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +/* cl_khr_gl_sharing extension */ + +#define cl_khr_gl_sharing 1 + +typedef cl_uint cl_gl_context_info; + +/* Additional Error Codes */ +#define CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR -1000 + +/* cl_gl_context_info */ +#define CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR 0x2006 +#define CL_DEVICES_FOR_GL_CONTEXT_KHR 0x2007 + +/* Additional cl_context_properties */ +#define CL_GL_CONTEXT_KHR 0x2008 +#define CL_EGL_DISPLAY_KHR 0x2009 +#define CL_GLX_DISPLAY_KHR 0x200A +#define CL_WGL_HDC_KHR 0x200B +#define CL_CGL_SHAREGROUP_KHR 0x200C + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLContextInfoKHR(const cl_context_properties * /* properties */, + cl_gl_context_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +typedef CL_API_ENTRY cl_int (CL_API_CALL *clGetGLContextInfoKHR_fn)( + const cl_context_properties * properties, + cl_gl_context_info param_name, + size_t param_value_size, + void * param_value, + size_t * param_value_size_ret); + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENCL_CL_GL_H */ diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_gl_ext.h a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_gl_ext.h new file mode 100644 index 0000000..5555dd1 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_gl_ext.h @@ -0,0 +1,70 @@ +/********************************************************************************** + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +/* $Revision: 11708 $ on $Date: 2010-06-14 12:06:24 +0530 (Mon, 14 Jun 2010) $ */ + +/* cl_gl_ext.h contains vendor (non-KHR) OpenCL extensions which have */ +/* OpenGL dependencies. */ + +#ifndef __OPENCL_CL_GL_EXT_H +#define __OPENCL_CL_GL_EXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __APPLE__ + #include +#else + #include +#endif + +/* + * For each extension, follow this template + * // cl_VEN_extname extension + */ +/* #define cl_VEN_extname 1 + * ... define new types, if any + * ... define new tokens, if any + * ... define new APIs, if any + * + * If you need GLtypes here, mirror them with a cl_GLtype, rather than including a GL header + * This allows us to avoid having to decide whether to include GL headers or GLES here. + */ + +/* + * cl_khr_gl_event extension + * See section 9.9 in the OpenCL 1.1 spec for more information + */ +#define CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR 0x200D + +extern CL_API_ENTRY cl_event CL_API_CALL +clCreateEventFromGLsyncKHR(cl_context /* context */, + cl_GLsync /* cl_GLsync */, + cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_1; + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENCL_CL_GL_EXT_H */ diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_platform.h a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_platform.h new file mode 100644 index 0000000..337d120 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/CL/cl_platform.h @@ -0,0 +1,1198 @@ +/********************************************************************************** + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +/* $Revision: 11803 $ on $Date: 2010-06-25 22:32:12 +0530 (Fri, 25 Jun 2010) $ */ + +#ifndef __CL_PLATFORM_H +#define __CL_PLATFORM_H + +#ifdef __APPLE__ + /* Contains #defines for AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER below */ + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) +#define CL_API_ENTRY +#define CL_API_CALL __stdcall +#define CL_CALLBACK __stdcall +#else +#define CL_API_ENTRY +#define CL_API_CALL +#define CL_CALLBACK +#endif + +#ifdef __APPLE__ + #define CL_EXTENSION_WEAK_LINK __attribute__((weak_import)) + #define CL_API_SUFFIX__VERSION_1_0 AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_0 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define CL_API_SUFFIX__VERSION_1_1 CL_EXTENSION_WEAK_LINK + #define CL_EXT_SUFFIX__VERSION_1_1 CL_EXTENSION_WEAK_LINK + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER +#else + #define CL_EXTENSION_WEAK_LINK + #define CL_API_SUFFIX__VERSION_1_0 + #define CL_EXT_SUFFIX__VERSION_1_0 + #define CL_API_SUFFIX__VERSION_1_1 + #define CL_EXT_SUFFIX__VERSION_1_1 + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED +#endif + +#if (defined (_WIN32) && defined(_MSC_VER)) + +/* scalar types */ +typedef signed __int8 cl_char; +typedef unsigned __int8 cl_uchar; +typedef signed __int16 cl_short; +typedef unsigned __int16 cl_ushort; +typedef signed __int32 cl_int; +typedef unsigned __int32 cl_uint; +typedef signed __int64 cl_long; +typedef unsigned __int64 cl_ulong; + +typedef unsigned __int16 cl_half; +typedef float cl_float; +typedef double cl_double; + +/* Macro names and corresponding values defined by OpenCL */ +#define CL_CHAR_BIT 8 +#define CL_SCHAR_MAX 127 +#define CL_SCHAR_MIN (-127-1) +#define CL_CHAR_MAX CL_SCHAR_MAX +#define CL_CHAR_MIN CL_SCHAR_MIN +#define CL_UCHAR_MAX 255 +#define CL_SHRT_MAX 32767 +#define CL_SHRT_MIN (-32767-1) +#define CL_USHRT_MAX 65535 +#define CL_INT_MAX 2147483647 +#define CL_INT_MIN (-2147483647-1) +#define CL_UINT_MAX 0xffffffffU +#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) +#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) +#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) + +#define CL_FLT_DIG 6 +#define CL_FLT_MANT_DIG 24 +#define CL_FLT_MAX_10_EXP +38 +#define CL_FLT_MAX_EXP +128 +#define CL_FLT_MIN_10_EXP -37 +#define CL_FLT_MIN_EXP -125 +#define CL_FLT_RADIX 2 +#define CL_FLT_MAX 340282346638528859811704183484516925440.0f +#define CL_FLT_MIN 1.175494350822287507969e-38f +#define CL_FLT_EPSILON 0x1.0p-23f + +#define CL_DBL_DIG 15 +#define CL_DBL_MANT_DIG 53 +#define CL_DBL_MAX_10_EXP +308 +#define CL_DBL_MAX_EXP +1024 +#define CL_DBL_MIN_10_EXP -307 +#define CL_DBL_MIN_EXP -1021 +#define CL_DBL_RADIX 2 +#define CL_DBL_MAX 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 +#define CL_DBL_MIN 2.225073858507201383090e-308 +#define CL_DBL_EPSILON 2.220446049250313080847e-16 + +#define CL_M_E 2.718281828459045090796 +#define CL_M_LOG2E 1.442695040888963387005 +#define CL_M_LOG10E 0.434294481903251816668 +#define CL_M_LN2 0.693147180559945286227 +#define CL_M_LN10 2.302585092994045901094 +#define CL_M_PI 3.141592653589793115998 +#define CL_M_PI_2 1.570796326794896557999 +#define CL_M_PI_4 0.785398163397448278999 +#define CL_M_1_PI 0.318309886183790691216 +#define CL_M_2_PI 0.636619772367581382433 +#define CL_M_2_SQRTPI 1.128379167095512558561 +#define CL_M_SQRT2 1.414213562373095145475 +#define CL_M_SQRT1_2 0.707106781186547572737 + +#define CL_M_E_F 2.71828174591064f +#define CL_M_LOG2E_F 1.44269502162933f +#define CL_M_LOG10E_F 0.43429449200630f +#define CL_M_LN2_F 0.69314718246460f +#define CL_M_LN10_F 2.30258512496948f +#define CL_M_PI_F 3.14159274101257f +#define CL_M_PI_2_F 1.57079637050629f +#define CL_M_PI_4_F 0.78539818525314f +#define CL_M_1_PI_F 0.31830987334251f +#define CL_M_2_PI_F 0.63661974668503f +#define CL_M_2_SQRTPI_F 1.12837922573090f +#define CL_M_SQRT2_F 1.41421353816986f +#define CL_M_SQRT1_2_F 0.70710676908493f + +#define CL_NAN (CL_INFINITY - CL_INFINITY) +#define CL_HUGE_VALF ((cl_float) 1e50) +#define CL_HUGE_VAL ((cl_double) 1e500) +#define CL_MAXFLOAT CL_FLT_MAX +#define CL_INFINITY CL_HUGE_VALF + +#else + +#include + +/* scalar types */ +typedef int8_t cl_char; +typedef uint8_t cl_uchar; +typedef int16_t cl_short __attribute__((aligned(2))); +typedef uint16_t cl_ushort __attribute__((aligned(2))); +typedef int32_t cl_int __attribute__((aligned(4))); +typedef uint32_t cl_uint __attribute__((aligned(4))); +typedef int64_t cl_long __attribute__((aligned(8))); +typedef uint64_t cl_ulong __attribute__((aligned(8))); + +typedef uint16_t cl_half __attribute__((aligned(2))); +typedef float cl_float __attribute__((aligned(4))); +typedef double cl_double __attribute__((aligned(8))); + +/* Macro names and corresponding values defined by OpenCL */ +#define CL_CHAR_BIT 8 +#define CL_SCHAR_MAX 127 +#define CL_SCHAR_MIN (-127-1) +#define CL_CHAR_MAX CL_SCHAR_MAX +#define CL_CHAR_MIN CL_SCHAR_MIN +#define CL_UCHAR_MAX 255 +#define CL_SHRT_MAX 32767 +#define CL_SHRT_MIN (-32767-1) +#define CL_USHRT_MAX 65535 +#define CL_INT_MAX 2147483647 +#define CL_INT_MIN (-2147483647-1) +#define CL_UINT_MAX 0xffffffffU +#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) +#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) +#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) + +#define CL_FLT_DIG 6 +#define CL_FLT_MANT_DIG 24 +#define CL_FLT_MAX_10_EXP +38 +#define CL_FLT_MAX_EXP +128 +#define CL_FLT_MIN_10_EXP -37 +#define CL_FLT_MIN_EXP -125 +#define CL_FLT_RADIX 2 +#define CL_FLT_MAX 0x1.fffffep127f +#define CL_FLT_MIN 0x1.0p-126f +#define CL_FLT_EPSILON 0x1.0p-23f + +#define CL_DBL_DIG 15 +#define CL_DBL_MANT_DIG 53 +#define CL_DBL_MAX_10_EXP +308 +#define CL_DBL_MAX_EXP +1024 +#define CL_DBL_MIN_10_EXP -307 +#define CL_DBL_MIN_EXP -1021 +#define CL_DBL_RADIX 2 +#define CL_DBL_MAX 0x1.fffffffffffffp1023 +#define CL_DBL_MIN 0x1.0p-1022 +#define CL_DBL_EPSILON 0x1.0p-52 + +#define CL_M_E 2.718281828459045090796 +#define CL_M_LOG2E 1.442695040888963387005 +#define CL_M_LOG10E 0.434294481903251816668 +#define CL_M_LN2 0.693147180559945286227 +#define CL_M_LN10 2.302585092994045901094 +#define CL_M_PI 3.141592653589793115998 +#define CL_M_PI_2 1.570796326794896557999 +#define CL_M_PI_4 0.785398163397448278999 +#define CL_M_1_PI 0.318309886183790691216 +#define CL_M_2_PI 0.636619772367581382433 +#define CL_M_2_SQRTPI 1.128379167095512558561 +#define CL_M_SQRT2 1.414213562373095145475 +#define CL_M_SQRT1_2 0.707106781186547572737 + +#define CL_M_E_F 2.71828174591064f +#define CL_M_LOG2E_F 1.44269502162933f +#define CL_M_LOG10E_F 0.43429449200630f +#define CL_M_LN2_F 0.69314718246460f +#define CL_M_LN10_F 2.30258512496948f +#define CL_M_PI_F 3.14159274101257f +#define CL_M_PI_2_F 1.57079637050629f +#define CL_M_PI_4_F 0.78539818525314f +#define CL_M_1_PI_F 0.31830987334251f +#define CL_M_2_PI_F 0.63661974668503f +#define CL_M_2_SQRTPI_F 1.12837922573090f +#define CL_M_SQRT2_F 1.41421353816986f +#define CL_M_SQRT1_2_F 0.70710676908493f + +#if defined( __GNUC__ ) + #define CL_HUGE_VALF __builtin_huge_valf() + #define CL_HUGE_VAL __builtin_huge_val() + #define CL_NAN __builtin_nanf( "" ) +#else + #define CL_HUGE_VALF ((cl_float) 1e50) + #define CL_HUGE_VAL ((cl_double) 1e500) + float nanf( const char * ); + #define CL_NAN nanf( "" ) +#endif +#define CL_MAXFLOAT CL_FLT_MAX +#define CL_INFINITY CL_HUGE_VALF + +#endif + +#include + +/* Mirror types to GL types. Mirror types allow us to avoid deciding which headers to load based on whether we are using GL or GLES here. */ +typedef unsigned int cl_GLuint; +typedef int cl_GLint; +typedef unsigned int cl_GLenum; + +/* + * Vector types + * + * Note: OpenCL requires that all types be naturally aligned. + * This means that vector types must be naturally aligned. + * For example, a vector of four floats must be aligned to + * a 16 byte boundary (calculated as 4 * the natural 4-byte + * alignment of the float). The alignment qualifiers here + * will only function properly if your compiler supports them + * and if you don't actively work to defeat them. For example, + * in order for a cl_float4 to be 16 byte aligned in a struct, + * the start of the struct must itself be 16-byte aligned. + * + * Maintaining proper alignment is the user's responsibility. + */ + +/* Define basic vector types */ +#if defined( __VEC__ ) + #include /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */ + typedef vector unsigned char __cl_uchar16; + typedef vector signed char __cl_char16; + typedef vector unsigned short __cl_ushort8; + typedef vector signed short __cl_short8; + typedef vector unsigned int __cl_uint4; + typedef vector signed int __cl_int4; + typedef vector float __cl_float4; + #define __CL_UCHAR16__ 1 + #define __CL_CHAR16__ 1 + #define __CL_USHORT8__ 1 + #define __CL_SHORT8__ 1 + #define __CL_UINT4__ 1 + #define __CL_INT4__ 1 + #define __CL_FLOAT4__ 1 +#endif + +#if defined( __SSE__ ) + #if defined( __MINGW64__ ) + #include + #else + #include + #endif + #if defined( __GNUC__ ) + typedef float __cl_float4 __attribute__((vector_size(16))); + #else + typedef __m128 __cl_float4; + #endif + #define __CL_FLOAT4__ 1 +#endif + +#if defined( __SSE2__ ) + #if defined( __MINGW64__ ) + #include + #else + #include + #endif + #if defined( __GNUC__ ) + typedef cl_uchar __cl_uchar16 __attribute__((vector_size(16))); + typedef cl_char __cl_char16 __attribute__((vector_size(16))); + typedef cl_ushort __cl_ushort8 __attribute__((vector_size(16))); + typedef cl_short __cl_short8 __attribute__((vector_size(16))); + typedef cl_uint __cl_uint4 __attribute__((vector_size(16))); + typedef cl_int __cl_int4 __attribute__((vector_size(16))); + typedef cl_ulong __cl_ulong2 __attribute__((vector_size(16))); + typedef cl_long __cl_long2 __attribute__((vector_size(16))); + typedef cl_double __cl_double2 __attribute__((vector_size(16))); + #else + typedef __m128i __cl_uchar16; + typedef __m128i __cl_char16; + typedef __m128i __cl_ushort8; + typedef __m128i __cl_short8; + typedef __m128i __cl_uint4; + typedef __m128i __cl_int4; + typedef __m128i __cl_ulong2; + typedef __m128i __cl_long2; + typedef __m128d __cl_double2; + #endif + #define __CL_UCHAR16__ 1 + #define __CL_CHAR16__ 1 + #define __CL_USHORT8__ 1 + #define __CL_SHORT8__ 1 + #define __CL_INT4__ 1 + #define __CL_UINT4__ 1 + #define __CL_ULONG2__ 1 + #define __CL_LONG2__ 1 + #define __CL_DOUBLE2__ 1 +#endif + +#if defined( __MMX__ ) + #include + #if defined( __GNUC__ ) + typedef cl_uchar __cl_uchar8 __attribute__((vector_size(8))); + typedef cl_char __cl_char8 __attribute__((vector_size(8))); + typedef cl_ushort __cl_ushort4 __attribute__((vector_size(8))); + typedef cl_short __cl_short4 __attribute__((vector_size(8))); + typedef cl_uint __cl_uint2 __attribute__((vector_size(8))); + typedef cl_int __cl_int2 __attribute__((vector_size(8))); + typedef cl_ulong __cl_ulong1 __attribute__((vector_size(8))); + typedef cl_long __cl_long1 __attribute__((vector_size(8))); + typedef cl_float __cl_float2 __attribute__((vector_size(8))); + #else + typedef __m64 __cl_uchar8; + typedef __m64 __cl_char8; + typedef __m64 __cl_ushort4; + typedef __m64 __cl_short4; + typedef __m64 __cl_uint2; + typedef __m64 __cl_int2; + typedef __m64 __cl_ulong1; + typedef __m64 __cl_long1; + typedef __m64 __cl_float2; + #endif + #define __CL_UCHAR8__ 1 + #define __CL_CHAR8__ 1 + #define __CL_USHORT4__ 1 + #define __CL_SHORT4__ 1 + #define __CL_INT2__ 1 + #define __CL_UINT2__ 1 + #define __CL_ULONG1__ 1 + #define __CL_LONG1__ 1 + #define __CL_FLOAT2__ 1 +#endif + +#if defined( __AVX__ ) + #if defined( __MINGW64__ ) + #include + #else + #include + #endif + #if defined( __GNUC__ ) + typedef cl_float __cl_float8 __attribute__((vector_size(32))); + typedef cl_double __cl_double4 __attribute__((vector_size(32))); + #else + typedef __m256 __cl_float8; + typedef __m256d __cl_double4; + #endif + #define __CL_FLOAT8__ 1 + #define __CL_DOUBLE4__ 1 +#endif + +/* Define alignment keys */ +#if defined( __GNUC__ ) + #define CL_ALIGNED(_x) __attribute__ ((aligned(_x))) +#elif defined( _WIN32) && (_MSC_VER) + /* Alignment keys neutered on windows because MSVC can't swallow function arguments with alignment requirements */ + /* http://msdn.microsoft.com/en-us/library/373ak2y1%28VS.71%29.aspx */ + /* #include */ + /* #define CL_ALIGNED(_x) _CRT_ALIGN(_x) */ + #define CL_ALIGNED(_x) +#else + #warning Need to implement some method to align data here + #define CL_ALIGNED(_x) +#endif + +/* Indicate whether .xyzw, .s0123 and .hi.lo are supported */ +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + /* .xyzw and .s0123...{f|F} are supported */ + #define CL_HAS_NAMED_VECTOR_FIELDS 1 + /* .hi and .lo are supported */ + #define CL_HAS_HI_LO_VECTOR_FIELDS 1 +#endif + +/* Define cl_vector types */ + +/* ---- cl_charn ---- */ +typedef union +{ + cl_char CL_ALIGNED(2) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_char x, y; }; + __extension__ struct{ cl_char s0, s1; }; + __extension__ struct{ cl_char lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2; +#endif +}cl_char2; + +typedef union +{ + cl_char CL_ALIGNED(4) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_char x, y, z, w; }; + __extension__ struct{ cl_char s0, s1, s2, s3; }; + __extension__ struct{ cl_char2 lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2[2]; +#endif +#if defined( __CL_CHAR4__) + __cl_char4 v4; +#endif +}cl_char4; + +/* cl_char3 is identical in size, alignment and behavior to cl_char4. See section 6.1.5. */ +typedef cl_char4 cl_char3; + +typedef union +{ + cl_char CL_ALIGNED(8) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_char x, y, z, w; }; + __extension__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_char4 lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2[4]; +#endif +#if defined( __CL_CHAR4__) + __cl_char4 v4[2]; +#endif +#if defined( __CL_CHAR8__ ) + __cl_char8 v8; +#endif +}cl_char8; + +typedef union +{ + cl_char CL_ALIGNED(16) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_char x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_char8 lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2[8]; +#endif +#if defined( __CL_CHAR4__) + __cl_char4 v4[4]; +#endif +#if defined( __CL_CHAR8__ ) + __cl_char8 v8[2]; +#endif +#if defined( __CL_CHAR16__ ) + __cl_char16 v16; +#endif +}cl_char16; + + +/* ---- cl_ucharn ---- */ +typedef union +{ + cl_uchar CL_ALIGNED(2) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uchar x, y; }; + __extension__ struct{ cl_uchar s0, s1; }; + __extension__ struct{ cl_uchar lo, hi; }; +#endif +#if defined( __cl_uchar2__) + __cl_uchar2 v2; +#endif +}cl_uchar2; + +typedef union +{ + cl_uchar CL_ALIGNED(4) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uchar x, y, z, w; }; + __extension__ struct{ cl_uchar s0, s1, s2, s3; }; + __extension__ struct{ cl_uchar2 lo, hi; }; +#endif +#if defined( __CL_UCHAR2__) + __cl_uchar2 v2[2]; +#endif +#if defined( __CL_UCHAR4__) + __cl_uchar4 v4; +#endif +}cl_uchar4; + +/* cl_uchar3 is identical in size, alignment and behavior to cl_uchar4. See section 6.1.5. */ +typedef cl_uchar4 cl_uchar3; + +typedef union +{ + cl_uchar CL_ALIGNED(8) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uchar x, y, z, w; }; + __extension__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_uchar4 lo, hi; }; +#endif +#if defined( __CL_UCHAR2__) + __cl_uchar2 v2[4]; +#endif +#if defined( __CL_UCHAR4__) + __cl_uchar4 v4[2]; +#endif +#if defined( __CL_UCHAR8__ ) + __cl_uchar8 v8; +#endif +}cl_uchar8; + +typedef union +{ + cl_uchar CL_ALIGNED(16) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uchar x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_uchar8 lo, hi; }; +#endif +#if defined( __CL_UCHAR2__) + __cl_uchar2 v2[8]; +#endif +#if defined( __CL_UCHAR4__) + __cl_uchar4 v4[4]; +#endif +#if defined( __CL_UCHAR8__ ) + __cl_uchar8 v8[2]; +#endif +#if defined( __CL_UCHAR16__ ) + __cl_uchar16 v16; +#endif +}cl_uchar16; + + +/* ---- cl_shortn ---- */ +typedef union +{ + cl_short CL_ALIGNED(4) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_short x, y; }; + __extension__ struct{ cl_short s0, s1; }; + __extension__ struct{ cl_short lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2; +#endif +}cl_short2; + +typedef union +{ + cl_short CL_ALIGNED(8) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_short x, y, z, w; }; + __extension__ struct{ cl_short s0, s1, s2, s3; }; + __extension__ struct{ cl_short2 lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2[2]; +#endif +#if defined( __CL_SHORT4__) + __cl_short4 v4; +#endif +}cl_short4; + +/* cl_short3 is identical in size, alignment and behavior to cl_short4. See section 6.1.5. */ +typedef cl_short4 cl_short3; + +typedef union +{ + cl_short CL_ALIGNED(16) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_short x, y, z, w; }; + __extension__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_short4 lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2[4]; +#endif +#if defined( __CL_SHORT4__) + __cl_short4 v4[2]; +#endif +#if defined( __CL_SHORT8__ ) + __cl_short8 v8; +#endif +}cl_short8; + +typedef union +{ + cl_short CL_ALIGNED(32) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_short x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_short8 lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2[8]; +#endif +#if defined( __CL_SHORT4__) + __cl_short4 v4[4]; +#endif +#if defined( __CL_SHORT8__ ) + __cl_short8 v8[2]; +#endif +#if defined( __CL_SHORT16__ ) + __cl_short16 v16; +#endif +}cl_short16; + + +/* ---- cl_ushortn ---- */ +typedef union +{ + cl_ushort CL_ALIGNED(4) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ushort x, y; }; + __extension__ struct{ cl_ushort s0, s1; }; + __extension__ struct{ cl_ushort lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2; +#endif +}cl_ushort2; + +typedef union +{ + cl_ushort CL_ALIGNED(8) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ushort x, y, z, w; }; + __extension__ struct{ cl_ushort s0, s1, s2, s3; }; + __extension__ struct{ cl_ushort2 lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2[2]; +#endif +#if defined( __CL_USHORT4__) + __cl_ushort4 v4; +#endif +}cl_ushort4; + +/* cl_ushort3 is identical in size, alignment and behavior to cl_ushort4. See section 6.1.5. */ +typedef cl_ushort4 cl_ushort3; + +typedef union +{ + cl_ushort CL_ALIGNED(16) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ushort x, y, z, w; }; + __extension__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_ushort4 lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2[4]; +#endif +#if defined( __CL_USHORT4__) + __cl_ushort4 v4[2]; +#endif +#if defined( __CL_USHORT8__ ) + __cl_ushort8 v8; +#endif +}cl_ushort8; + +typedef union +{ + cl_ushort CL_ALIGNED(32) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ushort x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_ushort8 lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2[8]; +#endif +#if defined( __CL_USHORT4__) + __cl_ushort4 v4[4]; +#endif +#if defined( __CL_USHORT8__ ) + __cl_ushort8 v8[2]; +#endif +#if defined( __CL_USHORT16__ ) + __cl_ushort16 v16; +#endif +}cl_ushort16; + +/* ---- cl_intn ---- */ +typedef union +{ + cl_int CL_ALIGNED(8) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_int x, y; }; + __extension__ struct{ cl_int s0, s1; }; + __extension__ struct{ cl_int lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2; +#endif +}cl_int2; + +typedef union +{ + cl_int CL_ALIGNED(16) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_int x, y, z, w; }; + __extension__ struct{ cl_int s0, s1, s2, s3; }; + __extension__ struct{ cl_int2 lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2[2]; +#endif +#if defined( __CL_INT4__) + __cl_int4 v4; +#endif +}cl_int4; + +/* cl_int3 is identical in size, alignment and behavior to cl_int4. See section 6.1.5. */ +typedef cl_int4 cl_int3; + +typedef union +{ + cl_int CL_ALIGNED(32) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_int x, y, z, w; }; + __extension__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_int4 lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2[4]; +#endif +#if defined( __CL_INT4__) + __cl_int4 v4[2]; +#endif +#if defined( __CL_INT8__ ) + __cl_int8 v8; +#endif +}cl_int8; + +typedef union +{ + cl_int CL_ALIGNED(64) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_int x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_int8 lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2[8]; +#endif +#if defined( __CL_INT4__) + __cl_int4 v4[4]; +#endif +#if defined( __CL_INT8__ ) + __cl_int8 v8[2]; +#endif +#if defined( __CL_INT16__ ) + __cl_int16 v16; +#endif +}cl_int16; + + +/* ---- cl_uintn ---- */ +typedef union +{ + cl_uint CL_ALIGNED(8) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uint x, y; }; + __extension__ struct{ cl_uint s0, s1; }; + __extension__ struct{ cl_uint lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2; +#endif +}cl_uint2; + +typedef union +{ + cl_uint CL_ALIGNED(16) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uint x, y, z, w; }; + __extension__ struct{ cl_uint s0, s1, s2, s3; }; + __extension__ struct{ cl_uint2 lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2[2]; +#endif +#if defined( __CL_UINT4__) + __cl_uint4 v4; +#endif +}cl_uint4; + +/* cl_uint3 is identical in size, alignment and behavior to cl_uint4. See section 6.1.5. */ +typedef cl_uint4 cl_uint3; + +typedef union +{ + cl_uint CL_ALIGNED(32) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uint x, y, z, w; }; + __extension__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_uint4 lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2[4]; +#endif +#if defined( __CL_UINT4__) + __cl_uint4 v4[2]; +#endif +#if defined( __CL_UINT8__ ) + __cl_uint8 v8; +#endif +}cl_uint8; + +typedef union +{ + cl_uint CL_ALIGNED(64) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_uint x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_uint8 lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2[8]; +#endif +#if defined( __CL_UINT4__) + __cl_uint4 v4[4]; +#endif +#if defined( __CL_UINT8__ ) + __cl_uint8 v8[2]; +#endif +#if defined( __CL_UINT16__ ) + __cl_uint16 v16; +#endif +}cl_uint16; + +/* ---- cl_longn ---- */ +typedef union +{ + cl_long CL_ALIGNED(16) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_long x, y; }; + __extension__ struct{ cl_long s0, s1; }; + __extension__ struct{ cl_long lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2; +#endif +}cl_long2; + +typedef union +{ + cl_long CL_ALIGNED(32) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_long x, y, z, w; }; + __extension__ struct{ cl_long s0, s1, s2, s3; }; + __extension__ struct{ cl_long2 lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2[2]; +#endif +#if defined( __CL_LONG4__) + __cl_long4 v4; +#endif +}cl_long4; + +/* cl_long3 is identical in size, alignment and behavior to cl_long4. See section 6.1.5. */ +typedef cl_long4 cl_long3; + +typedef union +{ + cl_long CL_ALIGNED(64) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_long x, y, z, w; }; + __extension__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_long4 lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2[4]; +#endif +#if defined( __CL_LONG4__) + __cl_long4 v4[2]; +#endif +#if defined( __CL_LONG8__ ) + __cl_long8 v8; +#endif +}cl_long8; + +typedef union +{ + cl_long CL_ALIGNED(128) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_long x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_long8 lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2[8]; +#endif +#if defined( __CL_LONG4__) + __cl_long4 v4[4]; +#endif +#if defined( __CL_LONG8__ ) + __cl_long8 v8[2]; +#endif +#if defined( __CL_LONG16__ ) + __cl_long16 v16; +#endif +}cl_long16; + + +/* ---- cl_ulongn ---- */ +typedef union +{ + cl_ulong CL_ALIGNED(16) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ulong x, y; }; + __extension__ struct{ cl_ulong s0, s1; }; + __extension__ struct{ cl_ulong lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2; +#endif +}cl_ulong2; + +typedef union +{ + cl_ulong CL_ALIGNED(32) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ulong x, y, z, w; }; + __extension__ struct{ cl_ulong s0, s1, s2, s3; }; + __extension__ struct{ cl_ulong2 lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2[2]; +#endif +#if defined( __CL_ULONG4__) + __cl_ulong4 v4; +#endif +}cl_ulong4; + +/* cl_ulong3 is identical in size, alignment and behavior to cl_ulong4. See section 6.1.5. */ +typedef cl_ulong4 cl_ulong3; + +typedef union +{ + cl_ulong CL_ALIGNED(64) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ulong x, y, z, w; }; + __extension__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_ulong4 lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2[4]; +#endif +#if defined( __CL_ULONG4__) + __cl_ulong4 v4[2]; +#endif +#if defined( __CL_ULONG8__ ) + __cl_ulong8 v8; +#endif +}cl_ulong8; + +typedef union +{ + cl_ulong CL_ALIGNED(128) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_ulong x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_ulong8 lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2[8]; +#endif +#if defined( __CL_ULONG4__) + __cl_ulong4 v4[4]; +#endif +#if defined( __CL_ULONG8__ ) + __cl_ulong8 v8[2]; +#endif +#if defined( __CL_ULONG16__ ) + __cl_ulong16 v16; +#endif +}cl_ulong16; + + +/* --- cl_floatn ---- */ + +typedef union +{ + cl_float CL_ALIGNED(8) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_float x, y; }; + __extension__ struct{ cl_float s0, s1; }; + __extension__ struct{ cl_float lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2; +#endif +}cl_float2; + +typedef union +{ + cl_float CL_ALIGNED(16) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_float x, y, z, w; }; + __extension__ struct{ cl_float s0, s1, s2, s3; }; + __extension__ struct{ cl_float2 lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2[2]; +#endif +#if defined( __CL_FLOAT4__) + __cl_float4 v4; +#endif +}cl_float4; + +/* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */ +typedef cl_float4 cl_float3; + +typedef union +{ + cl_float CL_ALIGNED(32) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_float x, y, z, w; }; + __extension__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_float4 lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2[4]; +#endif +#if defined( __CL_FLOAT4__) + __cl_float4 v4[2]; +#endif +#if defined( __CL_FLOAT8__ ) + __cl_float8 v8; +#endif +}cl_float8; + +typedef union +{ + cl_float CL_ALIGNED(64) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_float x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_float8 lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2[8]; +#endif +#if defined( __CL_FLOAT4__) + __cl_float4 v4[4]; +#endif +#if defined( __CL_FLOAT8__ ) + __cl_float8 v8[2]; +#endif +#if defined( __CL_FLOAT16__ ) + __cl_float16 v16; +#endif +}cl_float16; + +/* --- cl_doublen ---- */ + +typedef union +{ + cl_double CL_ALIGNED(16) s[2]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_double x, y; }; + __extension__ struct{ cl_double s0, s1; }; + __extension__ struct{ cl_double lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2; +#endif +}cl_double2; + +typedef union +{ + cl_double CL_ALIGNED(32) s[4]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_double x, y, z, w; }; + __extension__ struct{ cl_double s0, s1, s2, s3; }; + __extension__ struct{ cl_double2 lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2[2]; +#endif +#if defined( __CL_DOUBLE4__) + __cl_double4 v4; +#endif +}cl_double4; + +/* cl_double3 is identical in size, alignment and behavior to cl_double4. See section 6.1.5. */ +typedef cl_double4 cl_double3; + +typedef union +{ + cl_double CL_ALIGNED(64) s[8]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_double x, y, z, w; }; + __extension__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7; }; + __extension__ struct{ cl_double4 lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2[4]; +#endif +#if defined( __CL_DOUBLE4__) + __cl_double4 v4[2]; +#endif +#if defined( __CL_DOUBLE8__ ) + __cl_double8 v8; +#endif +}cl_double8; + +typedef union +{ + cl_double CL_ALIGNED(128) s[16]; +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) + __extension__ struct{ cl_double x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __extension__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __extension__ struct{ cl_double8 lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2[8]; +#endif +#if defined( __CL_DOUBLE4__) + __cl_double4 v4[4]; +#endif +#if defined( __CL_DOUBLE8__ ) + __cl_double8 v8[2]; +#endif +#if defined( __CL_DOUBLE16__ ) + __cl_double16 v16; +#endif +}cl_double16; + +/* Macro to facilitate debugging + * Usage: + * Place CL_PROGRAM_STRING_DEBUG_INFO on the line before the first line of your source. + * The first line ends with: CL_PROGRAM_STRING_BEGIN \" + * Each line thereafter of OpenCL C source must end with: \n\ + * The last line ends in "; + * + * Example: + * + * const char *my_program = CL_PROGRAM_STRING_BEGIN "\ + * kernel void foo( int a, float * b ) \n\ + * { \n\ + * // my comment \n\ + * *b[ get_global_id(0)] = a; \n\ + * } \n\ + * "; + * + * This should correctly set up the line, (column) and file information for your source + * string so you can do source level debugging. + */ +#define __CL_STRINGIFY( _x ) # _x +#define _CL_STRINGIFY( _x ) __CL_STRINGIFY( _x ) +#define CL_PROGRAM_STRING_DEBUG_INFO "#line " _CL_STRINGIFY(__LINE__) " \"" __FILE__ "\" \n\n" + +#ifdef __cplusplus +} +#endif + +#endif /* __CL_PLATFORM_H */ diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/CL/opencl.h a/mtapi_plugins_c/mtapi_opencl_c/src/CL/opencl.h new file mode 100644 index 0000000..8017299 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/CL/opencl.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +/* $Revision: 11708 $ on $Date: 2010-06-14 12:06:24 +0530 (Mon, 14 Jun 2010) $ */ + +#ifndef __OPENCL_H +#define __OPENCL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __APPLE__ + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENCL_H */ + diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl.c a/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl.c new file mode 100644 index 0000000..c435e73 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +struct embb_mtapi_opencl_plugin_struct { + cl_platform_id platform_id; + cl_device_id device_id; + cl_context context; + cl_command_queue command_queue; + size_t work_group_size; + size_t work_item_sizes[3]; +}; + +typedef struct embb_mtapi_opencl_plugin_struct embb_mtapi_opencl_plugin_t; + +static embb_mtapi_opencl_plugin_t embb_mtapi_opencl_plugin; + +struct embb_mtapi_opencl_action_struct { + cl_program program; + cl_kernel kernel; + cl_mem node_local_data; + int node_local_data_size; + size_t local_work_size; + size_t element_size; +}; + +typedef struct embb_mtapi_opencl_action_struct embb_mtapi_opencl_action_t; + +struct embb_mtapi_opencl_task_struct { + cl_mem arguments; + int arguments_size; + cl_mem result_buffer; + int result_buffer_size; + cl_event kernel_finish_event; + mtapi_task_hndl_t task; +}; + +typedef struct embb_mtapi_opencl_task_struct embb_mtapi_opencl_task_t; + +static size_t round_up(size_t group_size, size_t global_size) { + size_t r = global_size % group_size; + if (r == 0) { + return global_size; + } else { + return global_size + group_size - r; + } +} + +static void CL_API_CALL opencl_task_complete( + cl_event ev, cl_int status, void * data) { + EMBB_UNUSED(ev); + EMBB_UNUSED(status); + + cl_int err; + EMBB_UNUSED_IN_RELEASE(err); + embb_mtapi_opencl_task_t * opencl_task = (embb_mtapi_opencl_task_t*)data; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid( + node->task_pool, opencl_task->task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle( + node->task_pool, opencl_task->task); + + err = clWaitForEvents(1, &opencl_task->kernel_finish_event); + assert(CL_SUCCESS == err); + + if (NULL != opencl_task->result_buffer) { + err = clReleaseMemObject(opencl_task->result_buffer); + assert(CL_SUCCESS == err); + } + if (NULL != opencl_task->arguments) { + err = clReleaseMemObject(opencl_task->arguments); + assert(CL_SUCCESS == err); + } + + embb_mtapi_task_set_state(local_task, MTAPI_TASK_COMPLETED); + } + } +} + +static void opencl_task_start( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + cl_int err; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t * local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_task->action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, local_task->action); + + embb_mtapi_opencl_plugin_t * plugin = &embb_mtapi_opencl_plugin; + embb_mtapi_opencl_action_t * opencl_action = + (embb_mtapi_opencl_action_t*)local_action->plugin_data; + embb_mtapi_opencl_task_t * opencl_task = + (embb_mtapi_opencl_task_t*)embb_alloc( + sizeof(embb_mtapi_opencl_task_t)); + + size_t elements = local_task->result_size / + opencl_action->element_size; + size_t global_work_size; + + if (0 == elements) + elements = 1; + global_work_size = + round_up(opencl_action->local_work_size, elements); + + opencl_task->task = task; + + opencl_task->arguments_size = (int)local_task->arguments_size; + if (0 < local_task->arguments_size) { + opencl_task->arguments = clCreateBuffer(plugin->context, + CL_MEM_READ_ONLY, local_task->arguments_size, NULL, &err); + } else { + opencl_task->arguments = NULL; + } + opencl_task->result_buffer_size = (int)local_task->result_size; + if (0 < local_task->result_size) { + opencl_task->result_buffer = clCreateBuffer(plugin->context, + CL_MEM_WRITE_ONLY, local_task->result_size, NULL, &err); + } else { + opencl_task->result_buffer = NULL; + } + + err = clSetKernelArg(opencl_action->kernel, 0, sizeof(cl_mem), + (const void*)&opencl_task->arguments); + err |= clSetKernelArg(opencl_action->kernel, 1, sizeof(cl_int), + (const void*)&opencl_task->arguments_size); + + err |= clSetKernelArg(opencl_action->kernel, 2, sizeof(cl_mem), + (const void*)&opencl_task->result_buffer); + err |= clSetKernelArg(opencl_action->kernel, 3, sizeof(cl_int), + (const void*)&opencl_task->result_buffer_size); + + err |= clEnqueueWriteBuffer(plugin->command_queue, + opencl_task->arguments, CL_FALSE, 0, + (size_t)opencl_task->arguments_size, local_task->arguments, + 0, NULL, NULL); + + if (CL_SUCCESS == err) { + embb_mtapi_task_set_state(local_task, MTAPI_TASK_RUNNING); + + err |= clEnqueueNDRangeKernel(plugin->command_queue, + opencl_action->kernel, 1, NULL, + &global_work_size, &opencl_action->local_work_size, 0, NULL, NULL); + err |= clEnqueueReadBuffer(plugin->command_queue, + opencl_task->result_buffer, CL_FALSE, 0, + (size_t)opencl_task->result_buffer_size, local_task->result_buffer, + 0, NULL, &opencl_task->kernel_finish_event); + err |= clSetEventCallback(opencl_task->kernel_finish_event, + CL_COMPLETE, opencl_task_complete, opencl_task); + } + + err |= clFlush(plugin->command_queue); + if (CL_SUCCESS != err) { + embb_mtapi_task_set_state(local_task, MTAPI_TASK_ERROR); + local_status = MTAPI_ERR_ACTION_FAILED; + } else { + local_status = MTAPI_SUCCESS; + } + } + } + } + + mtapi_status_set(status, local_status); +} + +static void opencl_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(task); + + mtapi_status_set(status, local_status); +} + +static void opencl_action_finalize( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + cl_int err; + EMBB_UNUSED_IN_RELEASE(err); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t * local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + embb_mtapi_opencl_action_t * opencl_action = + (embb_mtapi_opencl_action_t *)local_action->plugin_data; + if (NULL != opencl_action->node_local_data) { + err = clReleaseMemObject(opencl_action->node_local_data); + assert(CL_SUCCESS == err); + } + + err = clReleaseKernel(opencl_action->kernel); + assert(CL_SUCCESS == err); + err = clReleaseProgram(opencl_action->program); + assert(CL_SUCCESS == err); + + embb_free(opencl_action); + local_status = MTAPI_SUCCESS; + } + } + + mtapi_status_set(status, local_status); +} + +char buffer[1024]; + +void mtapi_opencl_plugin_initialize( + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + cl_int err; + embb_mtapi_opencl_plugin_t * plugin = &embb_mtapi_opencl_plugin; + + err = embb_mtapi_opencl_link_at_runtime(); + if (err <= 0) { + // OpenCL not available, or wrong version + local_status = MTAPI_ERR_FUNC_NOT_IMPLEMENTED; + } else { + // all good, go ahead + err = clGetPlatformIDs(1, &plugin->platform_id, NULL); + if (CL_SUCCESS == err) { + err = clGetDeviceIDs(plugin->platform_id, CL_DEVICE_TYPE_DEFAULT, + 1, &plugin->device_id, NULL); + } + if (CL_SUCCESS == err) { + plugin->context = clCreateContext(NULL, 1, &plugin->device_id, + NULL, NULL, &err); + } + if (CL_SUCCESS == err) { + err = clGetDeviceInfo(plugin->device_id, CL_DEVICE_MAX_WORK_GROUP_SIZE, + sizeof(size_t), &plugin->work_group_size, NULL); + } + if (CL_SUCCESS == err) { + err = clGetDeviceInfo(plugin->device_id, CL_DEVICE_MAX_WORK_ITEM_SIZES, + 3 * sizeof(size_t), &plugin->work_item_sizes[0], NULL); + } + if (CL_SUCCESS == err) { + plugin->command_queue = clCreateCommandQueue(plugin->context, + plugin->device_id, 0, &err); + } + if (CL_SUCCESS == err) { + local_status = MTAPI_SUCCESS; + } + } + + mtapi_status_set(status, local_status); +} + +void mtapi_opencl_plugin_finalize( + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + cl_int err; + EMBB_UNUSED_IN_RELEASE(err); + embb_mtapi_opencl_plugin_t * plugin = &embb_mtapi_opencl_plugin; + + /* finalization */ + err = clReleaseCommandQueue(plugin->command_queue); + assert(CL_SUCCESS == err); + err = clReleaseContext(plugin->context); + assert(CL_SUCCESS == err); + + local_status = MTAPI_SUCCESS; + mtapi_status_set(status, local_status); +} + +mtapi_action_hndl_t mtapi_opencl_action_create( + MTAPI_IN mtapi_job_id_t job_id, + MTAPI_IN char* kernel_source, + MTAPI_IN char* kernel_name, + MTAPI_IN mtapi_size_t local_work_size, + MTAPI_IN mtapi_size_t element_size, + MTAPI_IN void* node_local_data, + MTAPI_IN mtapi_size_t node_local_data_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + cl_int err; + embb_mtapi_opencl_plugin_t * plugin = &embb_mtapi_opencl_plugin; + embb_mtapi_opencl_action_t * action = + (embb_mtapi_opencl_action_t*)embb_alloc( + sizeof(embb_mtapi_opencl_action_t)); + mtapi_action_hndl_t action_hndl = { 0, 0 }; // invalid handle + size_t kernel_length = strlen(kernel_source); + mtapi_boolean_t free_program_on_error = MTAPI_FALSE; + mtapi_boolean_t free_kernel_on_error = MTAPI_FALSE; + mtapi_boolean_t free_node_local_data_on_error = MTAPI_FALSE; + + action->local_work_size = local_work_size; + action->element_size = element_size; + + /* initialization */ + action->program = clCreateProgramWithSource(plugin->context, + 1, &kernel_source, &kernel_length, &err); + if (CL_SUCCESS == err) { + free_program_on_error = MTAPI_TRUE; + err = clBuildProgram(action->program, 1, &plugin->device_id, + NULL, NULL, NULL); + } else { + err = clGetProgramBuildInfo(action->program, plugin->device_id, + CL_PROGRAM_BUILD_LOG, 1024, buffer, NULL); + } + + if (CL_SUCCESS == err) { + action->kernel = clCreateKernel(action->program, kernel_name, &err); + if (CL_SUCCESS == err) { + free_kernel_on_error = MTAPI_TRUE; + } + } + + if (0 < node_local_data_size) { + action->node_local_data = clCreateBuffer(plugin->context, CL_MEM_READ_ONLY, + node_local_data_size, NULL, &err); + if (CL_SUCCESS == err) { + free_node_local_data_on_error = MTAPI_TRUE; + } + action->node_local_data_size = (int)node_local_data_size; + if (CL_SUCCESS == err) { + err = clEnqueueWriteBuffer(plugin->command_queue, + action->node_local_data, CL_TRUE, 0, + (size_t)action->node_local_data_size, node_local_data, 0, NULL, NULL); + } + } else { + action->node_local_data = NULL; + action->node_local_data_size = 0; + } + + if (CL_SUCCESS == err) { + err = clSetKernelArg(action->kernel, 4, sizeof(cl_mem), + (const void*)&action->node_local_data); + } + if (CL_SUCCESS == err) { + err = clSetKernelArg(action->kernel, 5, sizeof(cl_int), + (const void*)&action->node_local_data_size); + } + + if (CL_SUCCESS == err) { + action_hndl = mtapi_ext_plugin_action_create( + job_id, + opencl_task_start, + opencl_task_cancel, + opencl_action_finalize, + action, + node_local_data, + node_local_data_size, + MTAPI_NULL, + &local_status); + } else { + if (free_node_local_data_on_error) { + clReleaseMemObject(action->node_local_data); + } + if (free_kernel_on_error) { + clReleaseKernel(action->kernel); + } + if (free_program_on_error) { + clReleaseProgram(action->program); + } + embb_free(action); + } + + mtapi_status_set(status, local_status); + + return action_hndl; +} diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl_runtimelinker.c a/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl_runtimelinker.c new file mode 100644 index 0000000..e96f9d9 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl_runtimelinker.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +////////////////////////////////////////////////////////////////////////// +// function pointer wrappers to hide runtime linking + +#define DECLARECLFUNC(rettype, name, params) typedef CL_API_ENTRY rettype\ + (CL_API_CALL *name##Proc)params;\ + name##Proc name##_Dynamic = 0;\ + \ + CL_API_ENTRY rettype CL_API_CALL name params + +DECLARECLFUNC(cl_int, clGetPlatformIDs, (cl_uint num_entries, + cl_platform_id * platforms, cl_uint * num_platforms)) { + return clGetPlatformIDs_Dynamic(num_entries, platforms, num_platforms); +} + +DECLARECLFUNC(cl_int, clGetPlatformInfo, (cl_platform_id platform, + cl_platform_info param_name, size_t param_value_size, void * param_value, + size_t * param_value_size_ret)) { + return clGetPlatformInfo_Dynamic(platform, param_name, param_value_size, + param_value, param_value_size_ret); +} + +DECLARECLFUNC(cl_int, clGetDeviceIDs, (cl_platform_id platform, + cl_device_type device_type, cl_uint num_entries, cl_device_id * devices, + cl_uint * num_devices)) { + return clGetDeviceIDs_Dynamic(platform, device_type, num_entries, devices, + num_devices); +} + +DECLARECLFUNC(cl_int, clGetDeviceInfo, (cl_device_id device, + cl_device_info param_name, size_t param_value_size, void * param_value, + size_t * param_value_size_ret)) { + return clGetDeviceInfo_Dynamic(device, param_name, param_value_size, + param_value, param_value_size_ret); +} + +DECLARECLFUNC(cl_context, clCreateContext, + (const cl_context_properties * properties, cl_uint num_devices, + const cl_device_id * devices, + void (CL_CALLBACK * pfn_notify)(const char *, const void *, size_t, void *), + void * user_data, cl_int * errcode_ret)) { + return clCreateContext_Dynamic(properties, num_devices, devices, pfn_notify, + user_data, errcode_ret); +} + +DECLARECLFUNC(cl_command_queue, clCreateCommandQueue, (cl_context context, + cl_device_id device, cl_command_queue_properties properties, + cl_int * errcode_ret)) { + return clCreateCommandQueue_Dynamic(context, device, properties, + errcode_ret); +} + +DECLARECLFUNC(cl_mem, clCreateBuffer, (cl_context context, cl_mem_flags flags, + size_t size, void * host_ptr, cl_int * errcode_ret)) { + return clCreateBuffer_Dynamic(context, flags, size, host_ptr, errcode_ret); +} + +DECLARECLFUNC(cl_program, clCreateProgramWithSource, (cl_context context, + cl_uint count, const char ** strings, const size_t * lengths, + cl_int * errcode_ret)) { + return clCreateProgramWithSource_Dynamic(context, count, strings, lengths, + errcode_ret); +} + +DECLARECLFUNC(cl_int, clBuildProgram, (cl_program program, cl_uint num_devices, + const cl_device_id * device_list, const char * options, + void (CL_CALLBACK * pfn_notify)(cl_program, void *), void * user_data)) { + return clBuildProgram_Dynamic(program, num_devices, device_list, options, + pfn_notify, user_data); +} + +DECLARECLFUNC(cl_int, clGetProgramBuildInfo, (cl_program program, + cl_device_id device, cl_program_build_info param_name, + size_t param_value_size, void * param_value, + size_t * param_value_size_ret)) { + return clGetProgramBuildInfo_Dynamic(program, device, param_name, + param_value_size, param_value, param_value_size_ret); +} + +DECLARECLFUNC(cl_kernel, clCreateKernel, (cl_program program, + const char * kernel_name, cl_int * errcode_ret)) { + return clCreateKernel_Dynamic(program, kernel_name, errcode_ret); +} + +DECLARECLFUNC(cl_int, clSetKernelArg, (cl_kernel kernel, cl_uint arg_index, + size_t arg_size, const void * arg_value)) { + return clSetKernelArg_Dynamic(kernel, arg_index, arg_size, arg_value); +} + +DECLARECLFUNC(cl_int, clEnqueueWriteBuffer, (cl_command_queue command_queue, + cl_mem buffer, cl_bool blocking_write, size_t offset, size_t cb, + const void * ptr, cl_uint num_events_in_wait_list, + const cl_event * event_wait_list, cl_event * event)) { + return clEnqueueWriteBuffer_Dynamic(command_queue, buffer, blocking_write, + offset, cb, ptr, num_events_in_wait_list, event_wait_list, event); +} + +DECLARECLFUNC(cl_int, clEnqueueNDRangeKernel, (cl_command_queue command_queue, + cl_kernel kernel, cl_uint work_dim, const size_t * global_work_offset, + const size_t * global_work_size, const size_t * local_work_size, + cl_uint num_events_in_wait_list, const cl_event * event_wait_list, + cl_event * event)) { + return clEnqueueNDRangeKernel_Dynamic(command_queue, kernel, work_dim, + global_work_offset, global_work_size, local_work_size, + num_events_in_wait_list, event_wait_list, event); +} + +DECLARECLFUNC(cl_int, clEnqueueReadBuffer, (cl_command_queue command_queue, + cl_mem buffer, cl_bool blocking_read, size_t offset, size_t cb, void * ptr, + cl_uint num_events_in_wait_list, const cl_event * event_wait_list, + cl_event * event)) { + return clEnqueueReadBuffer_Dynamic(command_queue, buffer, blocking_read, + offset, cb, ptr, num_events_in_wait_list, event_wait_list, event); +} + +DECLARECLFUNC(cl_int, clSetEventCallback, (cl_event event, + cl_int command_exec_callback_type, + void (CL_CALLBACK * pfn_notify)(cl_event, cl_int, void *), + void * user_data)) { + return clSetEventCallback_Dynamic(event, command_exec_callback_type, + pfn_notify, user_data); +} + +DECLARECLFUNC(cl_int, clWaitForEvents, (cl_uint num_events, + const cl_event * event_list)) { + return clWaitForEvents_Dynamic(num_events, event_list); +} + +DECLARECLFUNC(cl_int, clReleaseKernel, (cl_kernel kernel)) { + return clReleaseKernel_Dynamic(kernel); +} + +DECLARECLFUNC(cl_int, clReleaseProgram, (cl_program program)) { + return clReleaseProgram_Dynamic(program); +} + +DECLARECLFUNC(cl_int, clReleaseCommandQueue, + (cl_command_queue command_queue)) { + return clReleaseCommandQueue_Dynamic(command_queue); +} + +DECLARECLFUNC(cl_int, clReleaseContext, (cl_context context)) { + return clReleaseContext_Dynamic(context); +} + +DECLARECLFUNC(cl_int, clReleaseMemObject, (cl_mem memobj)) { + return clReleaseMemObject_Dynamic(memobj); +} + +DECLARECLFUNC(cl_int, clFlush, (cl_command_queue command_queue)) { + return clFlush_Dynamic(command_queue); +} + +DECLARECLFUNC(cl_int, clFinish, (cl_command_queue command_queue)) { + return clFinish_Dynamic(command_queue); +} + +DECLARECLFUNC(cl_sampler, clCreateSampler, (cl_context context, + cl_bool normalized_coords, cl_addressing_mode addressing_mode, + cl_filter_mode filter_mode, cl_int * errcode_ret)) { + return clCreateSampler_Dynamic(context, normalized_coords, addressing_mode, + filter_mode, errcode_ret); +} + +DECLARECLFUNC(cl_int, clReleaseSampler, (cl_sampler sampler)) { + return clReleaseSampler_Dynamic(sampler); +} + +DECLARECLFUNC(cl_mem, clCreateImage2D, (cl_context context, + cl_mem_flags flags, const cl_image_format * image_format, size_t image_width, + size_t image_height, size_t image_row_pitch, void * host_ptr, + cl_int * errcode_ret)) { + return clCreateImage2D_Dynamic(context, flags, image_format, image_width, + image_height, image_row_pitch, host_ptr, errcode_ret); +} + +DECLARECLFUNC(cl_mem, clCreateImage3D, (cl_context context, cl_mem_flags flags, + const cl_image_format * image_format, size_t image_width, + size_t image_height, size_t image_depth, size_t image_row_pitch, + size_t image_slice_pitch, void * host_ptr, cl_int * errcode_ret)) { + return clCreateImage3D_Dynamic(context, flags, image_format, image_width, + image_height, image_depth, image_row_pitch, image_slice_pitch, host_ptr, + errcode_ret); +} + +DECLARECLFUNC(cl_int, clEnqueueAcquireGLObjects, + (cl_command_queue command_queue, cl_uint num_objects, + const cl_mem * mem_objects, cl_uint num_events_in_wait_list, + const cl_event * event_wait_list, cl_event * event)) { + return clEnqueueAcquireGLObjects_Dynamic(command_queue, num_objects, + mem_objects, num_events_in_wait_list, event_wait_list, event); +} + +DECLARECLFUNC(cl_int, clEnqueueReleaseGLObjects, + (cl_command_queue command_queue, cl_uint num_objects, + const cl_mem * mem_objects, cl_uint num_events_in_wait_list, + const cl_event * event_wait_list, cl_event * event)) { + return clEnqueueReleaseGLObjects_Dynamic(command_queue, num_objects, + mem_objects, num_events_in_wait_list, event_wait_list, event); +} + +DECLARECLFUNC(cl_mem, clCreateFromGLBuffer, (cl_context context, + cl_mem_flags flags, cl_GLuint bufobj, int * errcode_ret)) { + return clCreateFromGLBuffer_Dynamic(context, flags, bufobj, errcode_ret); +} + +////////////////////////////////////////////////////////////////////////// +// system specific functions + +#ifdef _WIN32 + +#include + +#define CHECKEDIMPORT(name) name##_Dynamic = \ + (name##Proc)GetProcAddress(opencl_dll_handle, #name); \ + if (name##_Dynamic == 0) return 0; + +#else + +#include + +#define CHECKEDIMPORT(name) name##_Dynamic = \ + (name##Proc)dlsym(opencl_dll_handle, #name); \ + if (name##_Dynamic == 0) return 0; + +#endif + +#ifdef __cplusplus +extern "C" +#endif +int embb_mtapi_opencl_link_at_runtime() { +#ifdef _WIN32 + HMODULE opencl_dll_handle = LoadLibraryA("opencl.dll"); +#else + void * opencl_dll_handle = dlopen("libOpenCL.so", RTLD_LAZY); +#endif + if (opencl_dll_handle == 0) + return -1; + +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable: 4191) +#endif + CHECKEDIMPORT(clGetPlatformIDs); + CHECKEDIMPORT(clGetPlatformInfo); + CHECKEDIMPORT(clGetDeviceIDs); + CHECKEDIMPORT(clGetDeviceInfo); + CHECKEDIMPORT(clCreateContext); + CHECKEDIMPORT(clCreateCommandQueue); + CHECKEDIMPORT(clCreateBuffer); + CHECKEDIMPORT(clCreateProgramWithSource); + CHECKEDIMPORT(clBuildProgram); + CHECKEDIMPORT(clGetProgramBuildInfo); + CHECKEDIMPORT(clCreateKernel); + CHECKEDIMPORT(clSetKernelArg); + CHECKEDIMPORT(clEnqueueWriteBuffer); + CHECKEDIMPORT(clEnqueueNDRangeKernel); + CHECKEDIMPORT(clEnqueueReadBuffer); + CHECKEDIMPORT(clSetEventCallback); + CHECKEDIMPORT(clWaitForEvents); + CHECKEDIMPORT(clReleaseKernel); + CHECKEDIMPORT(clReleaseProgram); + CHECKEDIMPORT(clReleaseCommandQueue); + CHECKEDIMPORT(clReleaseContext); + CHECKEDIMPORT(clReleaseMemObject); + CHECKEDIMPORT(clFlush); + CHECKEDIMPORT(clFinish); + CHECKEDIMPORT(clCreateSampler); + CHECKEDIMPORT(clReleaseSampler); + CHECKEDIMPORT(clCreateImage2D); + CHECKEDIMPORT(clCreateImage3D); + + CHECKEDIMPORT(clEnqueueAcquireGLObjects); + CHECKEDIMPORT(clEnqueueReleaseGLObjects); + CHECKEDIMPORT(clCreateFromGLBuffer); +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + + return 1; +} diff --git b/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl_runtimelinker.h a/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl_runtimelinker.h new file mode 100644 index 0000000..47f7828 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/src/embb_mtapi_opencl_runtimelinker.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_OPENCL_C_SRC_EMBB_MTAPI_OPENCL_RUNTIMELINKER_H_ +#define MTAPI_PLUGINS_C_MTAPI_OPENCL_C_SRC_EMBB_MTAPI_OPENCL_RUNTIMELINKER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int embb_mtapi_opencl_link_at_runtime(); + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_PLUGINS_C_MTAPI_OPENCL_C_SRC_EMBB_MTAPI_OPENCL_RUNTIMELINKER_H_ diff --git b/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_linker.cc a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_linker.cc new file mode 100644 index 0000000..246c3a4 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_linker.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + + +LinkerTest::LinkerTest() { + CreateUnit("mtapi opencl linker test").Add(&LinkerTest::TestBasic, this); +} + +void LinkerTest::TestBasic() { + int result = embb_mtapi_opencl_link_at_runtime(); + bool success = result != 0; + PT_EXPECT(success); +} diff --git b/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_linker.h a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_linker.h new file mode 100644 index 0000000..5654dd5 --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_linker.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_OPENCL_C_TEST_EMBB_MTAPI_OPENCL_TEST_LINKER_H_ +#define MTAPI_PLUGINS_C_MTAPI_OPENCL_C_TEST_EMBB_MTAPI_OPENCL_TEST_LINKER_H_ + +#include + +class LinkerTest : public partest::TestCase { + public: + LinkerTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_PLUGINS_C_MTAPI_OPENCL_C_TEST_EMBB_MTAPI_OPENCL_TEST_LINKER_H_ diff --git b/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_task.cc a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_task.cc new file mode 100644 index 0000000..0a922da --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_task.cc @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#define MTAPI_CHECK_STATUS(status) \ +PT_ASSERT(MTAPI_SUCCESS == status) + +#define OPENCL_DOMAIN 1 +#define OPENCL_NODE 2 +#define OPENCL_JOB 2 + +// OpenCL Kernel Function for element by element vector addition +const char * kernel = +"__kernel void test(\n" +" __global void* arguments,\n" +" int arguments_size,\n" +" __global void* result_buffer,\n" +" int result_buffer_size,\n" +" __global void* node_local_data,\n" +" int node_local_data_size) {\n" +" int ii = get_global_id(0);\n" +" int elements = arguments_size / sizeof(float) / 2;\n" +" if (ii >= elements)" +" return;" +" __global float* a = (__global float*)arguments;\n" +" __global float* b = ((__global float*)arguments) + elements;\n" +" __global float* c = (__global float*)result_buffer;\n" +" __global float* d = (__global float*)node_local_data;\n" +" c[ii] = a[ii] + b[ii] + d[0];\n" +"}\n"; + +TaskTest::TaskTest() { + CreateUnit("mtapi opencl task test").Add(&TaskTest::TestBasic, this); +} + +void TaskTest::TestBasic() { + mtapi_status_t status; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_action_hndl_t action; + + const int kElements = 64; + float arguments[kElements * 2]; + float results[kElements]; + + for (int ii = 0; ii < kElements; ii++) { + arguments[ii] = static_cast(ii); + arguments[ii + kElements] = static_cast(ii); + } + + mtapi_opencl_plugin_initialize(&status); + if (status == MTAPI_ERR_FUNC_NOT_IMPLEMENTED) { + // OpenCL unavailable + return; + } + MTAPI_CHECK_STATUS(status); + + mtapi_initialize( + OPENCL_DOMAIN, + OPENCL_NODE, + MTAPI_NULL, + MTAPI_NULL, + &status); + MTAPI_CHECK_STATUS(status); + + float node_local = 1.0f; + action = mtapi_opencl_action_create( + OPENCL_JOB, + kernel, "test", 32, 4, + &node_local, sizeof(float), + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(OPENCL_JOB, OPENCL_DOMAIN, &status); + MTAPI_CHECK_STATUS(status); + + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, + job, + arguments, kElements * 2 * sizeof(float), + results, kElements*sizeof(float), + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + for (int ii = 0; ii < kElements; ii++) { + PT_EXPECT_EQ(results[ii], ii * 2 + 1); + } + + mtapi_action_delete(action, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + mtapi_opencl_plugin_finalize(&status); + MTAPI_CHECK_STATUS(status); +} diff --git b/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_task.h a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_task.h new file mode 100644 index 0000000..09f42fd --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/test/embb_mtapi_opencl_test_task.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MTAPI_PLUGINS_C_MTAPI_OPENCL_C_TEST_EMBB_MTAPI_OPENCL_TEST_TASK_H_ +#define MTAPI_PLUGINS_C_MTAPI_OPENCL_C_TEST_EMBB_MTAPI_OPENCL_TEST_TASK_H_ + +#include + +class TaskTest : public partest::TestCase { + public: + TaskTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_PLUGINS_C_MTAPI_OPENCL_C_TEST_EMBB_MTAPI_OPENCL_TEST_TASK_H_ diff --git b/mtapi_plugins_c/mtapi_opencl_c/test/main.cc a/mtapi_plugins_c/mtapi_opencl_c/test/main.cc new file mode 100644 index 0000000..0888cac --- /dev/null +++ a/mtapi_plugins_c/mtapi_opencl_c/test/main.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + + +PT_MAIN("MTAPI OPENCL") { + PT_RUN(LinkerTest); + PT_RUN(TaskTest); +} diff --git b/tasks_cpp/CMakeLists.txt a/tasks_cpp/CMakeLists.txt new file mode 100644 index 0000000..397be86 --- /dev/null +++ a/tasks_cpp/CMakeLists.txt @@ -0,0 +1,46 @@ +project (project_tasks_cpp) + +file(GLOB_RECURSE EMBB_TASKS_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_TASKS_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_TASKS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) + set(TASKS_CPP_AUTOMATIC_INITIALIZE 1) +else() + set(TASKS_CPP_AUTOMATIC_INITIALIZE 0) +endif() + +configure_file("include/embb/tasks/internal/cmake_config.h.in" + "include/embb/tasks/internal/cmake_config.h") + +# Execute the GroupSources macro +include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_TASKS_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_TASKS_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include) + +add_library (embb_tasks_cpp ${EMBB_TASKS_CPP_SOURCES} ${EMBB_TASKS_CPP_HEADERS}) +target_link_libraries(embb_tasks_cpp embb_mtapi_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_tasks_cpp_test ${EMBB_TASKS_CPP_TEST_SOURCES}) + target_link_libraries(embb_tasks_cpp_test embb_tasks_cpp embb_mtapi_c partest + embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_tasks_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_tasks_cpp DESTINATION lib) diff --git b/tasks_cpp/include/embb/tasks/action.h a/tasks_cpp/include/embb/tasks/action.h new file mode 100644 index 0000000..e034cbf --- /dev/null +++ a/tasks_cpp/include/embb/tasks/action.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_ACTION_H_ +#define EMBB_TASKS_ACTION_H_ + +#include +#include +#include + +namespace embb { +namespace tasks { + +/** + * A function to be spawned as a Task. + * + * \ingroup CPP_TASKS + */ +class Action { + public: + /** + * Constructs an empty Action. + */ + Action() + : function_() + , execution_policy_() { + // empty + } + + /** + * Constructs an Action from a function object. + * + * \tparam Function Function object + */ + template + Action( + Function func /**< [in] Function object */ + ) + : function_(func) + , execution_policy_() { + // empty + } + + /** + * Constructs an Action from a function object and an Affinity. + * + * \tparam Function Function object + */ + template + Action( + Function func, /**< [in] Function object */ + ExecutionPolicy execution_policy /**< [in] Execution policy */ + ) + : function_(func) + , execution_policy_(execution_policy) { + // empty + } + + /** + * Executes the Action in a given TaskContext. + */ + void operator() ( + TaskContext & context /**< [in, out] Context the operator + is executed in */ + ) { + function_(context); + } + + /** + * Returns the ExecutionPolicy specified during creation. + * \return The ExecutionPolicy of the Action + * \waitfree + */ + ExecutionPolicy GetExecutionPolicy() const { + return execution_policy_; + } + + private: + embb::base::Function function_; + ExecutionPolicy execution_policy_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_ACTION_H_ diff --git b/tasks_cpp/include/embb/tasks/continuation.h a/tasks_cpp/include/embb/tasks/continuation.h new file mode 100644 index 0000000..30c2922 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/continuation.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_CONTINUATION_H_ +#define EMBB_TASKS_CONTINUATION_H_ + +#include +#include +#include +#include + +namespace embb { +namespace tasks { + +/** + * Helper struct for Continuation. + * + * \ingroup CPP_TASKS + */ +struct ContinuationStage; + +/** + * A Continuation encapsulates a chain of \link Action Actions \endlink to be + * executed consecutively. + * + * \ingroup CPP_TASKS + */ +class Continuation { + public: + /** + * Copies a Continuation. + */ + Continuation( + Continuation const & cont /**< [in] The Continuation to copy. */ + ); + + /** + * Destroys a Continuation. + */ + ~Continuation(); + + /** + * Appends an Action to the Continuation chain. + * \returns A reference to this Continuation chain. + * \notthreadsafe + */ + Continuation & Then( + Action action /**< [in] The Action to append to the + continuation */ + ); + + /** + * Runs the Continuation chain. + * \returns The Task representing the Continuation chain. + * \notthreadsafe + */ + Task Spawn(); + + /** + * Runs the Continuation chain with the specified execution_policy. + * \returns The Task representing the Continuation chain. + * \notthreadsafe + */ + Task Spawn( + ExecutionPolicy execution_policy /**< [in] The execution policy to use */ + ); + + friend class Node; + + private: + explicit Continuation(Action action); + + void ExecuteContinuation(TaskContext & context); + + ContinuationStage * first_; + ContinuationStage * last_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_CONTINUATION_H_ diff --git b/tasks_cpp/include/embb/tasks/execution_policy.h a/tasks_cpp/include/embb/tasks/execution_policy.h new file mode 100644 index 0000000..5248900 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/execution_policy.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_EXECUTION_POLICY_H_ +#define EMBB_TASKS_EXECUTION_POLICY_H_ + +#include + +namespace embb { +namespace tasks { + +/** + * Describes the execution policy of a parallel algorithm. + * The execution policy comprises + * - the affinity of tasks to MTAPI worker threads (not CPU cores) and + * - the priority of the spawned tasks. + * + * The priority is a number between 0 (denoting the highest priority) to + * max_priorities - 1 as given during initialization using Node::Initialize(). + * The default value of max_priorities is 4. + * + * \ingroup CPP_TASKS + */ +class ExecutionPolicy{ + public: + /** + * Constructs the default execution policy. + * Sets the affinity to all worker threads and the priority to the default + * value. + */ + ExecutionPolicy(); + + /** + * Constructs an execution policy with the specified affinity and priority. + */ + ExecutionPolicy( + bool initial_affinity, /**< [in] \c true sets the affinity to + all worker threads, \c false to no + worker threads. */ + mtapi_uint_t priority /**< [in] Priority for the execution + policy. */ + ); + + /** + * Constructs an execution policy with the specified priority. + * Sets the affinity to all worker threads. + */ + explicit ExecutionPolicy( + mtapi_uint_t priority /**< [in] Priority for the execution + policy. */ + ); + + /** + * Constructs an execution policy with the specified affinity. + * Sets the priority to the default value. + */ + explicit ExecutionPolicy( + bool initial_affinity /**< [in] \c true sets the affinity to + all worker threads, \c false to no + worker threads. */ + ); + + /** + * Sets affinity to a specific worker thread. + */ + void AddWorker( + mtapi_uint_t worker /**< [in] Worker thread index */ + ); + + /** + * Removes affinity to a specific worker thread. + */ + void RemoveWorker( + mtapi_uint_t worker /**< [in] Worker thread index */ + ); + + /** + * Checks if affinity to a specific worker thread is set. + * + * \return \c true if affinity is set, otherwise \c false + */ + bool IsSetWorker( + mtapi_uint_t worker /**< [in] Worker thread index */ + ); + + /** + * Returns the number of cores the policy is affine to. + * + * \return the number of cores + */ + unsigned int GetCoreCount() const; + + /** + * Returns the affinity + * + * \return the affinity + */ + const mtapi_affinity_t &GetAffinity() const; + + /** Returns the priority + * + * \return the priority + */ + mtapi_uint_t GetPriority() const; + + friend class Task; + + private: + /** + * Default priority. + * Currently set to 0 = MAX priority. + */ + static const mtapi_uint_t DefaultPriority; + + /** + * Task Affinity. + * Maps the affinity of tasks to MTAPI worker threads (not CPU cores). + */ + mtapi_affinity_t affinity_; + + /** + * Task Priority. + */ + mtapi_uint_t priority_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_EXECUTION_POLICY_H_ diff --git b/tasks_cpp/include/embb/tasks/group.h a/tasks_cpp/include/embb/tasks/group.h new file mode 100644 index 0000000..700f09e --- /dev/null +++ a/tasks_cpp/include/embb/tasks/group.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_GROUP_H_ +#define EMBB_TASKS_GROUP_H_ + +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace tasks { + +/** + * Represents a facility to wait for multiple related + * \link Task Tasks\endlink. + * + * \ingroup CPP_TASKS + */ +class Group { + public: + /** + * Runs an Action within the Group. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action within the Group. The \c id is returned by WaitAny(). + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + mtapi_task_id_t id, /**< [in] The id to return by + WaitAny() */ + Action action /**< [in] The Action to run */ + ); + + /** + * Waits for any Task in the Group to finish for \c timeout milliseconds. + * \return The status of the Task that finished execution + * \threadsafe + */ + mtapi_status_t WaitAny( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ); + + /** + * Waits for any Task in the Group to finish for \c timeout milliseconds and + * retrieves the id given in Spawn(). + * \return The status of the Task that finished execution, \c MTAPI_TIMEOUT + * or \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t WaitAny( + mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + mtapi_task_id_t & id /**< [out] The id given to Spawn() */ + ); + + /** + * Waits for all Task in the Group to finish for \c timeout milliseconds. + * \return \c MTAPI_SUCCESS, \c MTAPI_TIMEOUT, \c MTAPI_ERR_* or the status + * of any failed Task + * \threadsafe + */ + mtapi_status_t WaitAll( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ); + + friend class embb::base::Allocation; + friend class Node; + friend class Queue; + + private: + Group(Group const & group); + Group(); + ~Group(); + + void Create(); + + mtapi_group_hndl_t handle_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_GROUP_H_ diff --git b/tasks_cpp/include/embb/tasks/internal/cmake_config.h.in a/tasks_cpp/include/embb/tasks/internal/cmake_config.h.in new file mode 100644 index 0000000..bb4ead5 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/internal/cmake_config.h.in @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_INTERNAL_CMAKE_CONFIG_H_ +#define EMBB_TASKS_INTERNAL_CMAKE_CONFIG_H_ + +/* This file is used as input for CMake. CMake creates a file cmake_config.h in + its current build directory under the path builddir/embb/tasks/internal/. From + there, the cmake_config.h can be included as usual using + #include + */ + +/** + * Is used to enable automatic initialization of the MTAPI node + */ +#define TASKS_CPP_AUTOMATIC_INITIALIZE ${TASKS_CPP_AUTOMATIC_INITIALIZE} + +#endif // EMBB_TASKS_INTERNAL_CMAKE_CONFIG_H_ diff --git b/tasks_cpp/include/embb/tasks/node.h a/tasks_cpp/include/embb/tasks/node.h new file mode 100644 index 0000000..1695f54 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/node.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_NODE_H_ +#define EMBB_TASKS_NODE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace tasks { + +/** + * A singleton representing the MTAPI runtime. + * + * \ingroup CPP_TASKS + */ +class Node { + public: + /** + * Initializes the runtime singleton using default values: + * - all available cores will be used + * - maximum number of tasks is 1024 + * - maximum number of groups is 128 + * - maximum number of queues is 16 + * - maximum queue capacity is 1024 + * - maximum number of priorities is 4. + * + * \notthreadsafe + * \throws ErrorException if the singleton was already initialized or the + * Node could not be initialized. + * \memory Allocates about 200kb of memory. + */ + static void Initialize( + mtapi_domain_t domain_id, /**< [in] The domain id to use */ + mtapi_node_t node_id /**< [in] The node id to use */ + ); + + /** + * Initializes the runtime singleton. + * \notthreadsafe + * \throws ErrorException if the singleton was already initialized or the + * Node could not be initialized. + * \memory Allocates some memory depending on the values given. + */ + static void Initialize( + mtapi_domain_t domain_id, /**< [in] The domain id to use */ + mtapi_node_t node_id, /**< [in] The node id to use */ + embb::base::CoreSet const & core_set, + /**< [in] A set of cores MTAPI should + use for its worker threads */ + mtapi_uint_t max_tasks, /**< [in] Maximum number of concurrent + \link Task Tasks \endlink */ + mtapi_uint_t max_groups, /**< [in] Maximum number of concurrent + \link Group Groups \endlink */ + mtapi_uint_t max_queues, /**< [in] Maximum number of concurrent + \link Queue Queues \endlink */ + mtapi_uint_t queue_limit, /**< [in] Maximum Queue capacity */ + mtapi_uint_t max_priorities /**< [in] Maximum number of priorities, + priorities will be between 0 and + max_priorities-1 */ + ); + + /** + * Checks if runtime is initialized. + * \return \c true if the Node singleton is already initialized, false + * otherwise + * \waitfree + */ + static bool IsInitialized(); + + /** + * Gets the instance of the runtime system. + * \return Reference to the Node singleton + * \threadsafe + */ + static Node & GetInstance(); + + /** + * Shuts the runtime system down. + * \throws ErrorException if the singleton is not initialized. + * \notthreadsafe + */ + static void Finalize(); + + /** + * Returns the number of available queues. + * \return The number of available queues + * \waitfree + */ + mtapi_uint_t GetQueueCount() const { + return queue_count_; + } + + /** + * Returns the number of available groups. + * \return The number of available groups + * \waitfree + */ + mtapi_uint_t GetGroupCount() const { + return group_count_; + } + + /** + * Returns the number of available tasks. + * \return The number of available tasks + * \waitfree + */ + mtapi_uint_t GetTaskLimit() const { + return task_limit_; + } + + /** + * Returns the number of available cores. + * \return The number of available cores + * \waitfree + */ + mtapi_uint_t GetCoreCount() const { + return core_count_; + } + + /** + * Returns the number of worker threads. + * \return The number of worker threads. + * \waitfree + */ + mtapi_uint_t GetWorkerThreadCount() const { + return worker_thread_count_; + } + + /** + * Creates a Group to launch \link Task Tasks \endlink in. + * \return A reference to the created Group + * \throws ErrorException if the Group object could not be constructed. + * \threadsafe + * \memory Allocates some memory depending on the configuration of the + * runtime. + */ + Group & CreateGroup(); + + /** + * Destroys a Group. \link Task Tasks \endlink running in the Group will + * finish execution. + * \threadsafe + */ + void DestroyGroup( + Group & group /**< [in,out] The Group to destroy */ + ); + + /** + * Creates a Queue for stream processing. The queue might execute its + * \link Task Tasks \endlink either in order or unordered. + * \return A reference to the new Queue + * \throws ErrorException if the Queue object could not be constructed. + * \threadsafe + * \memory Allocates some memory depending on the configuration of the + * runtime. + */ + Queue & CreateQueue( + mtapi_uint_t priority, /**< [in] Priority of the Queue */ + bool ordered /**< [in] \c true if the Queue should be + ordered, otherwise \c false */ + ); + + /** + * Destroys a Queue. Running \link Task Tasks \endlink will be canceled. + * \threadsafe + */ + void DestroyQueue( + Queue & queue /**< [in,out] The Queue to destroy */ + ); + + /** + * Runs an Action. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action /**< [in] The Action to execute */ + ); + + /** + * Creates a Continuation. + * \return A Continuation chain + * \threadsafe + */ + Continuation First( + Action action /**< [in] The first Action of the + Continuation chain */ + ); + + friend class embb::base::Allocation; + + private: + Node(Node const & node); + Node( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + mtapi_node_attributes_t * attr); + ~Node(); + + static void action_func( + const void* args, + mtapi_size_t args_size, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* node_local_data, + mtapi_size_t node_local_data_size, + mtapi_task_context_t * context); + + mtapi_uint_t queue_count_; + mtapi_uint_t group_count_; + mtapi_uint_t task_limit_; + mtapi_uint_t core_count_; + mtapi_uint_t worker_thread_count_; + mtapi_action_hndl_t action_handle_; + std::list queues_; + std::list groups_; + embb::base::Spinlock queue_lock_; + embb::base::Spinlock group_lock_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_NODE_H_ diff --git b/tasks_cpp/include/embb/tasks/queue.h a/tasks_cpp/include/embb/tasks/queue.h new file mode 100644 index 0000000..a4f1479 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/queue.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_QUEUE_H_ +#define EMBB_TASKS_QUEUE_H_ + +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace tasks { + +/** + * Allows for stream processing, either ordered or unordered. + * + * \ingroup CPP_TASKS + */ +class Queue { + public: + /** + * Enables the Queue. \link Task Tasks \endlink enqueued while the Queue was + * disabled are executed. + * \waitfree + */ + void Enable(); + + /** + * Disables the Queue. Running \link Task Tasks \endlink are canceled. + * \waitfree + */ + void Disable(); + + /** + * Runs an Action. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action in the specified Group + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Group const * group, /**< [in] The Group to run the Action + in */ + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action in the specified Group. The \c id is returned by + * Group::WaitAny(). + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + mtapi_task_id_t id, /**< [in] The id to return in + Group::WaitAny() */ + Group const * group, /**< [in] The Group to run the Action + in */ + Action action /**< [in] The Action to run */ + ); + + friend class embb::base::Allocation; + friend class Node; + + private: + Queue(Queue const & taskqueue); + Queue(mtapi_uint_t priority, bool ordered); + ~Queue(); + + mtapi_queue_hndl_t handle_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_QUEUE_H_ diff --git b/tasks_cpp/include/embb/tasks/task.h a/tasks_cpp/include/embb/tasks/task.h new file mode 100644 index 0000000..c78d6d1 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/task.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_TASK_H_ +#define EMBB_TASKS_TASK_H_ + +#include +#include + +namespace embb { +namespace tasks { + +/** + * A Task represents a running Action. + * + * \ingroup CPP_TASKS + */ +class Task { + public: + /** + * Constructs an empty Task + */ + Task(); + + /** + * Copies a Task + */ + Task( + Task const & task /**< The task to copy. */ + ); + + /** + * Destroys a Task + */ + ~Task(); + + /** + * Waits for Task to finish for \c timeout milliseconds. + * \return The status of the finished Task, \c MTAPI_TIMEOUT or + * \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t Wait( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ); + + /** + * Signals the Task to cancel computation. + * \waitfree + */ + void Cancel(); + + friend class Group; + friend class Queue; + friend class Node; + + private: + Task( + Action action); + + Task( + Action action, + mtapi_group_hndl_t group); + + Task( + mtapi_task_id_t id, + Action action, + mtapi_group_hndl_t group); + + Task( + Action action, + mtapi_queue_hndl_t queue); + + Task( + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group); + + Task( + mtapi_task_id_t id, + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group); + + mtapi_task_hndl_t handle_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_TASK_H_ diff --git b/tasks_cpp/include/embb/tasks/task_context.h a/tasks_cpp/include/embb/tasks/task_context.h new file mode 100644 index 0000000..9aaa663 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/task_context.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_TASK_CONTEXT_H_ +#define EMBB_TASKS_TASK_CONTEXT_H_ + +#include + +namespace embb { +namespace tasks { + +/** + * Provides information about the status of the currently running Task. + * + * \ingroup CPP_TASKS + */ +class TaskContext { + public: + /** + * Queries whether the Task running in the TaskContext should finish. + * \return \c true if the Task should finish, otherwise \c false + * \notthreadsafe + */ + bool ShouldCancel(); + + /** + * Queries the index of the worker thread the Task is running on. + * \return The worker thread index the Task is running on + * \notthreadsafe + */ + mtapi_uint_t GetCurrentCoreNumber(); + + /** + * Sets the return status of the running Task. This will be returned by + * Task::Wait() and is set to \c MTAPI_SUCCESS by default. + * \notthreadsafe + */ + void SetStatus( + mtapi_status_t error_code /**< [in] The status to return by + Task::Wait(), Group::WaitAny(), + Group::WaitAll() */ + ); + + friend class Node; + + private: + explicit TaskContext(mtapi_task_context_t * task_context); + + mtapi_task_context_t * context_; +}; + +} // namespace tasks +} // namespace embb + +#endif // EMBB_TASKS_TASK_CONTEXT_H_ diff --git b/tasks_cpp/include/embb/tasks/tasks.h a/tasks_cpp/include/embb/tasks/tasks.h new file mode 100644 index 0000000..bb3e3d5 --- /dev/null +++ a/tasks_cpp/include/embb/tasks/tasks.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_TASKS_TASKS_H_ +#define EMBB_TASKS_TASKS_H_ + +/** + * \defgroup CPP_TASKS Tasks + * Simple task management based on MTAPI. + * \ingroup CPP + */ + +#include + +#define TASKS_CPP_JOB 1 +#if TASKS_CPP_AUTOMATIC_INITIALIZE +#define TASKS_CPP_AUTOMATIC_DOMAIN_ID 1 +#define TASKS_CPP_AUTOMATIC_NODE_ID 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_TASKS_TASKS_H_ diff --git b/tasks_cpp/src/continuation.cc a/tasks_cpp/src/continuation.cc new file mode 100644 index 0000000..330fae3 --- /dev/null +++ a/tasks_cpp/src/continuation.cc @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +namespace embb { +namespace tasks { + +Continuation::Continuation(Action action) { + first_ = last_ = embb::base::Allocation::New(); + first_->action = action; + first_->next = NULL; +} + +Continuation::Continuation(Continuation const & cont) + : first_(cont.first_) + , last_(cont.last_) { +} + +Continuation::~Continuation() { +} + +void Continuation::ExecuteContinuation(TaskContext &) { + ContinuationStage * stage = first_; + Node & node = Node::GetInstance(); + while (NULL != stage) { + Task task = node.Spawn(stage->action); + task.Wait(MTAPI_INFINITE); + stage = stage->next; + } + + // delete stages + stage = first_; + while (NULL != stage) { + ContinuationStage * next = stage->next; + embb::base::Allocation::Delete(stage); + stage = next; + } +} + +Continuation & Continuation::Then(Action action) { + ContinuationStage * cur = embb::base::Allocation::New(); + cur->action = action; + cur->next = NULL; + + last_->next = cur; + last_ = cur; + + return *this; +} + +Task Continuation::Spawn() { + return Spawn(ExecutionPolicy()); +} + +Task Continuation::Spawn(ExecutionPolicy execution_policy) { + Node & node = Node::GetInstance(); + return node.Spawn( + Action( + embb::base::MakeFunction(*this, &Continuation::ExecuteContinuation), + ExecutionPolicy(execution_policy))); +} + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/src/continuationstage.h a/tasks_cpp/src/continuationstage.h new file mode 100644 index 0000000..79d260f --- /dev/null +++ a/tasks_cpp/src/continuationstage.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TASKS_CPP_SRC_CONTINUATIONSTAGE_H_ +#define TASKS_CPP_SRC_CONTINUATIONSTAGE_H_ + +#include + +namespace embb { +namespace tasks { + +struct ContinuationStage { + Action action; + ContinuationStage * next; +}; + +} // namespace tasks +} // namespace embb + +#endif // TASKS_CPP_SRC_CONTINUATIONSTAGE_H_ diff --git b/tasks_cpp/src/execution_policy.cc a/tasks_cpp/src/execution_policy.cc new file mode 100644 index 0000000..b7e9b96 --- /dev/null +++ a/tasks_cpp/src/execution_policy.cc @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +namespace embb { +namespace tasks { + +ExecutionPolicy::ExecutionPolicy() : + priority_(DefaultPriority) { +#if TASKS_CPP_AUTOMATIC_INITIALIZE + Node::GetInstance(); // MTAPI has to be initialized +#endif + mtapi_status_t status; + mtapi_affinity_init(&affinity_, MTAPI_TRUE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "Could not default construct Affinity."); + } +} + +ExecutionPolicy::ExecutionPolicy(bool initial_affinity, mtapi_uint_t priority) +:priority_(priority) { +#if TASKS_CPP_AUTOMATIC_INITIALIZE + Node::GetInstance(); // MTAPI has to be initialized +#endif + mtapi_status_t status; + mtapi_affinity_init(&affinity_, initial_affinity ? MTAPI_TRUE : MTAPI_FALSE, + &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "Could not default construct Affinity."); + } +} + +ExecutionPolicy::ExecutionPolicy(mtapi_uint_t priority) +:priority_(priority) { +#if TASKS_CPP_AUTOMATIC_INITIALIZE + Node::GetInstance(); // MTAPI has to be initialized +#endif + mtapi_status_t status; + mtapi_affinity_init(&affinity_, MTAPI_TRUE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "Could not default construct Affinity."); + } +} + +ExecutionPolicy::ExecutionPolicy(bool initial_affinity) +:priority_(DefaultPriority) { +#if TASKS_CPP_AUTOMATIC_INITIALIZE + Node::GetInstance(); // MTAPI has to be initialized +#endif + mtapi_status_t status; + mtapi_affinity_init(&affinity_, initial_affinity ? MTAPI_TRUE : MTAPI_FALSE, + &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "Could not default construct Affinity."); + } +} + +void ExecutionPolicy::AddWorker(mtapi_uint_t worker) { + mtapi_status_t status; + mtapi_affinity_set(&affinity_, worker, MTAPI_TRUE, &status); + assert(MTAPI_SUCCESS == status); +} + +void ExecutionPolicy::RemoveWorker(mtapi_uint_t worker) { + mtapi_status_t status; + mtapi_affinity_set(&affinity_, worker, MTAPI_FALSE, &status); + assert(MTAPI_SUCCESS == status); +} + +bool ExecutionPolicy::IsSetWorker(mtapi_uint_t worker) { + mtapi_status_t status; + mtapi_boolean_t aff = mtapi_affinity_get(&affinity_, worker, &status); + assert(MTAPI_SUCCESS == status); + return MTAPI_TRUE == aff; +} + +unsigned int ExecutionPolicy::GetCoreCount() const { + return embb_bitset_count(&affinity_); +} + +const mtapi_affinity_t &ExecutionPolicy::GetAffinity() const { + return affinity_; +} + +mtapi_uint_t ExecutionPolicy::GetPriority() const { + return priority_; +} + +const mtapi_uint_t ExecutionPolicy::DefaultPriority = 0; + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/src/group.cc a/tasks_cpp/src/group.cc new file mode 100644 index 0000000..04971a8 --- /dev/null +++ a/tasks_cpp/src/group.cc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +namespace embb { +namespace tasks { + +Group::Group() { + Create(); +} + +Group::~Group() { + mtapi_status_t status; + mtapi_group_delete(handle_, &status); + assert(MTAPI_SUCCESS == status); +} + +void Group::Create() { + mtapi_status_t status; + handle_ = mtapi_group_create(MTAPI_GROUP_ID_NONE, MTAPI_NULL, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Group could not be constructed"); + } +} + +Task Group::Spawn(Action action) { + return Task(action, handle_); +} + +Task Group::Spawn(mtapi_task_id_t id, Action action) { + return Task(id, action, handle_); +} + +mtapi_status_t Group::WaitAny(mtapi_timeout_t timeout) { + mtapi_status_t status; + mtapi_group_wait_any(handle_, MTAPI_NULL, timeout, &status); + if (MTAPI_GROUP_COMPLETED == status) { + // group has been deleted, so recreate it for simplicity + Create(); + } + return status; +} + +mtapi_status_t Group::WaitAny( + mtapi_timeout_t timeout, + mtapi_task_id_t & result) { + mtapi_status_t status; + void * res; + mtapi_group_wait_any(handle_, &res, timeout, &status); + memcpy(&result, &res, sizeof(result)); + if (MTAPI_GROUP_COMPLETED == status) { + // group has been deleted, so recreate it for simplicity + Create(); + } + return status; +} + +mtapi_status_t Group::WaitAll(mtapi_timeout_t timeout) { + mtapi_status_t status; + mtapi_group_wait_all(handle_, timeout, &status); + // group has been deleted, so recreate it for simplicity + Create(); + return status; +} + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/src/node.cc a/tasks_cpp/src/node.cc new file mode 100644 index 0000000..6d92e9c --- /dev/null +++ a/tasks_cpp/src/node.cc @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#if TASKS_CPP_AUTOMATIC_INITIALIZE +#include +#endif + +namespace { + +static embb::tasks::Node * node_instance = NULL; +#if TASKS_CPP_AUTOMATIC_INITIALIZE +static embb_spinlock_t init_mutex = { { 0 } }; +#endif + +} + +namespace embb { +namespace tasks { + +void Node::action_func( + const void* args, + mtapi_size_t /*args_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * context) { + Action * action = + reinterpret_cast(const_cast(args)); + TaskContext task_context(context); + (*action)(task_context); + embb::base::Allocation::Delete(action); +} + +Node::Node( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + mtapi_node_attributes_t * attr) { + mtapi_status_t status; + mtapi_info_t info; + mtapi_initialize(domain_id, node_id, attr, &info, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node could not initialize mtapi"); + } + mtapi_node_get_attribute(node_id, MTAPI_NODE_MAX_QUEUES, &queue_count_, + sizeof(queue_count_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_node_get_attribute(node_id, MTAPI_NODE_MAX_GROUPS, &group_count_, + sizeof(group_count_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_node_get_attribute(node_id, MTAPI_NODE_MAX_TASKS, &task_limit_, + sizeof(queue_count_), &status); + assert(MTAPI_SUCCESS == status); + core_count_ = info.hardware_concurrency; + worker_thread_count_ = embb_core_set_count(&attr->core_affinity); + action_handle_ = mtapi_action_create(TASKS_CPP_JOB, action_func, + MTAPI_NULL, 0, MTAPI_NULL, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node could not create an action"); + } +} + +Node::~Node() { + for (std::list::iterator ii = queues_.begin(); + ii != queues_.end(); + ++ii) { + embb::base::Allocation::Delete(*ii); + } + queues_.clear(); + + for (std::list::iterator ii = groups_.begin(); + ii != groups_.end(); + ++ii) { + embb::base::Allocation::Delete(*ii); + } + groups_.clear(); + + mtapi_status_t status; + mtapi_action_delete(action_handle_, MTAPI_INFINITE, &status); + assert(MTAPI_SUCCESS == status); + mtapi_finalize(&status); + assert(MTAPI_SUCCESS == status); +} + +void Node::Initialize( + mtapi_domain_t domain_id, + mtapi_node_t node_id) { + if (IsInitialized()) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node was already initialized"); + } else { + mtapi_status_t status; + mtapi_node_attributes_t attr; + mtapi_uint_t tmp; + mtapi_nodeattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + // tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_JOBS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + tmp = 1; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS_PER_JOB, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + node_instance = embb::base::Allocation::New( + domain_id, node_id, &attr); + } +} + +void Node::Initialize( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + embb::base::CoreSet const & core_set, + mtapi_uint_t max_tasks, + mtapi_uint_t max_groups, + mtapi_uint_t max_queues, + mtapi_uint_t queue_limit, + mtapi_uint_t max_priorities) { + if (IsInitialized()) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node was already initialized"); + } else { + mtapi_status_t status; + mtapi_node_attributes_t attr; + mtapi_uint_t tmp; + mtapi_nodeattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + embb_core_set_t cs; + embb_core_set_init(&cs, 0); + for (unsigned int ii = 0; embb_core_set_count(&cs) < core_set.Count(); + ii++) { + if (core_set.IsContained(ii)) { + embb_core_set_add(&cs, ii); + } + } + mtapi_nodeattr_set(&attr, MTAPI_NODE_CORE_AFFINITY, + &cs, sizeof(cs), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_TASKS, + &max_tasks, sizeof(max_tasks), &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_GROUPS, + &max_groups, sizeof(max_groups), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_QUEUES, + &max_queues, sizeof(max_queues), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_QUEUE_LIMIT, + &queue_limit, sizeof(queue_limit), &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_JOBS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + tmp = 1; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS_PER_JOB, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_PRIORITIES, + &max_priorities, sizeof(max_priorities), &status); + assert(MTAPI_SUCCESS == status); + node_instance = embb::base::Allocation::New( + domain_id, node_id, &attr); + } +} + +bool Node::IsInitialized() { + return NULL != node_instance; +} + +Node & Node::GetInstance() { +#if TASKS_CPP_AUTOMATIC_INITIALIZE + if (!IsInitialized()) { + embb_spin_lock(&init_mutex); + if (!IsInitialized()) { + Node::Initialize( + TASKS_CPP_AUTOMATIC_DOMAIN_ID, TASKS_CPP_AUTOMATIC_NODE_ID); + atexit(Node::Finalize); + } + embb_spin_unlock(&init_mutex); + } + return *node_instance; +#else + if (IsInitialized()) { + return *node_instance; + } else { + EMBB_THROW(embb::base::ErrorException, + "embb::tasks::Node is not initialized"); + } +#endif +} + +void Node::Finalize() { + if (IsInitialized()) { + embb::base::Allocation::Delete(node_instance); + node_instance = NULL; + } else { + EMBB_THROW(embb::base::ErrorException, + "embb::tasks::Node is not initialized"); + } +} + +Group & Node::CreateGroup() { + Group * group = embb::base::Allocation::New(); + while (!group_lock_.TryLock(1024)) { + embb::base::Thread::CurrentYield(); + } + groups_.push_back(group); + group_lock_.Unlock(); + return *group; +} + +void Node::DestroyGroup(Group & group) { + std::list::iterator ii = + std::find(groups_.begin(), groups_.end(), &group); + if (ii != groups_.end()) { + embb::base::Allocation::Delete(*ii); + groups_.erase(ii); + } +} + +Queue & Node::CreateQueue(mtapi_uint_t priority, bool ordered) { + Queue * queue = embb::base::Allocation::New(priority, ordered); + while (!queue_lock_.TryLock(1024)) { + embb::base::Thread::CurrentYield(); + } + queues_.push_back(queue); + queue_lock_.Unlock(); + return *queue; +} + +void Node::DestroyQueue(Queue & queue) { + std::list::iterator ii = + std::find(queues_.begin(), queues_.end(), &queue); + if (ii != queues_.end()) { + embb::base::Allocation::Delete(*ii); + queues_.erase(ii); + } +} + +Task Node::Spawn(Action action) { + return Task(action); +} + +Continuation Node::First(Action action) { + return Continuation(action); +} + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/src/queue.cc a/tasks_cpp/src/queue.cc new file mode 100644 index 0000000..efc574a --- /dev/null +++ a/tasks_cpp/src/queue.cc @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +namespace embb { +namespace tasks { + +Queue::Queue(mtapi_uint_t priority, bool ordered) { + mtapi_status_t status; + mtapi_queue_attributes_t attr; + mtapi_boolean_t bb; + mtapi_queueattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_queueattr_set(&attr, MTAPI_QUEUE_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + bb = ordered ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_queueattr_set(&attr, MTAPI_QUEUE_ORDERED, + &bb, sizeof(bb), &status); + assert(MTAPI_SUCCESS == status); + bb = MTAPI_TRUE; + mtapi_queueattr_set(&attr, MTAPI_QUEUE_RETAIN, + &bb, sizeof(bb), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(TASKS_CPP_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + handle_ = mtapi_queue_create(MTAPI_QUEUE_ID_NONE, job, &attr, &status); + // Handle MTAPI error status in appropriate exceptions + if (status == MTAPI_SUCCESS) { + return; + } else if (status == MTAPI_ERR_QUEUE_LIMIT) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Queue could not be constructed, " + "maximum number of queues exceeded"); + } else if (status == MTAPI_ERR_JOB_INVALID) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Queue could not be constructed, " + "invalid job"); + } else { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Queue could not be constructed"); + } +} + +Queue::~Queue() { + mtapi_status_t status; + mtapi_queue_delete(handle_, MTAPI_INFINITE, &status); + assert(MTAPI_SUCCESS == status); +} + +void Queue::Enable() { + mtapi_status_t status; + mtapi_queue_enable(handle_, &status); + assert(MTAPI_SUCCESS == status); +} + +void Queue::Disable() { + mtapi_status_t status; + mtapi_queue_disable(handle_, MTAPI_INFINITE, &status); + assert(MTAPI_SUCCESS == status); +} + +Task Queue::Spawn(Action action) { + return Task(action, handle_); +} + +Task Queue::Spawn(Group const * group, Action action) { + return Task(action, handle_, group->handle_); +} + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/src/task.cc a/tasks_cpp/src/task.cc new file mode 100644 index 0000000..518b637 --- /dev/null +++ a/tasks_cpp/src/task.cc @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +namespace embb { +namespace tasks { + +Task::Task() { + handle_.id = 0; + handle_.tag = 0; +} + +Task::Task(Task const & task) + : handle_(task.handle_) { + // empty +} + +Task::Task( + Action action) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + ExecutionPolicy policy = action.GetExecutionPolicy(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &policy.priority_, sizeof(policy.priority_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &policy.affinity_, sizeof(policy.affinity_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(TASKS_CPP_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_start(MTAPI_TASK_ID_NONE, job, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, MTAPI_GROUP_NONE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + Action action, + mtapi_group_hndl_t group) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + ExecutionPolicy policy = action.GetExecutionPolicy(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &policy.priority_, sizeof(policy.priority_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &policy.affinity_, sizeof(policy.affinity_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(TASKS_CPP_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_start(MTAPI_TASK_ID_NONE, job, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + mtapi_task_id_t id, + Action action, + mtapi_group_hndl_t group) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + ExecutionPolicy policy = action.GetExecutionPolicy(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &policy.priority_, sizeof(policy.priority_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &policy.affinity_, sizeof(policy.affinity_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(TASKS_CPP_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + void * idptr = MTAPI_NULL; + memcpy(&idptr, &id, sizeof(id)); + handle_ = mtapi_task_start(id, job, + holder, sizeof(Action), idptr, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + Action action, + mtapi_queue_hndl_t queue) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + ExecutionPolicy policy = action.GetExecutionPolicy(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &policy.priority_, sizeof(policy.priority_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &policy.affinity_, sizeof(policy.affinity_), &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, MTAPI_GROUP_NONE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + ExecutionPolicy policy = action.GetExecutionPolicy(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &policy.priority_, sizeof(policy.priority_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &policy.affinity_, sizeof(policy.affinity_), &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + mtapi_task_id_t id, + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + ExecutionPolicy policy = action.GetExecutionPolicy(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &policy.priority_, sizeof(policy.priority_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &policy.affinity_, sizeof(policy.affinity_), &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + void * idptr = MTAPI_NULL; + memcpy(&idptr, &id, sizeof(id)); + handle_ = mtapi_task_enqueue(id, queue, + holder, sizeof(Action), idptr, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::~Task() { +} + +mtapi_status_t Task::Wait(mtapi_timeout_t timeout) { + mtapi_status_t status; + mtapi_task_wait(handle_, timeout, &status); + return status; +} + +void Task::Cancel() { + mtapi_status_t status; + mtapi_task_cancel(handle_, &status); + assert(MTAPI_SUCCESS == status); +} + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/src/task_context.cc a/tasks_cpp/src/task_context.cc new file mode 100644 index 0000000..f863715 --- /dev/null +++ a/tasks_cpp/src/task_context.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +namespace embb { +namespace tasks { + +TaskContext::TaskContext(mtapi_task_context_t * task_context) + : context_(task_context) { +} + +bool TaskContext::ShouldCancel() { + mtapi_status_t status; + bool result = + MTAPI_TASK_CANCELLED == mtapi_context_taskstate_get(context_, &status); + assert(MTAPI_SUCCESS == status); + return result; +} + +mtapi_uint_t TaskContext::GetCurrentCoreNumber() { + mtapi_status_t status; + mtapi_uint_t result = + mtapi_context_corenum_get(context_, &status); + assert(MTAPI_SUCCESS == status); + return result; +} + +void TaskContext::SetStatus(mtapi_status_t error_code) { + mtapi_status_t status; + mtapi_context_status_set(context_, error_code, &status); + assert(MTAPI_SUCCESS == status); +} + +} // namespace tasks +} // namespace embb diff --git b/tasks_cpp/test/main.cc a/tasks_cpp/test/main.cc new file mode 100644 index 0000000..2f17af7 --- /dev/null +++ a/tasks_cpp/test/main.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include + + +PT_MAIN("TASKS") { + PT_RUN(TaskTest); + PT_RUN(GroupTest); + PT_RUN(QueueTest); +} diff --git b/tasks_cpp/test/tasks_cpp_test_config.h a/tasks_cpp/test/tasks_cpp_test_config.h new file mode 100644 index 0000000..cae2208 --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_config.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TASKS_CPP_TEST_TASKS_CPP_TEST_CONFIG_H_ +#define TASKS_CPP_TEST_TASKS_CPP_TEST_CONFIG_H_ + +#include +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#endif // TASKS_CPP_TEST_TASKS_CPP_TEST_CONFIG_H_ diff --git b/tasks_cpp/test/tasks_cpp_test_group.cc a/tasks_cpp/test/tasks_cpp_test_group.cc new file mode 100644 index 0000000..75cab3a --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_group.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +struct result_example_struct { + mtapi_uint_t value1; + mtapi_uint_t value2; +}; + +typedef struct result_example_struct result_example_t; + +static void testGroupAction(embb::tasks::TaskContext & /*context*/) { + // emtpy +} + +static void testDoSomethingElse() { +} + +GroupTest::GroupTest() { + CreateUnit("tasks_cpp group test").Add(&GroupTest::TestBasic, this); +} + +void GroupTest::TestBasic() { + embb::tasks::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::tasks::Node & node = embb::tasks::Node::GetInstance(); + embb::tasks::Group & group = node.CreateGroup(); + embb::tasks::Task task; + + for (int ii = 0; ii < 4; ii++) { + task = group.Spawn(testGroupAction); + } + testDoSomethingElse(); + group.WaitAll(MTAPI_INFINITE); + + for (int ii = 0; ii < 4; ii++) { + task = group.Spawn(mtapi_task_id_t(ii + 1), testGroupAction); + } + testDoSomethingElse(); + mtapi_status_t status; + mtapi_task_id_t result; + while (MTAPI_SUCCESS == (status = group.WaitAny(MTAPI_INFINITE, result))) { + // empty + } + + node.DestroyGroup(group); + + embb::tasks::Node::Finalize(); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} diff --git b/tasks_cpp/test/tasks_cpp_test_group.h a/tasks_cpp/test/tasks_cpp_test_group.h new file mode 100644 index 0000000..fe212ea --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_group.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TASKS_CPP_TEST_TASKS_CPP_TEST_GROUP_H_ +#define TASKS_CPP_TEST_TASKS_CPP_TEST_GROUP_H_ + +#include + +class GroupTest : public partest::TestCase { + public: + GroupTest(); + + private: + void TestBasic(); +}; + +#endif // TASKS_CPP_TEST_TASKS_CPP_TEST_GROUP_H_ diff --git b/tasks_cpp/test/tasks_cpp_test_queue.cc a/tasks_cpp/test/tasks_cpp_test_queue.cc new file mode 100644 index 0000000..eb6bc87 --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_queue.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 +#define QUEUE_TEST_ID 17 + +static void testQueueAction(embb::tasks::TaskContext & /*context*/) { + // empty +} + +static void testDoSomethingElse() { +} + +QueueTest::QueueTest() { + CreateUnit("tasks_cpp queue test").Add(&QueueTest::TestBasic, this); +} + +void QueueTest::TestBasic() { + embb::tasks::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::tasks::Node & node = embb::tasks::Node::GetInstance(); + embb::tasks::Queue & queue = node.CreateQueue(0, false); + + embb::tasks::Task task = queue.Spawn(testQueueAction); + + testDoSomethingElse(); + + task.Wait(MTAPI_INFINITE); + + node.DestroyQueue(queue); + + embb::tasks::Node::Finalize(); + + PT_EXPECT_EQ(embb_get_bytes_allocated(), 0u); +} diff --git b/tasks_cpp/test/tasks_cpp_test_queue.h a/tasks_cpp/test/tasks_cpp_test_queue.h new file mode 100644 index 0000000..5c86795 --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_queue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TASKS_CPP_TEST_TASKS_CPP_TEST_QUEUE_H_ +#define TASKS_CPP_TEST_TASKS_CPP_TEST_QUEUE_H_ + +#include + +class QueueTest : public partest::TestCase { + public: + QueueTest(); + + private: + void TestBasic(); +}; + +#endif // TASKS_CPP_TEST_TASKS_CPP_TEST_QUEUE_H_ diff --git b/tasks_cpp/test/tasks_cpp_test_task.cc a/tasks_cpp/test/tasks_cpp_test_task.cc new file mode 100644 index 0000000..69c56db --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_task.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 + +static void testTaskAction( + char const * msg, + std::string * output, + embb::tasks::TaskContext & /*context*/) { + *output = msg; +} + +static void testRecursiveTaskAction( + int * value, + embb::tasks::TaskContext & /*context*/) { + embb::tasks::Node & node = embb::tasks::Node::GetInstance(); + *value = *value + 1; + if (*value < 1000) { + embb::tasks::Task task = node.Spawn( + embb::base::Bind( + testRecursiveTaskAction, value, embb::base::Placeholder::_1)); + task.Wait(MTAPI_INFINITE); + } + PT_EXPECT(*value == 1000); +} + +static void testErrorTaskAction(embb::tasks::TaskContext & context) { + context.SetStatus(MTAPI_ERR_ACTION_FAILED); +} + +static void testDoSomethingElse() { +} + +TaskTest::TaskTest() { + CreateUnit("tasks_cpp task test").Add(&TaskTest::TestBasic, this); +} + +void TaskTest::TestBasic() { + embb::tasks::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::tasks::Node & node = embb::tasks::Node::GetInstance(); + + embb::tasks::ExecutionPolicy policy(false); + PT_EXPECT_EQ(policy.GetAffinity(), 0u); + PT_EXPECT_EQ(policy.GetPriority(), 0u); + policy.AddWorker(0u); + PT_EXPECT_EQ(policy.GetAffinity(), 1u); + + if (policy.GetCoreCount() > 1) { + policy.AddWorker(1u); + PT_EXPECT_EQ(policy.GetAffinity(), 3u); + } + + policy.RemoveWorker(0u); + PT_EXPECT_EQ(policy.IsSetWorker(0), false); + + if (policy.GetCoreCount() > 1) { + PT_EXPECT_EQ(policy.GetAffinity(), 2u); + PT_EXPECT_EQ(policy.IsSetWorker(1), true); + } + std::string test; + embb::tasks::Task task = node.Spawn( + embb::base::Bind( + testTaskAction, "simple", &test, embb::base::Placeholder::_1)); + testDoSomethingElse(); + task.Wait(MTAPI_INFINITE); + PT_EXPECT(test == "simple"); + + std::string test1, test2, test3; + task = node.First( + embb::base::Bind( + testTaskAction, "first", &test1, embb::base::Placeholder::_1)). + Then(embb::base::Bind( + testTaskAction, "second", &test2, embb::base::Placeholder::_1)). + Then(embb::base::Bind( + testTaskAction, "third", &test3, embb::base::Placeholder::_1)). + Spawn(); + testDoSomethingElse(); + task.Wait(MTAPI_INFINITE); + PT_EXPECT(test1 == "first"); + PT_EXPECT(test2 == "second"); + PT_EXPECT(test3 == "third"); + + int value = 0; + task = node.Spawn( + embb::base::Bind( + testRecursiveTaskAction, &value, embb::base::Placeholder::_1)); + task.Wait(MTAPI_INFINITE); + PT_EXPECT(value == 1000); + + mtapi_status_t status; + task = node.Spawn(testErrorTaskAction); + testDoSomethingElse(); + status = task.Wait(MTAPI_INFINITE); + PT_EXPECT(MTAPI_ERR_ACTION_FAILED == status); + + embb::tasks::Node::Finalize(); + + PT_EXPECT(embb_get_bytes_allocated() == 0); +} diff --git b/tasks_cpp/test/tasks_cpp_test_task.h a/tasks_cpp/test/tasks_cpp_test_task.h new file mode 100644 index 0000000..16f683b --- /dev/null +++ a/tasks_cpp/test/tasks_cpp_test_task.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2016, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TASKS_CPP_TEST_TASKS_CPP_TEST_TASK_H_ +#define TASKS_CPP_TEST_TASKS_CPP_TEST_TASK_H_ + +#include + +class TaskTest : public partest::TestCase { + public: + TaskTest(); + + private: + void TestBasic(); +}; + +#endif // TASKS_CPP_TEST_TASKS_CPP_TEST_TASK_H_