PengZheng commented on code in PR #583:
URL: https://github.com/apache/celix/pull/583#discussion_r1247368794


##########
libs/framework/include/celix/ScheduledEvent.h:
##########
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "celix_bundle_context.h"
+
+namespace celix {
+
+/**
+ * @brief A C++ abstraction for a scheduled event in Celix.
+ *
+ * A scheduled event is an event that is scheduled to be executed at a certain 
initial delay and/or interval.
+ * A new scheduld event should be created using 
celix::BundleContext::createScheduledEvent.
+ *
+ * This class uses RAII to automatically remove the (non one-shot) scheduled 
event from the bundle context
+ * when it is destroyed. For one-shot scheduled events, the destructor will 
not remove the scheduled event.
+ */
+class ScheduledEvent final {
+  public:
+    friend class ScheduledEventBuilder;
+
+    /**
+     * @brief Constructs a empty / not-active scheduled event.
+     *
+     * During destruction the scheduled event will be removed asynchronously 
from the bundle context.
+     */
+    ScheduledEvent() = default;
+
+    /**
+     * @brief Destroys the scheduled event by removes it from the bundle 
context if it is not a one-short event.
+     */
+    ~ScheduledEvent() noexcept {
+        if (!isOneShot) {
+            celix_bundleContext_tryRemoveScheduledEventAsync(ctx.get(), 
eventId);
+        }
+    }
+
+    ScheduledEvent(const ScheduledEvent&) = delete;
+    ScheduledEvent& operator=(const ScheduledEvent&) = delete;
+
+    ScheduledEvent(ScheduledEvent&&) noexcept = default;
+    ScheduledEvent& operator=(ScheduledEvent&&) noexcept = default;
+
+    /**
+     * @brief Cancels the scheduled event.
+     *
+     * This method will block until a possible in-progress scheduled event 
callback is finished, the scheduled event
+     * is removed and, if configured, the remove callback is called.
+     * Should not be called multiple times.
+     */
+    void cancel() {
+        if (ctx) {
+            celix_bundleContext_removeScheduledEvent(ctx.get(), eventId);
+        }
+    }
+
+    /**
+     * @brief Wakeup a scheduled event and returns immediately, not waiting 
for the scheduled event callback to be
+     * called.
+     */
+    void wakeup() { celix_bundleContext_wakeupScheduledEvent(ctx.get(), 
eventId); }

Review Comment:
   Missing ctx check?



##########
libs/framework/include/celix_bundle_context.h:
##########
@@ -1073,10 +1073,10 @@ typedef struct celix_bundle_tracker_options {
     void (*trackerCreatedCallback)(void *trackerCreatedCallbackData) 
CELIX_OPTS_INIT;
 } celix_bundle_tracking_options_t;
 
-/**
+#ifndef __cplusplus
+/*!

Review Comment:
   Is there any reason to change to the qt style?



##########
libs/framework/src/framework.c:
##########
@@ -1185,6 +1199,9 @@ static void* framework_shutdown(void *framework) {
         //NOTE possible starvation.
         fw_bundleEntry_waitTillUseCountIs(entry, 1);  //note this function has 
1 use count.
 
+        //note race between condition (use count == 1) and bundle stop, 
meaning use count can be > 1 when
+        //celix_framework_stopBundleEntry is called.

Review Comment:
   IMO, the whole `for` can be removed altogether, since all state transitions 
can be done reliably within `celix_framework_uninstallBundleEntry` now.



##########
libs/framework/include/celix/ScheduledEvent.h:
##########
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "celix_bundle_context.h"
+
+namespace celix {
+
+/**
+ * @brief A C++ abstraction for a scheduled event in Celix.
+ *
+ * A scheduled event is an event that is scheduled to be executed at a certain 
initial delay and/or interval.
+ * A new scheduld event should be created using 
celix::BundleContext::createScheduledEvent.
+ *
+ * This class uses RAII to automatically remove the (non one-shot) scheduled 
event from the bundle context
+ * when it is destroyed. For one-shot scheduled events, the destructor will 
not remove the scheduled event.
+ */
+class ScheduledEvent final {
+  public:
+    friend class ScheduledEventBuilder;
+
+    /**
+     * @brief Constructs a empty / not-active scheduled event.
+     *
+     * During destruction the scheduled event will be removed asynchronously 
from the bundle context.
+     */
+    ScheduledEvent() = default;
+
+    /**
+     * @brief Destroys the scheduled event by removes it from the bundle 
context if it is not a one-short event.
+     */
+    ~ScheduledEvent() noexcept {
+        if (!isOneShot) {
+            celix_bundleContext_tryRemoveScheduledEventAsync(ctx.get(), 
eventId);

Review Comment:
   Missing ctx nullity check?



##########
libs/framework/src/framework.c:
##########
@@ -2391,14 +2535,29 @@ celix_array_list_t* 
celix_framework_listInstalledBundles(celix_framework_t* fram
     return celix_framework_listBundlesInternal(framework, false);
 }
 
-void celix_framework_waitForEmptyEventQueue(celix_framework_t *fw) {
+celix_status_t celix_framework_waitForEmptyEventQueueFor(celix_framework_t 
*fw, double periodInSeconds) {
     assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+    celix_status_t status = CELIX_SUCCESS;
 
+    struct timespec absTimeout = 
celixThreadCondition_getDelayedTime(periodInSeconds);
     celixThreadMutex_lock(&fw->dispatcher.mutex);
-    while (fw->dispatcher.eventQueueSize > 0 || 
celix_arrayList_size(fw->dispatcher.dynamicEventQueue) > 0) {
-        celixThreadCondition_wait(&fw->dispatcher.cond, &fw->dispatcher.mutex);
+    while (celix_framework_eventQueueSize(fw) > 0) {

Review Comment:
   It takes all events including framework (not associated with a bundle) 
events into account, while `celix_framework_waitForEmptyEventQueue` only count 
bundle events.
   Shall we unify them?



##########
libs/framework/src/framework_private.h:
##########
@@ -437,4 +447,83 @@ celix_status_t 
celix_framework_uninstallBundleEntry(celix_framework_t* fw, celix
  */
 celix_status_t celix_framework_updateBundleEntry(celix_framework_t* fw, 
celix_framework_bundle_entry_t* bndEntry, const char* updatedBundleUrl);
 
+
+/** @brief Return the next scheduled event id.
+ * @param[in] fw The Celix framework
+ * @return The next scheduled event id.
+ */
+long celix_framework_nextScheduledEventId(framework_t *fw);
+
+/**
+ * @brief Add a scheduled event to the Celix framework.
+ *
+ *
+ * @param[in] fw The Celix framework
+ * @param[in] bndId The bundle id to add the scheduled event for. If < 0 the 
framework bundle is used.
+ * @param[in] eventName The event name to use for the scheduled event. If 
NULL, a default event name is used.
+ * @param[in] initialDelayInSeconds The initial delay in seconds before the 
first event callback is called.
+ * @param[in] intervalInSeconds The interval in seconds between event 
callbacks.
+ * @param[in] eventData The event data to pass to the event callback.
+ * @param[in] eventCallback The event callback to call when the scheduled 
event is triggered.
+ * @return The scheduled event id of the scheduled event. Can be used to 
cancel the event.
+ * @retval <0 If the event could not be added.
+ */
+long celix_framework_scheduleEvent(celix_framework_t* fw,
+                                    long bndId,
+                                    const char* eventName,
+                                    double initialDelayInSeconds,
+                                    double intervalInSeconds,
+                                    void* callbackData,
+                                    void (*callback)(void*),
+                                    void* removeCallbackData,
+                                    void (*removeCallback)(void*));
+
+/**
+ * @brief Wakeup a scheduled event and returns immediately, not waiting for 
the scheduled event callback to be
+ * called.
+ *
+ * @param[in] ctx The bundle context.
+ * @param[in] scheduledEventId The scheduled event id to wakeup.
+ * @return CELIX_SUCCESS if the scheduled event is woken up, 
CELIX_ILLEGAL_ARGUMENT if the scheduled event id is not
+ */
+celix_status_t celix_framework_wakeupScheduledEvent(celix_framework_t* fw, 
long scheduledEventId);
+
+/**
+ * @brief Wait for the next scheduled event to be processed.
+ * @param[in] fw The Celix framework
+ * @param[in] scheduledEventId The scheduled event id to wait for.
+ * @param[in] waitTimeInSeconds The maximum time to wait for the next 
scheduled event. If <= 0 the function will return
+ *                             immediately.
+ * @return CELIX_SUCCESS if the scheduled event is woken up, 
CELIX_ILLEGAL_ARGUMENT if the scheduled event id is not
+ *         known and ETIMEDOUT if the waitTimeInSeconds is reached.
+ */
+celix_status_t celix_framework_waitForScheduledEvent(celix_framework_t* fw,
+                                                     long scheduledEventId,
+                                                     double waitTimeInSeconds);
+
+/**
+ * @brief Cancel a scheduled event.
+ *
+ * When this function returns, no more scheduled event callbacks will be 
called.
+ *
+ * @param[in] fw The Celix framework
+ * @param[in] async If true, the scheduled event will be cancelled 
asynchronously and the function will not block.
+ * @param[in] errorIfNotFound If true, removal of a non existing scheduled 
event id will not be logged.

Review Comment:
   Should it be the opposite?



##########
libs/framework/src/framework.c:
##########
@@ -1185,6 +1199,9 @@ static void* framework_shutdown(void *framework) {
         //NOTE possible starvation.
         fw_bundleEntry_waitTillUseCountIs(entry, 1);  //note this function has 
1 use count.
 
+        //note race between condition (use count == 1) and bundle stop, 
meaning use count can be > 1 when
+        //celix_framework_stopBundleEntry is called.

Review Comment:
   Just noticed that the classical `ShellTestSuite.quitTest` bug did not bite 
us in the last two months. If it were solved by our refactoring efforts, I 
won't be too surprised.



##########
libs/framework/src/framework.c:
##########
@@ -1408,32 +1426,153 @@ static inline bool 
fw_removeTopEventFromQueue(celix_framework_t* fw) {
 static inline void fw_handleEvents(celix_framework_t* framework) {
     celixThreadMutex_lock(&framework->dispatcher.mutex);
     int size = framework->dispatcher.eventQueueSize + 
celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
-    if (size == 0 && framework->dispatcher.active) {
-        celixThreadCondition_timedwaitRelative(&framework->dispatcher.cond, 
&framework->dispatcher.mutex, 1, 0);
-    }
-    size = framework->dispatcher.eventQueueSize + 
celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
     celixThreadMutex_unlock(&framework->dispatcher.mutex);
 
     while (size > 0) {
         celix_framework_event_t* topEvent = fw_topEventFromQueue(framework);
         fw_handleEventRequest(framework, topEvent);
-        bool dynamiclyAllocatedEvent = fw_removeTopEventFromQueue(framework);
+        bool dynamicallyAllocatedEvent = fw_removeTopEventFromQueue(framework);
 
         if (topEvent->bndEntry != NULL) {
             celix_framework_bundleEntry_decreaseUseCount(topEvent->bndEntry);
         }
         free(topEvent->serviceName);
-        if (dynamiclyAllocatedEvent) {
+        if (dynamicallyAllocatedEvent) {
             free(topEvent);
         }
 
         celixThreadMutex_lock(&framework->dispatcher.mutex);
         size = framework->dispatcher.eventQueueSize + 
celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
-        celixThreadCondition_broadcast(&framework->dispatcher.cond);
         celixThreadMutex_unlock(&framework->dispatcher.mutex);
     }
 }
 
+/**
+ * @brief Process all scheduled events.
+ */
+static double celix_framework_processScheduledEvents(celix_framework_t* fw) {
+    struct timespec ts = celixThreadCondition_getTime();
+
+    double nextClosestScheduledEvent;
+    celix_scheduled_event_t* callEvent;
+    celix_scheduled_event_t* removeEvent;
+    do {
+        nextClosestScheduledEvent = -1; //negative means no event next event
+        callEvent = NULL;
+        removeEvent = NULL;
+        double nextEvent;
+        celixThreadMutex_lock(&fw->dispatcher.mutex);
+        CELIX_LONG_HASH_MAP_ITERATE(fw->dispatcher.scheduledEvents, entry) {
+            celix_scheduled_event_t* visit = entry.value.ptrValue;
+            if (celix_scheduledEvent_isMarkedForRemoval(visit)) {
+                removeEvent = visit;
+                celix_longHashMap_remove(fw->dispatcher.scheduledEvents, 
celix_scheduledEvent_getId(visit));
+                break;
+            }
+
+            bool call = celix_scheduledEvent_deadlineReached(visit, &ts, 
&nextEvent);
+            if (nextClosestScheduledEvent < 0 || nextEvent < 
nextClosestScheduledEvent) {
+                nextClosestScheduledEvent = nextEvent;
+            }
+            if (call) {
+                callEvent = visit;
+                if (celix_scheduledEvent_isSingleShot(visit)) {
+                    removeEvent = visit;
+                    celix_longHashMap_remove(fw->dispatcher.scheduledEvents, 
celix_scheduledEvent_getId(visit));
+                }
+                break;
+            }
+        }
+        celixThreadMutex_unlock(&fw->dispatcher.mutex);
+
+        if (callEvent != NULL) {
+            celix_scheduledEvent_process(callEvent, &ts);
+        }
+        if (removeEvent != NULL) {
+            const char* formatStr = 
celix_scheduledEvent_isSingleShot(removeEvent) ?
+                    "Removing processed one-shot scheduled event '%s' (id=%li) 
for bundle if %li.":
+                    "Removing processed scheduled event '%s' (id=%li) for 
bundle if %li.";
+            fw_log(fw->logger,
+                   CELIX_LOG_LEVEL_DEBUG,
+                   formatStr,

Review Comment:
   Does format string checking still work this way?



##########
libs/framework/src/framework.c:
##########
@@ -2440,6 +2599,127 @@ void 
celix_framework_waitUntilNoPendingRegistration(celix_framework_t* fw)
     celixThreadMutex_unlock(&fw->dispatcher.mutex);
 }
 
+long celix_framework_scheduleEvent(celix_framework_t* fw,
+                                    long bndId,
+                                    const char* eventName,
+                                    double initialDelayInSeconds,
+                                    double intervalInSeconds,
+                                    void* callbackData,
+                                    void (*callback)(void*),
+                                    void* removeCallbackData,
+                                    void (*removeCallback)(void*)) {
+    if (callback == NULL) {
+        fw_log(fw->logger,
+               CELIX_LOG_LEVEL_ERROR,
+               "Cannot add scheduled event for bundle id %li. Invalid NULL 
event callback.",
+               bndId);
+        return -1;
+    }
+
+    celix_framework_bundle_entry_t* bndEntry = 
celix_framework_bundleEntry_getBundleEntryAndIncreaseUseCount(fw, bndId);
+    if (bndEntry == NULL) {
+        fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Cannot add scheduled event 
for non existing bundle id %li.", bndId);
+        return -1;
+    }
+    celix_scheduled_event_t* newEvent = celix_scheduledEvent_create(fw,
+                                                                 
bndEntry->bndId,
+                                                                 
celix_framework_nextScheduledEventId(fw),
+                                                                 eventName,
+                                                                 
initialDelayInSeconds,
+                                                                 
intervalInSeconds,
+                                                                 callbackData,
+                                                                 callback,
+                                                                 
removeCallbackData,
+                                                                 
removeCallback);
+    CELIX_SCHEDULED_EVENT_RETAIN_GUARD(event, newEvent);
+    celix_framework_bundleEntry_decreaseUseCount(bndEntry);
+
+    if (event == NULL) {
+        return -1L; //error logged by celix_scheduledEvent_create
+    }
+
+    fw_log(fw->logger,
+           CELIX_LOG_LEVEL_DEBUG,
+           "Added scheduled event '%s' (id=%li) for bundle '%s' (id=%li).",
+           celix_scheduledEvent_getName(event),
+           celix_scheduledEvent_getId(event),
+           celix_bundle_getSymbolicName(bndEntry->bnd),
+           bndId);
+
+    celixThreadMutex_lock(&fw->dispatcher.mutex);
+    celix_longHashMap_put(fw->dispatcher.scheduledEvents, 
celix_scheduledEvent_getId(event), event);
+    celixThreadCondition_broadcast(&fw->dispatcher.cond); //notify dispatcher 
thread for newly added scheduled event
+    celixThreadMutex_unlock(&fw->dispatcher.mutex);
+
+    return celix_scheduledEvent_getId(event);
+}
+
+celix_status_t celix_framework_wakeupScheduledEvent(celix_framework_t* fw, 
long scheduledEventId) {
+    celixThreadMutex_lock(&fw->dispatcher.mutex);
+    celix_scheduled_event_t* event = 
celix_longHashMap_get(fw->dispatcher.scheduledEvents, scheduledEventId);
+    if (event != NULL) {
+        celix_scheduledEvent_markForWakeup(event);
+        celixThreadCondition_broadcast(&fw->dispatcher.cond); //notify 
dispatcher thread for configured wakeup
+    }
+    celixThreadMutex_unlock(&fw->dispatcher.mutex);
+
+    if (event == NULL) {
+        fw_log(fw->logger,
+               CELIX_LOG_LEVEL_WARNING,
+               "celix_framework_wakeupScheduledEvent called with unknown 
scheduled event id %li.",
+               scheduledEventId);
+        return CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    return CELIX_SUCCESS;
+}
+
+celix_status_t
+celix_framework_waitForScheduledEvent(celix_framework_t* fw, long 
scheduledEventId, double waitTimeInSeconds) {
+    celixThreadMutex_lock(&fw->dispatcher.mutex);
+    CELIX_SCHEDULED_EVENT_RETAIN_GUARD(event, 
celix_longHashMap_get(fw->dispatcher.scheduledEvents, scheduledEventId));
+    celixThreadMutex_unlock(&fw->dispatcher.mutex);
+
+    if (event == NULL) {

Review Comment:
   What if a one-shot event is processed and removed here? I think the next 
`wait` will block forever. Similar thinking also applies to normal events about 
to be removed.
   
   In `wait`, we should also check `isRemoved`?



##########
libs/framework/include/celix_framework.h:
##########
@@ -318,53 +317,83 @@ CELIX_FRAMEWORK_EXPORT celix_array_list_t* 
celix_framework_listBundles(celix_fra
  */
 CELIX_FRAMEWORK_EXPORT celix_array_list_t* 
celix_framework_listInstalledBundles(celix_framework_t* framework);
 
+/**
+ * @brief Sets the log function for this framework.
+ * Default the celix framework will log to stdout/stderr.
+ *
+ * A log function can be injected to change how the Celix framework logs.
+ * Can be reset by setting the log function to NULL.
+ */
+CELIX_FRAMEWORK_EXPORT void celix_framework_setLogCallback(celix_framework_t* 
fw, void* logHandle, void (*logFunction)(void* handle, celix_log_level_e level, 
const char* file, const char *function, int line, const char *format, va_list 
formatArgs));
+
 /**
  * @brief Wait until the framework event queue is empty.
  *
  * The Celix framework has an event queue which (among others) handles bundle 
events.
- * This function can be used to ensure that all queue event are handled, 
mainly useful
- * for testing.
+ * This function can be used to ensure that all queue event are handled.
+ * 
+ * Note scheduled events are not part of the event queue.
  *
  * @param fw The Celix Framework
  */
 CELIX_FRAMEWORK_EXPORT void 
celix_framework_waitForEmptyEventQueue(celix_framework_t *fw);
 
 /**
- * @brief Sets the log function for this framework.
- * Default the celix framework will log to stdout/stderr.
+ * @brief Wait until the framework event queue is empty or the provided period 
is reached.
  *
- * A log function can be injected to change how the Celix framework logs.
- * Can be reset by setting the log function to NULL.
+ * The Celix framework has an event queue which (among others) handles bundle 
events.
+ * This function can be used to ensure that all queue event are handled.
+ * 
+ * Note scheduled events are not part of the event queue.
+ *
+ * @param[in] fw The Celix Framework.
+ * @param[in] timeoutInSeconds The period in seconds to wait for the event 
queue to be empty. 0 means wait forever.
+ * @return CELIX_SUCCESS if the event queue is empty or ETIMEDOUT if the 
timeoutInSeconds is reached.
  */
-CELIX_FRAMEWORK_EXPORT void celix_framework_setLogCallback(celix_framework_t* 
fw, void* logHandle, void (*logFunction)(void* handle, celix_log_level_e level, 
const char* file, const char *function, int line, const char *format, va_list 
formatArgs));
-
+CELIX_FRAMEWORK_EXPORT celix_status_t 
celix_framework_waitForEmptyEventQueueFor(celix_framework_t *fw, double 
timeoutInSeconds);
 
 /**
- * @brief wait until all events for the bundle identified by the bndId are 
processed.
+ * @brief wait until all events from the event queue for the bundle identified 
by the bndId are processed.
+ *
+ * If bndId < 0, wait until all events from the event queue are processed.
+ * Note scheduled events are not part of the event queue.
+ * 
  */
 CELIX_FRAMEWORK_EXPORT void 
celix_framework_waitUntilNoEventsForBnd(celix_framework_t* fw, long bndId);
 
 /**
- * @brief wait until all pending service registration  are processed.
+ * @brief wait until all pending service registration are processed.
  */
 CELIX_FRAMEWORK_EXPORT void 
celix_framework_waitUntilNoPendingRegistration(celix_framework_t* fw);
 
 /**
  * @brief Returns whether the current thread is the Celix framework event loop 
thread.
+ * 
+ * Note scheduled events are not part of the event queue.

Review Comment:
   It seems irrelevant.



##########
libs/framework/src/framework_private.h:
##########
@@ -437,4 +447,83 @@ celix_status_t 
celix_framework_uninstallBundleEntry(celix_framework_t* fw, celix
  */
 celix_status_t celix_framework_updateBundleEntry(celix_framework_t* fw, 
celix_framework_bundle_entry_t* bndEntry, const char* updatedBundleUrl);
 
+
+/** @brief Return the next scheduled event id.
+ * @param[in] fw The Celix framework
+ * @return The next scheduled event id.
+ */
+long celix_framework_nextScheduledEventId(framework_t *fw);
+
+/**
+ * @brief Add a scheduled event to the Celix framework.
+ *
+ *
+ * @param[in] fw The Celix framework
+ * @param[in] bndId The bundle id to add the scheduled event for. If < 0 the 
framework bundle is used.

Review Comment:
   If < 0, `celix_framework_bundleEntry_getBundleEntryAndIncreaseUseCount` 
returns NULL, and -1 will be returned.



##########
libs/framework/include/celix_framework.h:
##########
@@ -318,53 +317,83 @@ CELIX_FRAMEWORK_EXPORT celix_array_list_t* 
celix_framework_listBundles(celix_fra
  */
 CELIX_FRAMEWORK_EXPORT celix_array_list_t* 
celix_framework_listInstalledBundles(celix_framework_t* framework);
 
+/**
+ * @brief Sets the log function for this framework.
+ * Default the celix framework will log to stdout/stderr.
+ *
+ * A log function can be injected to change how the Celix framework logs.
+ * Can be reset by setting the log function to NULL.
+ */
+CELIX_FRAMEWORK_EXPORT void celix_framework_setLogCallback(celix_framework_t* 
fw, void* logHandle, void (*logFunction)(void* handle, celix_log_level_e level, 
const char* file, const char *function, int line, const char *format, va_list 
formatArgs));
+
 /**
  * @brief Wait until the framework event queue is empty.
  *
  * The Celix framework has an event queue which (among others) handles bundle 
events.
- * This function can be used to ensure that all queue event are handled, 
mainly useful
- * for testing.
+ * This function can be used to ensure that all queue event are handled.
+ * 
+ * Note scheduled events are not part of the event queue.
  *
  * @param fw The Celix Framework
  */
 CELIX_FRAMEWORK_EXPORT void 
celix_framework_waitForEmptyEventQueue(celix_framework_t *fw);
 
 /**
- * @brief Sets the log function for this framework.
- * Default the celix framework will log to stdout/stderr.
+ * @brief Wait until the framework event queue is empty or the provided period 
is reached.
  *
- * A log function can be injected to change how the Celix framework logs.
- * Can be reset by setting the log function to NULL.
+ * The Celix framework has an event queue which (among others) handles bundle 
events.
+ * This function can be used to ensure that all queue event are handled.
+ * 
+ * Note scheduled events are not part of the event queue.
+ *
+ * @param[in] fw The Celix Framework.
+ * @param[in] timeoutInSeconds The period in seconds to wait for the event 
queue to be empty. 0 means wait forever.
+ * @return CELIX_SUCCESS if the event queue is empty or ETIMEDOUT if the 
timeoutInSeconds is reached.
  */
-CELIX_FRAMEWORK_EXPORT void celix_framework_setLogCallback(celix_framework_t* 
fw, void* logHandle, void (*logFunction)(void* handle, celix_log_level_e level, 
const char* file, const char *function, int line, const char *format, va_list 
formatArgs));
-
+CELIX_FRAMEWORK_EXPORT celix_status_t 
celix_framework_waitForEmptyEventQueueFor(celix_framework_t *fw, double 
timeoutInSeconds);
 
 /**
- * @brief wait until all events for the bundle identified by the bndId are 
processed.
+ * @brief wait until all events from the event queue for the bundle identified 
by the bndId are processed.
+ *
+ * If bndId < 0, wait until all events from the event queue are processed.

Review Comment:
   Not all events, just events associated with bundles, i.e. not including 
framework events.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@celix.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to