pnoltes commented on code in PR #583: URL: https://github.com/apache/celix/pull/583#discussion_r1258631143
########## libs/framework/src/celix_scheduled_event.c: ########## @@ -0,0 +1,312 @@ +/* + * 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 "celix_scheduled_event.h" + +#include <assert.h> + +#include "celix_constants.h" +#include "celix_utils.h" + +/*! + * @brief Allow error in seconds for the interval. This ensure pthread cond wakeups result in a call even if + * the exact wakeupt time is a bit off. + */ +#define CELIX_SCHEDULED_EVENT_INTERVAL_ALLOW_ERROR_IN_SECONDS 0.000001 + +/** + * @brief Default name for a scheduled event. + */ +static const char* const CELIX_SCHEDULED_EVENT_DEFAULT_NAME = "unnamed"; + +/** + * @brief Struct representing a scheduled event. + * + * A scheduled event is an event that is scheduled to be executed at a certain initial delay and/or interval. + * It is created using the `celix_bundleContext_scheduleEvent` function and can be woken up + * using the `celix_bundleContext_wakeupScheduledEvent` function. + * + * The struct contains information about the scheduled event, such as the event name, initial delay, + * interval, and callback function. It also contains synchronization primitives to protect the use + * count and call count of the event. + * + * @see celix_bundleContext_scheduleEvent + * @see celix_bundleContext_wakeupScheduledEvent + */ +struct celix_scheduled_event { + long scheduledEventId; /**< The ID of the scheduled event. */ + celix_framework_logger_t* logger; /**< The framework logger used to log information */ + long bndId; /**< The bundle id for the bundle that owns the scheduled event. */ + char* eventName; /**< The name of the scheduled event. Will be CELIX_SCHEDULED_EVENT_DEFAULT_NAME if no name is + provided during creation. */ + double logTimeoutInSeconds; /**< The timeout, in seconds, before a log message is printed while waiting for a + scheduled event to be processed or removed. */ + double initialDelayInSeconds; /**< The initial delay of the scheduled event in seconds. */ + double intervalInSeconds; /**< The interval of the scheduled event in seconds. */ + void* callbackData; /**< The data for the scheduled event callback. */ + void (*callback)(void* callbackData); /**< The callback function for the scheduled event. */ + void* removedCallbackData; /**< The data for the scheduled event removed callback. */ + void (*removedCallback)(void* removedCallbackData); /**< The callback function for the scheduled event removed + callback. */ + + celix_thread_mutex_t mutex; /**< The mutex to protect the data below. */ + celix_thread_cond_t cond; /**< The condition variable to signal the scheduled event for a changed callCount and + isRemoved. */ + size_t useCount; /**< The use count of the scheduled event. */ + size_t callCount; /**< The call count of the scheduled event. */ + bool isMarkedForRemoval; /**< Whether the scheduled event is marked for removal. */ + bool isRemoved; /**< Whether the scheduled event is removed. */ + struct timespec lastScheduledEventTime; /**< The last scheduled event time of the scheduled event. */ + bool processForWakeup; /**< Whether the scheduled event should be processed directly due to a wakeupScheduledEvent + call. */ +}; + +celix_scheduled_event_t* celix_scheduledEvent_create(celix_framework_t* fw, + long bndId, + long scheduledEventId, + const char* providedEventName, + double initialDelayInSeconds, + double intervalInSeconds, + void* callbackData, + void (*callback)(void* callbackData), + void* removedCallbackData, + void (*removedCallback)(void* removedCallbackData)) { + celix_scheduled_event_t* event = malloc(sizeof(*event)); + char* eventName = + providedEventName == NULL ? (char*)CELIX_SCHEDULED_EVENT_DEFAULT_NAME : celix_utils_strdup(providedEventName); + if (event == NULL || eventName == NULL) { + fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Cannot add scheduled event for bundle id %li. Out of memory", bndId); + free(event); + if (eventName != CELIX_SCHEDULED_EVENT_DEFAULT_NAME) { + free(eventName); + } + return NULL; + } + + event->scheduledEventId = scheduledEventId; + event->logger = fw->logger; + event->bndId = bndId; + + event->eventName = eventName; + event->logTimeoutInSeconds = + celix_framework_getConfigPropertyAsDouble(fw, + CELIX_ALLOWED_PROCESSING_TIME_FOR_SCHEDULED_EVENT_IN_SECONDS, + CELIX_DEFAULT_ALLOWED_PROCESSING_TIME_FOR_SCHEDULED_EVENT_IN_SECONDS, + NULL); + event->initialDelayInSeconds = initialDelayInSeconds; + event->intervalInSeconds = intervalInSeconds; + event->callbackData = callbackData; + event->callback = callback; + event->removedCallbackData = removedCallbackData; + event->removedCallback = removedCallback; + event->isMarkedForRemoval = false; + event->useCount = 1; + event->callCount = 0; + event->isRemoved = false; + event->lastScheduledEventTime = celixThreadCondition_getTime(); + event->processForWakeup = false; + + celixThreadMutex_create(&event->mutex, NULL); + celixThreadCondition_init(&event->cond, NULL); + + return event; +} + +static void celix_scheduledEvent_destroy(celix_scheduled_event_t* event) { + celixThreadMutex_destroy(&event->mutex); + celixThreadCondition_destroy(&event->cond); + if (event->eventName != CELIX_SCHEDULED_EVENT_DEFAULT_NAME) { + free(event->eventName); + } + free(event); +} + +celix_scheduled_event_t* celix_scheduledEvent_retain(celix_scheduled_event_t* event) { + if (event == NULL) { + return NULL; + } + + celixThreadMutex_lock(&event->mutex); + event->useCount += 1; + celixThreadMutex_unlock(&event->mutex); + return event; +} + +void celix_scheduledEvent_release(celix_scheduled_event_t* event) { + if (event == NULL) { + return; + } + + celixThreadMutex_lock(&event->mutex); + event->useCount -= 1; + bool unused = event->useCount == 0; + celixThreadMutex_unlock(&event->mutex); + + if (unused) { + celix_scheduledEvent_destroy(event); + } +} + +void celix_ScheduledEvent_cleanup(celix_scheduled_event_t** event) { + if (event) { + celix_scheduledEvent_release(*event); + } +} + +const char* celix_scheduledEvent_getName(const celix_scheduled_event_t* event) { return event->eventName; } + +long celix_scheduledEvent_getId(const celix_scheduled_event_t* event) { return event->scheduledEventId; } + +long celix_scheduledEvent_getBundleId(const celix_scheduled_event_t* event) { return event->bndId; } + +bool celix_scheduledEvent_deadlineReached(celix_scheduled_event_t* event, + const struct timespec* currentTime, + double* nextProcessTimeInSeconds) { + celixThreadMutex_lock(&event->mutex); + double elapsed = celix_difftime(&event->lastScheduledEventTime, currentTime); + double deadline = event->callCount == 0 ? event->initialDelayInSeconds : event->intervalInSeconds; + deadline -= CELIX_SCHEDULED_EVENT_INTERVAL_ALLOW_ERROR_IN_SECONDS; + bool deadlineReached = elapsed >= deadline; + if (event->processForWakeup) { + deadlineReached = true; + } + + if (deadlineReached && nextProcessTimeInSeconds) { + *nextProcessTimeInSeconds = + event->intervalInSeconds == 0 /*one shot*/ ? CELIX_FRAMEWORK_DEFAULT_MAX_TIMEDWAIT_EVENT_HANDLER_IN_SECONDS + : event->intervalInSeconds; + } else if (nextProcessTimeInSeconds) { + *nextProcessTimeInSeconds = event->callCount == 0 ? event->initialDelayInSeconds : event->intervalInSeconds; Review Comment: The nextDeadline is now updated in celix_scheduled_event.c, using celixThreadCondition_getDelayedTime right after processing -- 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