This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/celix_err in repository https://gitbox.apache.org/repos/asf/celix.git
commit cd2d042de07e2629068c5cb2db71ce766f4912a4 Author: Pepijn Noltes <pepijnnol...@gmail.com> AuthorDate: Tue May 2 00:04:20 2023 +0200 Refactor celix_err to use a fixed err msg buffer --- libs/utils/gtest/src/ErrTestSuite.cc | 24 ++++++- libs/utils/include/celix_err.h | 11 ++- libs/utils/src/celix_err.c | 128 ++++++++++++++++++++--------------- 3 files changed, 103 insertions(+), 60 deletions(-) diff --git a/libs/utils/gtest/src/ErrTestSuite.cc b/libs/utils/gtest/src/ErrTestSuite.cc index abb15cb6..6a8c4431 100644 --- a/libs/utils/gtest/src/ErrTestSuite.cc +++ b/libs/utils/gtest/src/ErrTestSuite.cc @@ -35,12 +35,10 @@ TEST_F(ErrTestSuite, AddAndPopErrorTest) { auto* m = celix_err_popLastError(); EXPECT_STREQ("error message from test", m); - free(m); EXPECT_EQ(1, celix_err_getErrorCount()); m = celix_err_popLastError(); EXPECT_STREQ("error message", m); - free(m); EXPECT_EQ(0, celix_err_getErrorCount()); } @@ -54,3 +52,25 @@ TEST_F(ErrTestSuite, ResetErrorTest) { EXPECT_EQ(0, celix_err_getErrorCount()); EXPECT_EQ(nullptr, celix_err_popLastError()); } + +TEST_F(ErrTestSuite, EdgeCaseTest) { + //test only "" error message, 1 char per message so max error count is CELIX_ERR_BUFFER_SIZE + for (int i = 0; i < CELIX_ERR_BUFFER_SIZE + 10; ++i) { + celix_err_push(""); + } + EXPECT_EQ(CELIX_ERR_BUFFER_SIZE, celix_err_getErrorCount()); + for (int i = 0; i < CELIX_ERR_BUFFER_SIZE; ++i) { + EXPECT_STREQ("", celix_err_popLastError()); + } + EXPECT_EQ(0, celix_err_getErrorCount()); + + //test only "[0-9]" error message, 2 char per message so max error count is CELIX_ERR_BUFFER_SIZE/2 + for (int i = 0; i < (CELIX_ERR_BUFFER_SIZE/2) + 10; ++i) { + celix_err_pushf("%i", i % 10); + } + EXPECT_EQ(CELIX_ERR_BUFFER_SIZE/2, celix_err_getErrorCount()); + for (int i = (CELIX_ERR_BUFFER_SIZE/2) - 1; i >= 0; --i) { + EXPECT_STREQ(std::to_string(i % 10).c_str(), celix_err_popLastError()); + } + EXPECT_EQ(0, celix_err_getErrorCount()); +} diff --git a/libs/utils/include/celix_err.h b/libs/utils/include/celix_err.h index 16bd2d31..7acb0f77 100644 --- a/libs/utils/include/celix_err.h +++ b/libs/utils/include/celix_err.h @@ -32,13 +32,20 @@ extern "C" { * @thread_safety Thread safe. */ +//TODO make configurable with a cmake option +/** + * @brief The error message buffer size per thread. + */ +#define CELIX_ERR_BUFFER_SIZE 512 + + /** * @brief Returns the last error message from the current thread. - * The error message must be freed by the caller. + * The error message is stored on a local thread storage and is valid until a new error message is pushed. * @returnval NULL if no error message is available. */ CELIX_UTILS_EXPORT -char* celix_err_popLastError(); //TODO update to return const char* and remove free +const char* celix_err_popLastError(); /** * @brief Push an formatted error message to the thread specific storage rcm errors. diff --git a/libs/utils/src/celix_err.c b/libs/utils/src/celix_err.c index 939c7078..a6a67f11 100644 --- a/libs/utils/src/celix_err.c +++ b/libs/utils/src/celix_err.c @@ -23,102 +23,118 @@ #include <stdarg.h> #include <stdio.h> #include <pthread.h> +#include <string.h> -#include "celix_array_list.h" -#include "celix_utils.h" +pthread_key_t celix_err_tssKey; //TODO replace with celix_tss_key and celix_tss_* functions -pthread_key_t celix_rcm_tssErrorsKey; //TODO replace with celix_tss_key and celix_tss_* functions +typedef struct celix_err { + char buffer[CELIX_ERR_BUFFER_SIZE]; + size_t pos; +} celix_err_t; -static void celix_err_destroyTssErrors(void* data) { - //TODO replace array list with some thread specific storage char buffer - celix_array_list_t* errors = data; - if (errors != NULL) { - for (int i = 0; i < celix_arrayList_size(errors); ++i) { - char* msg = celix_arrayList_get(errors, i); - free(msg); +static void celix_err_destroyTssErr(void* data) { + celix_err_t* err = data; + if (err != NULL) { + free(err); + } +} + +static celix_err_t* celix_err_getTssErr() { + celix_err_t* err = pthread_getspecific(celix_err_tssKey); + if (err) { + return err; + } + + err = malloc(sizeof(*err)); + if (err) { + err->pos = 0; //no entry + int rc = pthread_setspecific(celix_err_tssKey, err); + if (rc != 0) { + fprintf(stderr, "Failed to set thread specific storage for celix_err: %s\n", strerror(rc)); + free(err); + err = NULL; } + } else { + fprintf(stderr, "Failed to allocate memory for celix_err\n"); } - celix_arrayList_destroy(errors); + return err; } __attribute__((constructor)) void celix_err_initThreadSpecificStorageKey() { - int rc = pthread_key_create(&celix_rcm_tssErrorsKey, celix_err_destroyTssErrors); + int rc = pthread_key_create(&celix_err_tssKey, celix_err_destroyTssErr); if (rc != 0) { fprintf(stderr,"Failed to create thread specific storage key for celix_err\n"); } } __attribute__((destructor)) void celix_err_deinitThreadSpecificStorageKey() { - int rc = pthread_key_delete(celix_rcm_tssErrorsKey); + int rc = pthread_key_delete(celix_err_tssKey); if (rc != 0) { fprintf(stderr,"Failed to delete thread specific storage key for celix_err\n"); } } -char* celix_err_popLastError() { - char* result = NULL; - celix_array_list_t* errors = pthread_getspecific(celix_rcm_tssErrorsKey); - if (errors != NULL && celix_arrayList_size(errors) > 0) { - int last = celix_arrayList_size(errors) - 1; - result = celix_arrayList_get(errors, last); - celix_arrayList_removeAt(errors, last); +const char* celix_err_popLastError() { + const char* result = NULL; + celix_err_t* err = celix_err_getTssErr(); + if (err && err->pos > 0) { + //move back to start last error message + err->pos -= 1; //move before \0 in last error message + while (err->pos > 0 && err->buffer[err->pos-1] != '\0') { + err->pos -= 1; + } + result = err->buffer + err->pos; } return result; } int celix_err_getErrorCount() { int result = 0; - celix_array_list_t* errors = pthread_getspecific(celix_rcm_tssErrorsKey); - if (errors != NULL) { - result = celix_arrayList_size(errors); + celix_err_t* err = celix_err_getTssErr(); + for (int i = 0; err && i < err->pos; ++i) { + if (err->buffer[i] == '\0') { + result += 1; + } } return result; } void celix_err_resetErrors() { - celix_array_list_t* errors = pthread_getspecific(celix_rcm_tssErrorsKey); - celix_err_destroyTssErrors(errors); - pthread_setspecific(celix_rcm_tssErrorsKey, NULL); -} - -static void celix_err_pushMsg(char* msg) { - celix_array_list_t* errors = pthread_getspecific(celix_rcm_tssErrorsKey); - if (errors == NULL) { - errors = celix_arrayList_create(); - if (errors == NULL) { - fprintf(stderr, "Failed to create error list for Apache Celix rcm lib\n"); - free(msg); - return; - } - pthread_setspecific(celix_rcm_tssErrorsKey, errors); - } - if (errors != NULL) { - celix_status_t rc = celix_arrayList_add(errors, msg); - if (rc != CELIX_SUCCESS) { - fprintf(stderr, "Failed to add error to error list for Apache Celix rcm lib\n"); - free(msg); - } + void* data = pthread_getspecific(celix_err_tssKey); + if (data != NULL) { + celix_err_t* err = data; + err->pos = 0; //no entry } } void celix_err_push(const char* msg) { - char* msgCpy = celix_utils_strdup(msg); - if (msgCpy == NULL) { - fprintf(stderr, "Failed to copy error message for Apache Celix rcm lib\n"); - return; + celix_err_t* err = celix_err_getTssErr(); + if (err) { + size_t len = strnlen(msg, CELIX_ERR_BUFFER_SIZE); + if (err->pos + len + 1 <= CELIX_ERR_BUFFER_SIZE) { + strcpy(err->buffer + err->pos, msg); + err->pos += len + 1; + } else { + fprintf(stderr, "Failed to add error message '%s' to celix_err\n", msg); + } } - celix_err_pushMsg(msgCpy); } void celix_err_pushf(const char* format, ...) { va_list args; va_start(args, format); - char* msg = NULL; - int rc = vasprintf(&msg, format, args); - if (rc >= 0) { - celix_err_pushMsg(msg); - } else { - fprintf(stderr, "Failed to copy error message for Apache Celix rcm lib\n"); + celix_err_t* err = celix_err_getTssErr(); + if (err) { + size_t len = vsnprintf(err->buffer + err->pos, CELIX_ERR_BUFFER_SIZE - err->pos, format, args); + if (err->pos + len + 1 <= CELIX_ERR_BUFFER_SIZE) { + err->pos += len; + err->buffer[err->pos++] = '\0'; + } else { + fprintf(stderr, "Failed to add error message '"); + vfprintf(stderr, format, args); + fprintf(stderr, "' to celix_err\n"); + + } } va_end(args); }