proto_load_step and proto_cron_crontab drive themselves from timers rather than from their fd, and neither had a close callback, so nothing stopped them when the listener was closed. The load generator kept manufacturing packets and handing them to fr_network_send_request() after the network had signalled the workers to close, which the assert added there caught.
Both now close the fd and remove their timer. Of the remaining app_io modules with timers, proto_detail_file, proto_detail_work and proto_ldap_sync_ldap have a close callback that does not obviously disarm anything, and are worth a look.
fr_network_listen_read(thread->nr, thread->parent);
}
fr_network_listen_read(thread->nr, thread->parent);
}
+/** Close a virtual listener
+ *
+ * The fd only exists to bootstrap the listener, so closing it mostly means
+ * removing the timer that runs the jobs.
+ *
+ * @param[in] li the listener
+ * @return
+ * - 0 on success.
+ * - -1 if the timer could not be deleted. The fd is closed either way.
+ */
+static int mod_close(fr_listen_t *li)
+{
+ proto_cron_crontab_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_cron_crontab_thread_t);
+ int ret = 0;
+
+ if (thread->ev && (fr_timer_delete(&thread->ev) < 0)) {
+ PERROR("Failed deleting cron timer");
+ ret = -1;
+ }
+
+ close(li->fd);
+
+ return ret;
+}
+
/** Set the event list for a new socket
*
* @param[in] li the listener
/** Set the event list for a new socket
*
* @param[in] li the listener
.track_duplicates = false,
.open = mod_open,
.track_duplicates = false,
.open = mod_open,
.read = mod_read,
.write = mod_write,
.event_list_set = mod_event_list_set,
.read = mod_read,
.write = mod_write,
.event_list_set = mod_event_list_set,
+/** Close a virtual listener
+ *
+ * The fd only exists to bootstrap the listener, so closing it mostly means
+ * removing the timer that drives the load generator.
+ *
+ * @param[in] li the listener
+ * @return
+ * - 0 on success.
+ * - -1 if the generator could not be stopped. The fd is closed either way.
+ */
+static int mod_close(fr_listen_t *li)
+{
+ proto_load_step_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_load_step_thread_t);
+ int ret = 0;
+
+ if (thread->l && (fr_load_generator_stop(thread->l) < 0)) {
+ PERROR("Failed stopping load generator");
+ ret = -1;
+ }
+
+ close(li->fd);
+
+ return ret;
+}
+
/** Set the event list for a new socket
*
* @param[in] li the listener
/** Set the event list for a new socket
*
* @param[in] li the listener
.track_duplicates = false,
.open = mod_open,
.track_duplicates = false,
.open = mod_open,
.read = mod_read,
.write = mod_write,
.event_list_set = mod_event_list_set,
.read = mod_read,
.write = mod_write,
.event_list_set = mod_event_list_set,