Rename C++ header files to .hpp Rename all C++ header files (include/**/*-internal.h, src/**/*.h except argpar and msgpack, some headers in tests) to have the .hpp extension. Doing so highlights that we include some C++ header files in some test files still compiled as C. This is ok for now, as the files they include don't actually contain C++ code incompatible with C yet, but they could eventually. This is something we can fix later. Change-Id: I8bf326b6b2946a3e26704f3ef3ac5831bbe9bc26 Signed-off-by: Simon Marchi <simon.marchi@efficios.com> Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
action executor: use an execution context to validate enqueued action before execution Problem ====== Let's walk over the following scenario: Trigger my_trigger: condition: ust event a action: start session S At time T, session S exists. At T + 1, the event A is hit. At T + 2, the tracer event notification is received and the work item is queued. Here session S have an id of 1. At T + 3, the session S is destroyed and a new session S is created, with a resulting id of 200. At T +4, the work item is popped from the queue and begin execution and will start session S with an id of 200 instead of the session S id 1 that was present at the queuing phase. We effectively want to assign an execution context to an action at the queuing phase and validate that the execution context is still valid upon actual execution. Solution ===== Instead of iterating over the trigger's actions, we prepare an array of work sub-item (action_work_subitem) that effectively is a flattened and augmented array of the trigger's action. For now only the session id is an action level execution context. Each action handler is responsible of validating the execution context before performing the action. Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com> Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com> Change-Id: I72fbe8d52d77d1d696fa7d99b662b77e9dad02d5
sessiond: trigger: run trigger actions through an action executor The `action executor` interface allows the notification subsystem to enqueue work items to execute on behalf of a given trigger. This allows the notification thread to remain responsive even if the actions to execute are blocking (as through the use of network communication). Before this commit, the notification subsystem only handled `notify` actions; handling code for new action types are added as part of the action executor. The existing `notify` action is now performed through the action executor so that all actions can be managed in the same way. This is less efficient than sending the notifications directly, but could be optimized trivially (if it ever becomes a problem) when: - the action is a group containing only a `notify` action, - the action is a `notify` action. Overview of changes to existing code === Managing the new action types requires fairly localized changes to the existing notification subsystem code. The main code paths that are modified are the sites where `evaluation` objects are created: 1) on an object state change (session or channel state changes, see handle_notification_thread_channel_sample and handle_notification_thread_command_session_rotation), 2) on registration of a trigger (see handle_notification_thread_command_register_trigger), 3) on subscription to a condition (see client_handle_message_subscription). To understand the lifetime of most objects involved in a work deferral to the action executor, see the paragraph in notification-thread-internal.h (line 82) to understand the relation between clients and client lists. 1) Object state changes As hinted in the notification_client_list documentation, deferring work on a state change is straight-forward: a reference is taken on a client list and the list is provided to the action executor as part of a work item. Hence, very little changes are made to the the two state-change handling sites beyond enqueuing a work item rather than directly sending a notification. 2) Subscription to a condition A notification client can subscribe to a condition before or after a matching trigger (same condition and containing a notify action) has been registered. If no matching trigger were registered, no client list exists and there is nothing to do. If a matching trigger existed, a client list (which could be empty) will already exist and the client is simply added to the client list. However, it is important to evaluate the condition for the client (as the condition could already be true) and send the notification to that client only and not to all clients in the list. Before this change, since everything was done in the same thread, a temporary list containing only the newly-subscribed client was created on the stack and the notification was sent/queued immediately. After sending the condition, the client was removed from the temporary list and added to the "real" client list. This strategy cannot be used with the action executor as the "temporary" client list must exist beyond the scope of the function. Moreover, the notification subsystem assumes that clients are in per-condition client lists and that they can safely be destroyed when they are not present in any list. Fortunately, here we know that the action to perform is to `notify` and nothing else. The enqueuing of the notification is performed "in place" by the notification thread without deferring to the action executor. 3) Registration of a trigger When a client subscribes to a condition, the current state of that condition is immediately evaluated. If the condition is true (for instance, a channel's buffer are filled beyond X% of their capacity), the action associated with the trigger is executed right away. This path requires little changes as a client list is created when a trigger is registered. Hence, it is possible to use the client list to defer work as is done in `1`. 4) Trigger registration Since the `notify` action was the only supported action type, the notification subsystem always created a client list associated with the new trigger's condition. This is changed to only perform the creation (and publication) of the client list if the trigger's action is (or contains, in the case of a group) a `notify` action. Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com> Change-Id: I43b54b93c1244591aeff6e0d0fa8076c7b5e0c50
Revert "sessiond: trigger: run trigger actions through an action executor" This reverts commit d1ba29d290281cf72ca3ec7b0222b336c747e925. Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com> Change-Id: I70d57fef86aea94a590720689af751e2554184d0
sessiond: trigger: run trigger actions through an action executor The `action executor` interface allows the notification subsystem to enqueue work items to execute on behalf of a given trigger. This allows the notification thread to remain responsive even if the actions to execute are blocking (as through the use of network communication). Before this commit, the notification subsystem only handled `notify` actions; handling code for new action types are added as part of the action executor. The existing `notify` action is now performed through the action executor so that all actions can be managed in the same way. This is less efficient than sending the notifications directly, but could be optimized trivially (if it ever becomes a problem) when: - the action is a group containing only a `notify` action, - the action is a `notify` action. Managing the new action types requires fairly localized changes to the existing notification subsystem code. The main code paths that are modified are the sites where `evaluation` objects are created: - on an object state change (session or channel state changes, see handle_notification_thread_channel_sample and handle_notification_thread_command_session_rotation), - on registration of a trigger (see handle_notification_thread_command_register_trigger), - on subscription to a condition (see client_handle_message_subscription). To understand the lifetime of most objects involved in a work deferral to the action executor, see the paragraph in notification-thread-internal.h (line 82) to understand the relation between clients and client lists. Overview of changes === Object state changes Change-Id: I23290e94d98e781992661f0aee88de9986ed274f --- As hinted in the notification_client_list documentation, defering work on a state change is straight-forward: a reference is taken on a client list and the list is provided to the action executor as part of a work item. Hence, very little changes are made to the the two state-change handling sites beyond enqueuing a work item rather than directly sending a notification. Subscription to a condition --- A notification client can subscribe to a condition before or after a matching trigger (same condition and containing a notify action) has been registered. When a client subscribes to a condition, it is a added to a corresponding "client list" Registration of a trigger --- When a client subscribes to a condition, the current state of that condition is immediately evaluated. If the condition is true (for instance, a channel's buffer are filled beyond X% of their capacity), TODO: Change-Id: I7f9bc197715c9ca008a4f1fcd4c86e01b6252dce Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>