Skip to content

Commit 06ae35a

Browse files
authored
Merge pull request #780 from Deedss/feature/event-information-on-waiting
Expand logging information when genericEvent is waiting
2 parents 553e2f6 + 3c13b38 commit 06ae35a

File tree

6 files changed

+82
-13
lines changed

6 files changed

+82
-13
lines changed

documents/framework.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,3 +304,4 @@ The following framework properties are supported:
304304
| CELIX_AUTO_INSTALL | "" | The bundles to install after the framework is started. Multiple bundles can be provided separated by a space. |
305305
| CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL | "info" | The default active log level for created log services. Possible values are "trace", "debug", "info", "warning", "error" and "fatal". |
306306
| CELIX_ALLOWED_PROCESSING_TIME_FOR_SCHEDULED_EVENT_IN_SECONDS | "2" | The allowed processing time for scheduled events in seconds, if processing takes longer a warning message will be logged. |
307+
| CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS | "5" | The allowed processing time for generic events in seconds, if processing takes longer a warning message will be logged. |

libs/framework/gtest/src/CelixFrameworkTestSuite.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,40 @@ TEST_F(CelixFrameworkTestSuite, TimedWaitEventQueueTest) {
8888
EXPECT_EQ(CELIX_SUCCESS, status);
8989
}
9090

91+
TEST_F(CelixFrameworkTestSuite, GenericEventTimeoutPropertyTest) {
92+
celix_properties_t* config;
93+
auto status = celix_properties_load("config.properties", 0, &config);
94+
EXPECT_EQ(CELIX_SUCCESS, status);
95+
celix_properties_set(config, "CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL" , "warning");
96+
celix_properties_set(config, CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS, "0.010");
97+
98+
framework_t* fw = celix_frameworkFactory_createFramework(config);
99+
ASSERT_TRUE(fw != nullptr);
100+
101+
// Start capturing stdout
102+
::testing::internal::CaptureStderr();
103+
104+
//When there is a emtpy event queue
105+
celix_framework_waitForEmptyEventQueue(fw);
106+
107+
//And a generic event is fired, that block the queue for 20ms
108+
auto callback = [](void* /*data*/) {
109+
std::this_thread::sleep_for(std::chrono::milliseconds{20});
110+
};
111+
long eventId = celix_framework_fireGenericEvent(fw, -1L, -1L, "test", nullptr, callback, nullptr, nullptr);
112+
113+
//Then waiting for the event queue will have logged errors
114+
celix_framework_waitForGenericEvent(fw, eventId);
115+
116+
// And the log will contain a printed warning
117+
auto log = ::testing::internal::GetCapturedStderr();
118+
auto expected = "Generic event 'test' (id=" + std::to_string(eventId) + ")";
119+
EXPECT_TRUE(log.find(expected) != std::string::npos) << "Got " << log;
120+
121+
// Cleanup framework
122+
celix_frameworkFactory_destroyFramework(fw);
123+
}
124+
91125
TEST_F(CelixFrameworkTestSuite, AsyncInstallStartStopUpdateAndUninstallBundleTest) {
92126
long bndId = celix_framework_installBundleAsync(framework.get(), SIMPLE_TEST_BUNDLE1_LOCATION, false);
93127
EXPECT_GE(bndId, 0);

libs/framework/include/celix/Constants.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,12 @@ namespace celix {
223223
* Should be a boolean value.
224224
*/
225225
constexpr const char* const FRAMEWORK_CONDITION_SERVICES_ENABLED = CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED;
226+
227+
/**
228+
* @brief Celix framework property (named "CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS") to configure the timeout time
229+
* for generic events in seconds.
230+
* Default is 5.0
231+
* Should be a double value.
232+
*/
233+
constexpr const char* const ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS = CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS;
226234
}

libs/framework/include/celix_constants.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,21 @@ extern "C" {
281281
*/
282282
#define CELIX_DEFAULT_ALLOWED_PROCESSING_TIME_FOR_SCHEDULED_EVENT_IN_SECONDS 2.0
283283

284+
/*!
285+
* @brief Celix framework environment property (named "CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS")
286+
* to configure the allowed processing time for a generic event callback or a remove callback before a warning
287+
* log message is printed that the processing time is too long.
288+
* Should be a double value in seconds.
289+
*/
290+
#define CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS \
291+
"CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS"
292+
293+
/**
294+
* @brief The default , in seconds, allowed processing time for a processing a generic event or a remove callback
295+
* before a warning log message is printed.
296+
*/
297+
#define CELIX_DEFAULT_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS 5.0
298+
284299
/**
285300
* @brief Celix framework environment property (named "CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED") to configure
286301
* whether framework condition services are enabled or not.

libs/framework/src/framework.c

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ celix_status_t framework_create(framework_pt *out, celix_properties_t* config) {
252252
framework->dispatcher.eventQueue = malloc(sizeof(celix_framework_event_t) * framework->dispatcher.eventQueueCap);
253253
framework->dispatcher.dynamicEventQueue = celix_arrayList_create();
254254
framework->dispatcher.scheduledEvents = celix_longHashMap_create();
255+
framework->dispatcher.genericEventTimeoutInSeconds = celix_framework_getConfigPropertyAsDouble(framework,
256+
CELIX_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS,
257+
CELIX_DEFAULT_ALLOWED_PROCESSING_TIME_FOR_GENERIC_EVENT_IN_SECONDS,
258+
NULL);
255259

256260
celix_framework_createAndStoreFrameworkUUID(framework);
257261

@@ -2663,37 +2667,42 @@ long celix_framework_nextScheduledEventId(framework_t *fw) {
26632667
return __atomic_fetch_add(&fw->dispatcher.nextScheduledEventId, 1, __ATOMIC_RELAXED);
26642668
}
26652669

2666-
/**
2667-
* @brief Checks if a generic event with the provided eventId is in progress.
2668-
*/
2669-
static bool celix_framework_isGenericEventInProgress(celix_framework_t* fw, long eventId) {
2670-
// precondition fw->dispatcher.mutex locked)
2670+
static celix_framework_event_t* celix_framework_getGenericEvent(celix_framework_t* fw, long eventId) {
2671+
// precondition fw->dispatcher.mutex locked
26712672
for (int i = 0; i < fw->dispatcher.eventQueueSize; ++i) {
26722673
int index = (fw->dispatcher.eventQueueFirstEntry + i) % fw->dispatcher.eventQueueCap;
26732674
celix_framework_event_t* e = &fw->dispatcher.eventQueue[index];
26742675
if (e->type == CELIX_GENERIC_EVENT && e->genericEventId == eventId) {
2675-
return true;;
2676+
return e;
26762677
}
26772678
}
26782679
for (int i = 0; i < celix_arrayList_size(fw->dispatcher.dynamicEventQueue); ++i) {
26792680
celix_framework_event_t* e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, i);
26802681
if (e->type == CELIX_GENERIC_EVENT && e->genericEventId == eventId) {
2681-
return true;
2682+
return e;
26822683
}
26832684
}
2684-
return false;
2685+
return NULL;
26852686
}
26862687

26872688
void celix_framework_waitForGenericEvent(celix_framework_t* fw, long eventId) {
26882689
assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
2689-
struct timespec logAbsTime = celixThreadCondition_getDelayedTime(5);
2690+
struct timespec logAbsTime = celixThreadCondition_getDelayedTime(fw->dispatcher.genericEventTimeoutInSeconds);
26902691
celixThreadMutex_lock(&fw->dispatcher.mutex);
2691-
while (celix_framework_isGenericEventInProgress(fw, eventId)) {
2692+
celix_framework_event_t* event = celix_framework_getGenericEvent(fw, eventId);
2693+
while (event) {
26922694
celix_status_t waitStatus =
26932695
celixThreadCondition_waitUntil(&fw->dispatcher.cond, &fw->dispatcher.mutex, &logAbsTime);
2694-
if (waitStatus == ETIMEDOUT) {
2695-
fw_log(fw->logger, CELIX_LOG_LEVEL_WARNING, "Generic event with id %li not finished.", eventId);
2696-
logAbsTime = celixThreadCondition_getDelayedTime(5);
2696+
event = celix_framework_getGenericEvent(fw, eventId);
2697+
if (waitStatus == ETIMEDOUT && event != NULL) {
2698+
fw_log(fw->logger,
2699+
CELIX_LOG_LEVEL_WARNING,
2700+
"Generic event '%s' (id=%li) for bundle '%s' (id=%li) not finished",
2701+
event->genericEventName ? event->genericEventName : "unnamed",
2702+
eventId,
2703+
event->bndEntry ? celix_bundle_getSymbolicName(event->bndEntry->bnd) : "unnamed",
2704+
event->bndEntry ? event->bndEntry->bndId : -1l);
2705+
logAbsTime = celixThreadCondition_getDelayedTime(fw->dispatcher.genericEventTimeoutInSeconds);
26972706
}
26982707
}
26992708
celixThreadMutex_unlock(&fw->dispatcher.mutex);

libs/framework/src/framework_private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ struct celix_framework {
188188
int nbEvent; // number of pending generic events
189189
} stats;
190190
celix_long_hash_map_t *scheduledEvents; //key = scheduled event id, entry = celix_framework_scheduled_event_t*. Used for scheduled events
191+
192+
double genericEventTimeoutInSeconds; // Timeout for printing an warning on unfinished generic events
191193
} dispatcher;
192194

193195
celix_framework_logger_t* logger;

0 commit comments

Comments
 (0)