From 837d8e4e199e3ee64f1e6a47dbaa7119984b5a39 Mon Sep 17 00:00:00 2001 From: FritzFlorian Date: Wed, 20 Mar 2019 10:48:54 +0100 Subject: [PATCH] Add test to prohibit usage of new. We force linker errors if the new operator is used somewhere. See NOTES.md for details. --- CMakeLists.txt | 1 + NOTES.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ app/test_for_new/CMakeLists.txt | 4 ++++ app/test_for_new/main.cpp | 14 ++++++++++++++ lib/pls/CMakeLists.txt | 2 +- lib/pls/include/pls/internal/base/prohibit_new.h | 31 +++++++++++++++++++++++++++++++ lib/pls/include/pls/internal/base/thread.h | 15 +++++++++------ 7 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 NOTES.md create mode 100644 app/test_for_new/CMakeLists.txt create mode 100644 app/test_for_new/main.cpp create mode 100644 lib/pls/include/pls/internal/base/prohibit_new.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c0c2b42..92b9eca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_subdirectory(lib/pls) # Include examples add_subdirectory(app/playground) +add_subdirectory(app/test_for_new) # Add optional tests option(PACKAGE_TESTS "Build the tests" ON) diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..f0ab970 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,47 @@ +# Notes + +A collection of stuff that we noticed during development. +Useful later on two write a project report and to go back +in time to find out why certain decisions where made. + +## 20.03.2018 - Prohibit New + +We want to write this library without using any runtime memory +allocation to better fit the needs of the embedded marked. +To make sure we do not do so we add a trick: +we link an new implementation into the project (when testing) that +will cause an linker error if new is used somewhere. +If the linker reports such an error we can switch to debugging +by using a new implementation with a break point in it. + +That way we for example ruled out std::thread, as we found the dynamic +memory allocation used in it. + +## 20.03.2018 - callable objects and memory allocation / why we use no std::thread + +When working with any sort of functionality that can be passed +to an object or function it is usually passed as: +1. an function pointer and a list of parameter values +2. an lambda, capturing any surrounding parameters needed + +When we want to pass ANY functionality (with any number of parameters +or captured variables) we can not determine the amount of memory before +the call is done, making the callable (function + parameters) dynamicly +sized. + +This can be a problem when implementing e.g. a thread class, +as the callable has to be stored somewhere. The **std::thread** +implementation allocates memory at runtime using **new** when +called with any form of parameters for the started function. +Because of this (and because the implementation can differ from +system to system) we decided to not provide an **std::thread** backend +for our internal thread class (that does not use dynamic memory, +as it lives on the stack, knowing its size at compile time using +templates). + +Lambdas can be used, as long as we are sure the outer scope still exists +while executing (lambda lies in the callers stack), or if we copy the +lambda manually to some memory that we know will persist during the call. +It is important to know that the lambda wont be freed while it is +used, as the captured variables used inside the body are held in the +lambda object. diff --git a/app/test_for_new/CMakeLists.txt b/app/test_for_new/CMakeLists.txt new file mode 100644 index 0000000..06a77bd --- /dev/null +++ b/app/test_for_new/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(test_for_new main.cpp) + +# Example for adding the library to your app (as a cmake project dependency) +target_link_libraries(test_for_new pls) diff --git a/app/test_for_new/main.cpp b/app/test_for_new/main.cpp new file mode 100644 index 0000000..245c292 --- /dev/null +++ b/app/test_for_new/main.cpp @@ -0,0 +1,14 @@ +#include +#include + +using namespace pls::internal::base; + +int global = 0; + + +int main() { + // Try to use every feature, to trigger the prohibited use of new if found somewhere + auto t1 = create_thread([] (){}, 0); + t1.start(); + t1.join(); +} diff --git a/lib/pls/CMakeLists.txt b/lib/pls/CMakeLists.txt index 3686507..a7e0624 100644 --- a/lib/pls/CMakeLists.txt +++ b/lib/pls/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(pls STATIC include/pls/internal/base/choose_threading.h src/internal/base/spin_lock.cpp include/pls/internal/base/spin_lock.h src/internal/base/thread.cpp include/pls/internal/base/thread.h - ) + include/pls/internal/base/prohibit_new.h) # Settings for our project... # ...pthreads or C++ 11 threads diff --git a/lib/pls/include/pls/internal/base/prohibit_new.h b/lib/pls/include/pls/internal/base/prohibit_new.h new file mode 100644 index 0000000..baf8e01 --- /dev/null +++ b/lib/pls/include/pls/internal/base/prohibit_new.h @@ -0,0 +1,31 @@ +// Prevents the use of the new operator by forcing a linker error if it is used. +// Simply include this in your application and the linker will tell you if you +// use new somewhere (even in third party code linked to your application). + +#ifndef PLS_PROHIBIT_NEW_H +#define PLS_PROHIBIT_NEW_H + +#include + +// Comment this out and set a debug point in the std::cout +// line to find out where the new is located in case you +// get an linker error. +//#define NEW_LINK_ERROR + +#ifdef NEW_LINK_ERROR +// This will cause a linker error if new is used in the code. +// We also exit if it is somehow still called. +inline void * operator new (std::size_t) { + extern int bare_new_erroneously_called(); + exit(bare_new_erroneously_called() | 1); +} +#else +// Use this + debugging point to find out where we use a new +inline void * operator new (std::size_t) { +// extern int bare_new_erroneously_called(); + std::cout << "New called! Exiting application, no dynamic memory allocation allowed!"; + exit(1); +} +#endif + +#endif //PLS_PROHIBIT_NEW_H diff --git a/lib/pls/include/pls/internal/base/thread.h b/lib/pls/include/pls/internal/base/thread.h index b641a3a..7aebfdc 100644 --- a/lib/pls/include/pls/internal/base/thread.h +++ b/lib/pls/include/pls/internal/base/thread.h @@ -50,7 +50,7 @@ namespace pls { return reinterpret_cast(pthread_getspecific(local_storage_key_)); #endif #ifdef PLS_USING_CPP_THREADS - reinterpret_cast(local_storage_); + return reinterpret_cast(local_storage_); #endif } @@ -111,13 +111,16 @@ namespace pls { explicit thread(const Function& function, const State& state): function_{function}, state_{state}, - std_thread_{0} {}; + std_thread_{} {}; + + static void start_internal(void* my_thread_pointer) { + auto my_thread = reinterpret_cast(my_thread_pointer); + this_thread::local_storage_ = reinterpret_cast(&my_thread->state_); + my_thread->function_(); + } void start() { - std_thread_ = std::thread([=](){ - local_storage = reinterpret_cast<&this->state_>; - this->function_(); - }); + std_thread_ = std::thread(start_internal, (void*)this); } #endif public: -- libgit2 0.26.0