diff --git a/libs/error_injector/CMakeLists.txt b/libs/error_injector/CMakeLists.txt index 769f7bd67..fd808bc51 100644 --- a/libs/error_injector/CMakeLists.txt +++ b/libs/error_injector/CMakeLists.txt @@ -38,6 +38,7 @@ add_subdirectory(socket) add_subdirectory(pthread) add_subdirectory(unistd) add_subdirectory(dirent) +add_subdirectory(uv) celix_subproject(ERROR_INJECTOR_MDNSRESPONDER "Option to enable building the mdnsresponder error injector" OFF) if (ERROR_INJECTOR_MDNSRESPONDER) diff --git a/libs/error_injector/uv/CMakeLists.txt b/libs/error_injector/uv/CMakeLists.txt new file mode 100644 index 000000000..4fa31b399 --- /dev/null +++ b/libs/error_injector/uv/CMakeLists.txt @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +find_package(libuv REQUIRED) + +add_library(uv_ei STATIC src/uv_ei.cc) + +target_include_directories(uv_ei PUBLIC include) +target_link_libraries(uv_ei PUBLIC Celix::error_injector libuv::uv) + +target_link_options(uv_ei INTERFACE + LINKER:--wrap,uv_key_create +) +add_library(Celix::uv_ei ALIAS uv_ei) diff --git a/libs/error_injector/uv/include/uv_ei.h b/libs/error_injector/uv/include/uv_ei.h new file mode 100644 index 000000000..68f02ae85 --- /dev/null +++ b/libs/error_injector/uv/include/uv_ei.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CELIX_UV_THREADS_EI_H +#define CELIX_UV_THREADS_EI_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "celix_error_injector.h" + +CELIX_EI_DECLARE(uv_key_create, int); + +#ifdef __cplusplus +} +#endif + +#endif // CELIX_UV_THREADS_EI_H diff --git a/libs/error_injector/uv/src/uv_ei.cc b/libs/error_injector/uv/src/uv_ei.cc new file mode 100644 index 000000000..3529b67f7 --- /dev/null +++ b/libs/error_injector/uv/src/uv_ei.cc @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "uv_ei.h" +#include "celix_error_injector.h" + +extern "C" { + +int __real_uv_key_create(uv_key_t* key); +CELIX_EI_DEFINE(uv_key_create, int) +int __wrap_uv_key_create(uv_key_t* key) { + CELIX_EI_IMPL(uv_key_create); + return __real_uv_key_create(key); +} + +} diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 513314b2b..7b116783f 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -124,6 +124,17 @@ if (UTILS) ) target_link_libraries(utils_cut PUBLIC ${UTILS_PUBLIC_DEPS} ${UTILS_PRIVATE_DEPS}) + #Special test lib that disable the celix_err constructor/destructor attribute usage (for testing) + add_library(utils_err_no_ctor_dtor_cut STATIC src/celix_err.c) + target_include_directories(utils_err_no_ctor_dtor_cut PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_BINARY_DIR}/celix/gen/includes/utils + ${CMAKE_BINARY_DIR}/celix/gen/src/utils + src + ) + target_compile_definitions(utils_err_no_ctor_dtor_cut PRIVATE CELIX_ERR_DISABLE_CONSTRUCTORS) + target_link_libraries(utils_err_no_ctor_dtor_cut PUBLIC ${UTILS_PUBLIC_DEPS} ${UTILS_PRIVATE_DEPS}) + add_subdirectory(gtest) endif () diff --git a/libs/utils/error_injector/celix_threads/CMakeLists.txt b/libs/utils/error_injector/celix_threads/CMakeLists.txt index f7b9c5681..8e307d157 100644 --- a/libs/utils/error_injector/celix_threads/CMakeLists.txt +++ b/libs/utils/error_injector/celix_threads/CMakeLists.txt @@ -26,9 +26,5 @@ target_link_options(threads_ei INTERFACE LINKER:--wrap,celixThreadCondition_signal LINKER:--wrap,celixThreadCondition_init LINKER:--wrap,celixThreadRwlock_create - LINKER:--wrap,celix_tss_create - LINKER:--wrap,celix_tss_delete - LINKER:--wrap,celix_tss_set - LINKER:--wrap,celix_tss_get ) add_library(Celix::threads_ei ALIAS threads_ei) diff --git a/libs/utils/error_injector/celix_threads/include/celix_threads_ei.h b/libs/utils/error_injector/celix_threads/include/celix_threads_ei.h index 80d3ea6aa..2a2bd816c 100644 --- a/libs/utils/error_injector/celix_threads/include/celix_threads_ei.h +++ b/libs/utils/error_injector/celix_threads/include/celix_threads_ei.h @@ -34,11 +34,6 @@ CELIX_EI_DECLARE(celixThreadCondition_signal, celix_status_t); CELIX_EI_DECLARE(celixThreadCondition_init, celix_status_t); CELIX_EI_DECLARE(celixThreadRwlock_create, celix_status_t); -CELIX_EI_DECLARE(celix_tss_create, celix_status_t); -CELIX_EI_DECLARE(celix_tss_delete, celix_status_t); -CELIX_EI_DECLARE(celix_tss_set, celix_status_t); -CELIX_EI_DECLARE(celix_tss_get, void*); - #ifdef __cplusplus } #endif diff --git a/libs/utils/error_injector/celix_threads/src/celix_threads_ei.cc b/libs/utils/error_injector/celix_threads/src/celix_threads_ei.cc index 698a24acd..86455b098 100644 --- a/libs/utils/error_injector/celix_threads/src/celix_threads_ei.cc +++ b/libs/utils/error_injector/celix_threads/src/celix_threads_ei.cc @@ -63,33 +63,4 @@ celix_status_t __wrap_celixThreadRwlock_create(celix_thread_rwlock_t *__rwlock, return __real_celixThreadRwlock_create(__rwlock, __attr); } - -celix_status_t __real_celix_tss_create(celix_tss_key_t* __key, void (*__destroyFunction)(void*)); -CELIX_EI_DEFINE(celix_tss_create, celix_status_t) -celix_status_t __wrap_celix_tss_create(celix_tss_key_t* __key, void (*__destroyFunction)(void*)) { - CELIX_EI_IMPL(celix_tss_create); - return __real_celix_tss_create(__key, __destroyFunction); -} - -celix_status_t __real_celix_tss_delete(celix_tss_key_t __key); -CELIX_EI_DEFINE(celix_tss_delete, celix_status_t) -celix_status_t __wrap_celix_tss_delete(celix_tss_key_t __key) { - CELIX_EI_IMPL(celix_tss_delete); - return __real_celix_tss_delete(__key); -} - -celix_status_t __real_celix_tss_set(celix_tss_key_t __key, void* __value); -CELIX_EI_DEFINE(celix_tss_set, celix_status_t) -celix_status_t __wrap_celix_tss_set(celix_tss_key_t __key, void* __value) { - CELIX_EI_IMPL(celix_tss_set); - return __real_celix_tss_set(__key, __value); -} - -void* __real_celix_tss_get(celix_tss_key_t __key); -CELIX_EI_DEFINE(celix_tss_get, void*) -void* __wrap_celix_tss_get(celix_tss_key_t __key) { - CELIX_EI_IMPL(celix_tss_get); - return __real_celix_tss_get(__key); -} - } diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 84fe3a4c2..619c36807 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -103,13 +103,23 @@ if (EI_TESTS) target_link_libraries(test_utils_celix_err_with_ei PRIVATE utils_cut Celix::malloc_ei - Celix::threads_ei GTest::gtest GTest::gtest_main ) add_test(NAME test_utils_celix_err_with_ei COMMAND test_utils_celix_err_with_ei) setup_target_for_coverage(test_utils_celix_err_with_ei SCAN_DIR ..) - #Note testing version seperated, otherwise version calls are already wrapped + add_executable(test_utils_celix_err_key_create_with_ei + src/ErrKeyCreateErrorInjectionTestSuite.cc + ) + target_link_libraries(test_utils_celix_err_key_create_with_ei PRIVATE + utils_err_no_ctor_dtor_cut + Celix::uv_ei + GTest::gtest GTest::gtest_main + ) + add_test(NAME test_utils_celix_err_key_create_with_ei COMMAND test_utils_celix_err_key_create_with_ei) + setup_target_for_coverage(test_utils_celix_err_key_create_with_ei SCAN_DIR ..) + + #Note testing version separated, otherwise version calls are already wrapped add_executable(test_utils_version_with_ei src/VersionErrorInjectionTestSuite.cc ) @@ -154,7 +164,6 @@ if (EI_TESTS) Celix::fts_ei utils_cut Celix::utils_ei - Celix::ifaddrs_ei Celix::threads_ei Celix::malloc_ei Celix::asprintf_ei diff --git a/libs/utils/gtest/src/ErrErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ErrErrorInjectionTestSuite.cc index 1fc88fd75..de20a5dad 100644 --- a/libs/utils/gtest/src/ErrErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ErrErrorInjectionTestSuite.cc @@ -23,7 +23,7 @@ #include #include "celix_err.h" -#include "celix_threads_ei.h" +#include "celix_errno.h" #include "malloc_ei.h" class ErrErrorInjectionTestSuite : public ::testing::Test { @@ -34,28 +34,10 @@ class ErrErrorInjectionTestSuite : public ::testing::Test { freopen(CAPTURE_FILENAME, "w", stderr); } ~ErrErrorInjectionTestSuite() noexcept override { - celix_ei_expect_celix_tss_set(nullptr, 0, CELIX_SUCCESS); celix_ei_expect_malloc(nullptr, 0, CELIX_SUCCESS); } }; -TEST_F(ErrErrorInjectionTestSuite, PushErrorWithTssSetFailingTest) { - //Given a primed error injection for celix_tss_set - celix_ei_expect_celix_tss_set((void*)celix_err_push, 1, CELIX_ILLEGAL_STATE); - - //When an error is pushed - celix_err_push("error message"); - - fclose(stderr); - std::ifstream tempFile{CAPTURE_FILENAME}; - std::string fileContents((std::istreambuf_iterator(tempFile)), - std::istreambuf_iterator()); - tempFile.close(); - - EXPECT_TRUE(strstr(fileContents.c_str(), "Failed to set thread specific storage for celix_err") != nullptr) << - "Expected error message not found in: " << fileContents; -} - TEST_F(ErrErrorInjectionTestSuite, PushErrorWithMallocFailingTest) { //Given a primed error injection for malloc celix_ei_expect_malloc(CELIX_EI_UNKNOWN_CALLER, 1, nullptr); diff --git a/libs/utils/gtest/src/ErrKeyCreateErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ErrKeyCreateErrorInjectionTestSuite.cc new file mode 100644 index 000000000..891fd6024 --- /dev/null +++ b/libs/utils/gtest/src/ErrKeyCreateErrorInjectionTestSuite.cc @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "celix_err.h" +#include "celix_err_private.h" +#include "uv_ei.h" + +class ErrKeyCreateErrorInjectionTestSuite : public ::testing::Test { +public: + ErrKeyCreateErrorInjectionTestSuite() = default; + + ~ErrKeyCreateErrorInjectionTestSuite() noexcept override { + celix_ei_expect_uv_key_create(nullptr, 0, 0); + } +}; + +TEST_F(ErrKeyCreateErrorInjectionTestSuite, InitThreadStorageKeyFailureAndSuccess) { + celix_ei_expect_uv_key_create((void*)celix_err_initThreadSpecificStorageKey, 0, -1); + celix_err_initThreadSpecificStorageKey(); + + celix_err_push("error message"); + EXPECT_EQ(0, celix_err_getErrorCount()); + + celix_ei_expect_uv_key_create(nullptr, 0, 0); + celix_err_initThreadSpecificStorageKey(); + celix_err_resetErrors(); + celix_err_push("error message"); + EXPECT_EQ(1, celix_err_getErrorCount()); + celix_err_deinitThreadSpecificStorageKey(); +} diff --git a/libs/utils/gtest/src/ThreadsTestSuite.cc b/libs/utils/gtest/src/ThreadsTestSuite.cc index 2a52ba15e..dac4fc09f 100644 --- a/libs/utils/gtest/src/ThreadsTestSuite.cc +++ b/libs/utils/gtest/src/ThreadsTestSuite.cc @@ -371,23 +371,6 @@ TEST_F(ThreadsTestSuite, RwLockAttrTest) { celixThreadRwlockAttr_destroy(&attr); } -TEST_F(ThreadsTestSuite, TssTest) { - celix_tss_key_t key; - celix_status_t status = celix_tss_create(&key, [](void* ptr) { free(ptr); }); - EXPECT_EQ(CELIX_SUCCESS, status); - - int* value = (int*)malloc(sizeof(int)); - *value = 123; - status = celix_tss_set(key, value); - EXPECT_EQ(CELIX_SUCCESS, status); - - value = (int*)celix_tss_get(key); - EXPECT_EQ(123, *value); - - status = celix_tss_delete(key); - EXPECT_EQ(CELIX_SUCCESS, status); -} - static void * thread_test_func_create(void * arg) { char ** test_str = (char**) arg; *test_str = strdup("SUCCESS"); diff --git a/libs/utils/include/celix_threads.h b/libs/utils/include/celix_threads.h index 45c3111f1..5bf2a1686 100644 --- a/libs/utils/include/celix_threads.h +++ b/libs/utils/include/celix_threads.h @@ -359,65 +359,6 @@ CELIX_UTILS_EXPORT celix_status_t celixThreadCondition_signal(celix_thread_cond_ CELIX_UTILS_EXPORT celix_status_t celixThread_once(celix_thread_once_t *once_control, void (*init_routine)(void)); -//Thread Specific Storage (TSS) Abstraction -typedef pthread_key_t celix_tss_key_t; - -/** - * @brief Create a thread specific storage key visible for all threads. - * - * - * Upon creation, the value NULL shall be associated with - * the new key in all active threads of the process. Upon thread creation, - * the value NULL shall be associated with all defined keys in the new thread - * - * An optional destructor function may be associated with each key value. At thread exit, if a key value has a - * non-NULL destructor pointer, and the thread has a non-NULL value associated with that key, the value of the key is - * set to NULL, and then the function pointed to is called with the previously associated value as its sole argument. - * The order of destructor calls is unspecified if more than one destructor exists for a thread when it exits. - * - * @param key The key to create. - * @param destroyFunction The function to call when the key is destroyed. - * @return CELIX_SUCCESS if the key is created successfully. - * - * @retval CELIX_ENOMEM if there was insufficient memory for the key creation. - * @retval CELIX_EAGAIN if the system lacked the necessary resources to create another thread specific data key. - */ -CELIX_UTILS_EXPORT celix_status_t celix_tss_create(celix_tss_key_t* key, void (*destroyFunction)(void*)); - -/** - * @brief Delete a thread specific storage key previously created by celix_tss_create. - * - * @param key The key to delete. - * @return CELIX_SUCCESS if the key is deleted successfully. - * - * @retval CELIX_ILLEGAL_ARGUMENT if the key is invalid. - * @retval CELIX_ILLEGAL_STATE if the key is otherwise not deleted successfully. - */ -CELIX_UTILS_EXPORT celix_status_t celix_tss_delete(celix_tss_key_t key); - -/** - * @brief Set a thread-specific value for the provide thread specific storage key. - * - * @param key The key to set the value for. - * @param value The thread-specific value to set. - * @return CELIX_SUCCESS if the value is set successfully. - * - * @retval CELIX_ILLEGAL_ARGUMENT if the key is invalid. - * @retval CELIX_ENOMEM if there was insufficient memory to set the value. - * @retval CELIX_ILLEGAL_STATE if the value is not set successfully. - */ -CELIX_UTILS_EXPORT celix_status_t celix_tss_set(celix_tss_key_t key, void* value); - -/** - * @brief Get the thread-specific value for the provided thread specific storage key. - * - * @param key The key to get the value for. - * @return The thread-specific value. - * - * @retval NULL if the key is invalid or there is no thread-specific value set for the key. - */ -CELIX_UTILS_EXPORT void* celix_tss_get(celix_tss_key_t key); - #ifdef __cplusplus } #endif diff --git a/libs/utils/src/celix_err.c b/libs/utils/src/celix_err.c index 747dcf3fd..eabd2ac10 100644 --- a/libs/utils/src/celix_err.c +++ b/libs/utils/src/celix_err.c @@ -17,14 +17,23 @@ * under the License. */ +#include "celix_err_private.h" #include "celix_err.h" #include #include #include #include +#include -#include "celix_threads.h" + +#ifdef CELIX_ERR_DISABLE_CONSTRUCTORS +#define CELIX_ERR_CONSTRUCTOR +#define CELIX_ERR_DESTRUCTOR +#else +#define CELIX_ERR_CONSTRUCTOR __attribute__((constructor)) +#define CELIX_ERR_DESTRUCTOR __attribute__((destructor)) +#endif typedef struct celix_err { char buffer[CELIX_ERR_BUFFER_SIZE]; @@ -32,22 +41,14 @@ typedef struct celix_err { } celix_err_t; -celix_tss_key_t celix_err_tssKey; +uv_key_t celix_err_tssKey; bool celix_err_tssKeyInitialized = false; -static void celix_err_destroyTssErr(void* data) { - celix_err_t* err = data; - if (err != NULL) { - free(err); - } -} - - static celix_err_t* celix_err_getRawTssErr() { if (!celix_err_tssKeyInitialized) { return NULL; } - return celix_tss_get(celix_err_tssKey); + return uv_key_get(&celix_err_tssKey); } @@ -57,7 +58,7 @@ celix_err_t* celix_err_getTssErr() { return NULL; } - celix_err_t* err = celix_tss_get(celix_err_tssKey); + celix_err_t* err = uv_key_get(&celix_err_tssKey); if (err) { return err; } @@ -65,37 +66,31 @@ celix_err_t* celix_err_getTssErr() { err = malloc(sizeof(*err)); if (err) { err->pos = 0; //no entry - celix_status_t status = celix_tss_set(celix_err_tssKey, err); - if (status != CELIX_SUCCESS) { - fprintf(stderr, "Failed to set thread specific storage for celix_err\n"); - free(err); - err = NULL; - } + uv_key_set(&celix_err_tssKey, err); } else { fprintf(stderr, "Failed to allocate memory for celix_err\n"); } return err; } -__attribute__((constructor)) void celix_err_initThreadSpecificStorageKey() { - celix_status_t status = celix_tss_create(&celix_err_tssKey, celix_err_destroyTssErr); - if (status == CELIX_SUCCESS) { +CELIX_ERR_CONSTRUCTOR void celix_err_initThreadSpecificStorageKey() { + if (celix_err_tssKeyInitialized) { + return; + } + int rc = uv_key_create(&celix_err_tssKey); + if (rc == 0) { celix_err_tssKeyInitialized = true; } else { fprintf(stderr,"Failed to create thread specific storage key for celix_err\n"); } } -__attribute__((destructor)) void celix_err_deinitThreadSpecificStorageKey() { +CELIX_ERR_DESTRUCTOR void celix_err_deinitThreadSpecificStorageKey() { if (!celix_err_tssKeyInitialized) { - fprintf(stderr, "celix_err_tssKey is not initialized\n"); - return; - } - - celix_status_t status = celix_tss_delete(celix_err_tssKey); - if (status != CELIX_SUCCESS) { - fprintf(stderr,"Failed to delete thread specific storage key for celix_err\n"); + return; //note: error is logged during init } + uv_key_delete(&celix_err_tssKey); + celix_err_tssKeyInitialized = false; } const char* celix_err_popLastError() { diff --git a/libs/utils/src/celix_err_private.h b/libs/utils/src/celix_err_private.h new file mode 100644 index 000000000..66938f976 --- /dev/null +++ b/libs/utils/src/celix_err_private.h @@ -0,0 +1,35 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CELIX_CELIX_ERR_PRIVATE_H +#define CELIX_CELIX_ERR_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +void celix_err_initThreadSpecificStorageKey(); + +void celix_err_deinitThreadSpecificStorageKey(); + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_ERR_PRIVATE_H \ No newline at end of file diff --git a/libs/utils/src/celix_threads.c b/libs/utils/src/celix_threads.c index 19e64bec1..226e8d53e 100644 --- a/libs/utils/src/celix_threads.c +++ b/libs/utils/src/celix_threads.c @@ -267,19 +267,3 @@ celix_status_t celixThreadRwlockAttr_destroy(celix_thread_rwlockattr_t *attr) { celix_status_t celixThread_once(celix_thread_once_t *once_control, void (*init_routine)(void)) { return pthread_once(once_control, init_routine); } - -celix_status_t celix_tss_create(celix_tss_key_t* key, void (*destroyFunction)(void*)) { - return pthread_key_create(key, destroyFunction); -} - -celix_status_t celix_tss_delete(celix_tss_key_t key) { - return pthread_key_delete(key); -} - -celix_status_t celix_tss_set(celix_tss_key_t key, void* value) { - return pthread_setspecific(key, value); -} - -void* celix_tss_get(celix_tss_key_t key) { - return pthread_getspecific(key); -}