assert(current_condition);
if (!lttng_condition_is_equal(condition,
- current_condition)) {
+ current_condition)) {
continue;
}
&iter);
node = cds_lfht_iter_get_node(&iter);
if (!node) {
+ /*
+ * No notification-emiting trigger registered with this
+ * condition. We don't evaluate the condition right away
+ * since this trigger is not registered yet.
+ */
free(client_list_element);
goto end_unlock;
}
client_list = caa_container_of(node, struct notification_client_list,
notification_trigger_ht_node);
+ /*
+ * The condition to which the client just subscribed is evaluated
+ * at this point so that conditions that are already TRUE result
+ * in a notification being sent out.
+ */
if (evaluate_condition_for_client(client_list->trigger, condition,
client, state)) {
WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting.");
cds_lfht_add(state->notification_trigger_clients_ht,
lttng_condition_hash(condition),
&client_list->notification_trigger_ht_node);
- /*
- * Client list ownership transferred to the
- * notification_trigger_clients_ht.
- */
- client_list = NULL;
/*
* Add the trigger to list of triggers bound to the channels currently
break;
}
+ /*
+ * Since there is nothing preventing clients from subscribing to a
+ * condition before the corresponding trigger is registered, we have
+ * to evaluate this new condition right away.
+ *
+ * At some point, we were waiting for the next "evaluation" (e.g. on
+ * reception of a channel sample) to evaluate this new condition, but
+ * that was broken.
+ *
+ * The reason it was broken is that waiting for the next sample
+ * does not allow us to properly handle transitions for edge-triggered
+ * conditions.
+ *
+ * Consider this example: when we handle a new channel sample, we
+ * evaluate each conditions twice: once with the previous state, and
+ * again with the newest state. We then use those two results to
+ * determine whether a state change happened: a condition was false and
+ * became true. If a state change happened, we have to notify clients.
+ *
+ * Now, if a client subscribes to a given notification and registers
+ * a trigger *after* that subscription, we have to make sure the
+ * condition is evaluated at this point while considering only the
+ * current state. Otherwise, the next evaluation cycle may only see
+ * that the evaluations remain the same (true for samples n-1 and n) and
+ * the client will never know that the condition has been met.
+ */
+ cds_list_for_each_entry_safe(client_list_element, tmp,
+ &client_list->list, node) {
+ ret = evaluate_condition_for_client(trigger, condition,
+ client_list_element->client, state);
+ if (ret) {
+ goto error_free_client_list;
+ }
+ }
+
+ /*
+ * Client list ownership transferred to the
+ * notification_trigger_clients_ht.
+ */
+ client_list = NULL;
+
*cmd_result = LTTNG_OK;
error_free_client_list:
if (client_list) {
* notification_trigger_clients_ht,
* - add trigger to channel_triggers_ht (if applicable),
* - add trigger to triggers_ht
+ * - evaluate the trigger's condition right away to react if that condition
+ * is true from the beginning.
*
* 4) Unregistration of a trigger
* - if the trigger's action is of type "notify",
* - Add the condition to the client's list of subscribed conditions,
* - Look-up notification_trigger_clients_ht and add the client to
* list of clients.
+ * - Evaluate the condition for the client that subscribed if the trigger
+ * was already registered.
*
* 9) Unsubscription of a client to a condition's notifications
* - Remove the condition from the client's list of subscribed conditions,