diff --git a/cpu/esp32/Makefile b/cpu/esp32/Makefile index 1294cc435c..9a1b42f0ff 100644 --- a/cpu/esp32/Makefile +++ b/cpu/esp32/Makefile @@ -7,6 +7,10 @@ DIRS += periph DIRS += freertos DIRS += vendor +ifneq (, $(filter esp_cxx, $(USEMODULE))) + DIRS += cxx +endif + ifneq (, $(filter esp_can, $(USEMODULE))) DIRS += esp-can endif diff --git a/cpu/esp32/Makefile.include b/cpu/esp32/Makefile.include index fdf445a7c0..b25383e847 100644 --- a/cpu/esp32/Makefile.include +++ b/cpu/esp32/Makefile.include @@ -33,6 +33,10 @@ ifneq (,$(findstring core_thread_flags,$(USEMODULE))) USEMODULE += pthread endif +ifneq (,$(filter cpp,$(FEATURES_USED))) + USEMODULE += esp_cxx +endif + ifneq (,$(filter esp_gdbstub,$(USEMODULE))) USEMODULE += esp_gdb endif @@ -142,6 +146,10 @@ LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.rom.ld LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.rom.nanofmt.ld LINKFLAGS += -nostdlib -lgcc -Wl,-gc-sections +ifneq (,$(filter esp_cxx,$(USEMODULE))) + UNDEF += $(BINDIR)/esp_cxx/cxa_guard.o +endif + # The ELFFILE is the base one used for flashing FLASHFILE ?= $(ELFFILE) diff --git a/cpu/esp32/cxx/Makefile b/cpu/esp32/cxx/Makefile new file mode 100644 index 0000000000..7593ffc7dd --- /dev/null +++ b/cpu/esp32/cxx/Makefile @@ -0,0 +1,3 @@ +MODULE=esp_cxx + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/cxx/cxa_guard.cpp b/cpu/esp32/cxx/cxa_guard.cpp new file mode 100644 index 0000000000..a18eb0f846 --- /dev/null +++ b/cpu/esp32/cxx/cxa_guard.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32_cxx + * @{ + * + * @file + * @brief C++ functions for guarded initialization of static variables + * + * @author Gunar Schorcht + * + * This module implements very basic versions of the glibc++ functions + * `__cxa_guard_acquire` and `__cxa_guard_release`. These functions operate + * on guard variables that are used to realize thread-safe static object + * initialization. + * + * The glibc++ built-in versions of these functions had to be replaced since + * they use the `pthread_once` function from module `pthread` for singleton + * objects initialization where the parameter `once` is of incompatible type. + * + * The functions implemented by this module don't use a global mutex to + * avoid deadlocks. Instead, within the initialization of a static variable, + * creating a thread that in turn tries to initialize the same static variable + * is not allowed and results in an abort. + */ + +#include +#include +#include + +#include "assert.h" +#include "esp_common_log.h" +#include "irq.h" +#include "irq_arch.h" +#include "mutex.h" + +/** + * Structure of guard variable in glibc++ + * ((char*)&__guard)[0] == 1: _GLIBCXX_GUARD_BIT (static variable already initialized) + * ((char*)&__guard)[1] == 1: _GLIBCXX_GUARD_PENDING_BIT (static variable is being initialized) + * ((char*)&__guard)[2] == 1: _GLIBCXX_GUARD_WAITING_BIT (other thread is waiting) + */ +typedef struct { + char done; /* initialization is already done */ + char pending; /* initialization is in progress */ + char waiting; /* another thread is waiting until initialization is finished */ +} guard_t; + +namespace __cxxabiv1 +{ + extern "C" + int __cxa_guard_acquire (__guard *g) + { + guard_t *_gt = (guard_t *)g; + + assert(_gt); + + /* return if the static object is already initialized */ + if (_gt->done) { + return 0; + } + + critical_enter(); + + /* if another thread is already initializing the static object */ + if (_gt->pending) { + critical_exit(); + LOG_ERROR("Recursive static object initialization\n"); + assert(0); + } + + /* mark the initialization in process and aquire */ + _gt->pending = 1; + + critical_exit(); + return 1; + } + + extern "C" + void __cxa_guard_release (__guard *g) + { + guard_t *_gt = (guard_t *)g; + + assert(_gt); + assert(_gt->pending); + + critical_enter(); + + _gt->done = 1; + _gt->pending = 0; + + critical_exit(); + } + + extern "C" + void __cxa_guard_abort (__guard *g) + { + guard_t *_gt = (guard_t *)g; + + assert(_gt); + assert(_gt->pending); + + _gt->pending = 0; + } +}