+static
+void lttng_create_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler)
+{
+ struct lttng_trigger_group *trigger_group = trigger_enabler->group;
+ struct lttng_probe_desc *probe_desc;
+ struct cds_list_head *probe_list;
+ int i;
+
+ probe_list = lttng_get_probe_list_head();
+
+ cds_list_for_each_entry(probe_desc, probe_list, head) {
+ for (i = 0; i < probe_desc->nr_events; i++) {
+ int ret;
+ bool found = false;
+ const struct lttng_event_desc *desc;
+ struct lttng_trigger *trigger;
+ struct cds_hlist_head *head;
+ struct cds_hlist_node *node;
+
+ desc = probe_desc->event_desc[i];
+ if (!lttng_desc_match_enabler(desc,
+ lttng_trigger_enabler_as_enabler(trigger_enabler)))
+ continue;
+
+ /*
+ * Given the current trigger group, get the bucket that
+ * the target trigger would be if it was already
+ * created.
+ */
+ head = borrow_hash_table_bucket(
+ trigger_group->triggers_ht.table,
+ LTTNG_UST_TRIGGER_HT_SIZE, desc);
+
+ cds_hlist_for_each_entry(trigger, node, head, hlist) {
+ /*
+ * Check if trigger already exists by checking
+ * if the trigger and enabler share the same
+ * description and id.
+ */
+ if (trigger->desc == desc &&
+ trigger->id == trigger_enabler->id) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ /*
+ * We need to create a trigger for this event probe.
+ */
+ ret = lttng_trigger_create(desc, trigger_enabler->id,
+ trigger_group);
+ if (ret) {
+ DBG("Unable to create trigger %s, error %d\n",
+ probe_desc->event_desc[i]->name, ret);
+ }
+ }
+ }
+}
+
+void lttng_trigger_send_notification(struct lttng_trigger *trigger)
+{
+ /*
+ * We want this write to be atomic AND non-blocking, meaning that we
+ * want to write either everything OR nothing.
+ * According to `pipe(7)`, writes that are smaller that the `PIPE_BUF`
+ * value must be atomic, so we assert that the message we send is less
+ * than PIPE_BUF.
+ */
+ struct lttng_ust_trigger_notification notif;
+ ssize_t ret;
+
+ assert(trigger);
+ assert(trigger->group);
+ assert(sizeof(notif) <= PIPE_BUF);
+
+ notif.id = trigger->id;
+
+ ret = patient_write(trigger->group->notification_fd, ¬if,
+ sizeof(notif));
+ if (ret == -1) {
+ if (errno == EAGAIN) {
+ DBG("Cannot send trigger notification without blocking: %s",
+ strerror(errno));
+ } else {
+ DBG("Error to sending trigger notification: %s",
+ strerror(errno));
+ abort();
+ }
+ }
+}
+
+/*
+ * Create triggers associated with a trigger enabler (if not already present).
+ */
+static
+int lttng_trigger_enabler_ref_triggers(struct lttng_trigger_enabler *trigger_enabler)
+{
+ struct lttng_trigger_group *trigger_group = trigger_enabler->group;
+ struct lttng_trigger *trigger;
+
+ /* First, ensure that probe triggers are created for this enabler. */
+ lttng_create_trigger_if_missing(trigger_enabler);
+
+ /* Link the created trigger with its associated enabler. */
+ cds_list_for_each_entry(trigger, &trigger_group->triggers_head, node) {
+ struct lttng_enabler_ref *enabler_ref;
+
+ if (!lttng_trigger_enabler_match_trigger(trigger_enabler, trigger))
+ continue;
+
+ enabler_ref = lttng_enabler_ref(&trigger->enablers_ref_head,
+ lttng_trigger_enabler_as_enabler(trigger_enabler));
+ if (!enabler_ref) {
+ /*
+ * If no backward ref, create it.
+ * Add backward ref from trigger to enabler.
+ */
+ enabler_ref = zmalloc(sizeof(*enabler_ref));
+ if (!enabler_ref)
+ return -ENOMEM;
+
+ enabler_ref->ref = lttng_trigger_enabler_as_enabler(
+ trigger_enabler);
+ cds_list_add(&enabler_ref->node,
+ &trigger->enablers_ref_head);
+ }
+
+ /*
+ * Link filter bytecodes if not linked yet.
+ */
+ lttng_enabler_link_bytecode(trigger->desc,
+ &trigger_group->ctx, &trigger->bytecode_runtime_head,
+ lttng_trigger_enabler_as_enabler(trigger_enabler));
+ }
+ return 0;
+}
+
+static
+void lttng_trigger_group_sync_enablers(struct lttng_trigger_group *trigger_group)
+{
+ struct lttng_trigger_enabler *trigger_enabler;
+ struct lttng_trigger *trigger;
+
+ cds_list_for_each_entry(trigger_enabler, &trigger_group->enablers_head, node) {
+ /*
+ * Only link enablers that are enabled to triggers, the user
+ * might still be attaching filter or exclusion to the
+ * trigger_enabler.
+ */
+ if (!lttng_trigger_enabler_as_enabler(trigger_enabler)->enabled)
+ continue;
+
+ lttng_trigger_enabler_ref_triggers(trigger_enabler);
+ }
+
+ /*
+ * For each trigger, if at least one of its enablers is enabled,
+ * we enable the trigger, else we disable it.
+ */
+ cds_list_for_each_entry(trigger, &trigger_group->triggers_head, node) {
+ struct lttng_enabler_ref *enabler_ref;
+ struct lttng_bytecode_runtime *runtime;
+ int enabled = 0, has_enablers_without_bytecode = 0;
+
+ /* Enable triggers */
+ cds_list_for_each_entry(enabler_ref,
+ &trigger->enablers_ref_head, node) {
+ if (enabler_ref->ref->enabled) {
+ enabled = 1;
+ break;
+ }
+ }
+
+ CMM_STORE_SHARED(trigger->enabled, enabled);
+ /*
+ * Sync tracepoint registration with trigger enabled
+ * state.
+ */
+ if (enabled) {
+ if (!trigger->registered)
+ register_trigger(trigger);
+ } else {
+ if (trigger->registered)
+ unregister_trigger(trigger);
+ }
+
+ /* Check if has enablers without bytecode enabled */
+ cds_list_for_each_entry(enabler_ref,
+ &trigger->enablers_ref_head, node) {
+ if (enabler_ref->ref->enabled
+ && cds_list_empty(&enabler_ref->ref->filter_bytecode_head)) {
+ has_enablers_without_bytecode = 1;
+ break;
+ }
+ }
+ trigger->has_enablers_without_bytecode =
+ has_enablers_without_bytecode;
+
+ /* Enable filters */
+ cds_list_for_each_entry(runtime,
+ &trigger->bytecode_runtime_head, node) {
+ lttng_filter_sync_state(runtime);
+ }
+ }
+ __tracepoint_probe_prune_release_queue();
+}
+