Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 29 Mar 2018 04:41:46 +0000 (UTC)
From:      Matt Joras <mjoras@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r331727 - in stable/11: share/man/man9 sys/kern sys/sys
Message-ID:  <201803290441.w2T4fkTs033056@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mjoras
Date: Thu Mar 29 04:41:45 2018
New Revision: 331727
URL: https://svnweb.freebsd.org/changeset/base/331727

Log:
  MFC r325621, r325622, r331227
  
  Add EVENTHANDLER_LIST and some users.
  
  Also fix a longstanding bug in mtx initialization.

Modified:
  stable/11/share/man/man9/EVENTHANDLER.9
  stable/11/sys/kern/init_main.c
  stable/11/sys/kern/kern_exec.c
  stable/11/sys/kern/kern_exit.c
  stable/11/sys/kern/kern_fork.c
  stable/11/sys/kern/kern_proc.c
  stable/11/sys/kern/kern_thread.c
  stable/11/sys/kern/subr_eventhandler.c
  stable/11/sys/sys/eventhandler.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/share/man/man9/EVENTHANDLER.9
==============================================================================
--- stable/11/share/man/man9/EVENTHANDLER.9	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/share/man/man9/EVENTHANDLER.9	Thu Mar 29 04:41:45 2018	(r331727)
@@ -23,7 +23,7 @@
 .\" SUCH DAMAGE.
 .\" $FreeBSD$
 .\"
-.Dd October 1, 2017
+.Dd October 31, 2017
 .Dt EVENTHANDLER 9
 .Os
 .Sh NAME
@@ -38,6 +38,9 @@
 .Fn EVENTHANDLER_REGISTER name func arg priority
 .Fn EVENTHANDLER_DEREGISTER name tag
 .Fn EVENTHANDLER_DEREGISTER_NOWAIT name tag
+.Fn EVENTHANDLER_LIST_DECLARE name
+.Fn EVENTHANDLER_LIST_DEFINE name
+.Fn EVENTHANDLER_DIRECT_INVOKE name
 .Ft eventhandler_tag
 .Fo eventhandler_register
 .Fa "struct eventhandler_list *list"
@@ -82,8 +85,13 @@ may be used if the handler does not have a specific pr
 associated with it.
 .Pp
 The normal way to use this subsystem is via the macro interface.
-The macros that can be used for working with event handlers and callback
-function lists are:
+For events that are high frequency it is suggested that you additionally use
+.Fn EVENTHANDLER_LIST_DEFINE
+so that the event handlers can be invoked directly using
+.Fn EVENTHANDLER_DIRECT_INVOKE
+(see below).
+This saves the invoker from having to do a locked traversal of a global
+list of event handler lists.
 .Bl -tag -width indent
 .It Fn EVENTHANDLER_DECLARE
 This macro declares an event handler named by argument
@@ -148,6 +156,27 @@ Additional arguments to the macro after the
 .Fa name
 parameter are passed as the second and subsequent arguments to each
 registered callback function.
+.It Fn EVENTHANDLER_LIST_DEFINE
+This macro defines a reference to an event handler list named by
+argument
+.Fa name .
+It uses
+.Xr SYSINIT 9
+to initialize the reference and the eventhandler list.
+.It Fn EVENTHANDLER_LIST_DECLARE
+This macro declares an event handler list named by argument
+.Fa name .
+This is only needed for users of
+.Fn EVENTHANDLER_DIRECT_INVOKE
+which are not in the same compilation unit of that list's definition.
+.It Fn EVENTHANDLER_DIRECT_INVOKE
+This macro invokes the event handlers registered for the list named by
+argument
+.Fa name .
+This macro can only be used if the list was defined with
+.Fn EVENTHANDLER_LIST_DEFINE .
+The macro is variadic with the same semantics as
+.Fn EVENTHANDLER_INVOKE .
 .El
 .Pp
 The macros are implemented using the following functions:
@@ -315,7 +344,7 @@ This is never called.
 .It Vt process_fork
 Callbacks invoked when a process forks a child.
 .It Vt process_init
-Callback invoked when a process is initalized.
+Callback invoked when a process is initialized.
 .It Vt random_adaptor_attach
 Callback invoked when a new random module has been loaded.
 .It Vt register_framebuffer
@@ -337,7 +366,7 @@ Callback invoked when a thread object is created.
 .It Vt thread_dtor
 Callback invoked when a thread object is destroyed.
 .It Vt thread_init
-Callback invoked when a thread object is initalized.
+Callback invoked when a thread object is initialized.
 .It Vt thread_fini
 Callback invoked when a thread object is deinitalized.
 .It Vt usb_dev_configured
@@ -384,4 +413,6 @@ facility first appeared in
 .Fx 4.0 .
 .Sh AUTHORS
 This manual page was written by
-.An Joseph Koshy Aq Mt jkoshy@FreeBSD.org .
+.An Joseph Koshy Aq Mt jkoshy@FreeBSD.org
+and
+.An Matt Joras Aq Mt mjoras@FreeBSD.org .

Modified: stable/11/sys/kern/init_main.c
==============================================================================
--- stable/11/sys/kern/init_main.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/init_main.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -136,6 +136,11 @@ SET_DECLARE(sysinit_set, struct sysinit);
 struct sysinit **sysinit, **sysinit_end;
 struct sysinit **newsysinit, **newsysinit_end;
 
+EVENTHANDLER_LIST_DECLARE(process_init);
+EVENTHANDLER_LIST_DECLARE(thread_init);
+EVENTHANDLER_LIST_DECLARE(process_ctor);
+EVENTHANDLER_LIST_DECLARE(thread_ctor);
+
 /*
  * Merge a new sysinit set into the current set, reallocating it if
  * necessary.  This can only be called after malloc is running.
@@ -585,10 +590,10 @@ proc0_init(void *dummy __unused)
 	 * Call the init and ctor for the new thread and proc.  We wait
 	 * to do this until all other structures are fairly sane.
 	 */
-	EVENTHANDLER_INVOKE(process_init, p);
-	EVENTHANDLER_INVOKE(thread_init, td);
-	EVENTHANDLER_INVOKE(process_ctor, p);
-	EVENTHANDLER_INVOKE(thread_ctor, td);
+	EVENTHANDLER_DIRECT_INVOKE(process_init, p);
+	EVENTHANDLER_DIRECT_INVOKE(thread_init, td);
+	EVENTHANDLER_DIRECT_INVOKE(process_ctor, p);
+	EVENTHANDLER_DIRECT_INVOKE(thread_ctor, td);
 
 	/*
 	 * Charge root for one process.

Modified: stable/11/sys/kern/kern_exec.c
==============================================================================
--- stable/11/sys/kern/kern_exec.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/kern_exec.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -143,6 +143,8 @@ static int map_at_zero = 0;
 SYSCTL_INT(_security_bsd, OID_AUTO, map_at_zero, CTLFLAG_RWTUN, &map_at_zero, 0,
     "Permit processes to map an object at virtual address 0.");
 
+EVENTHANDLER_LIST_DECLARE(process_exec);
+
 static int
 sysctl_kern_ps_strings(SYSCTL_HANDLER_ARGS)
 {
@@ -1073,7 +1075,7 @@ exec_new_vmspace(struct image_params *imgp, struct sys
 	imgp->sysent = sv;
 
 	/* May be called with Giant held */
-	EVENTHANDLER_INVOKE(process_exec, p, imgp);
+	EVENTHANDLER_DIRECT_INVOKE(process_exec, p, imgp);
 
 	/*
 	 * Blow away entire process VM, if address space not shared,

Modified: stable/11/sys/kern/kern_exit.c
==============================================================================
--- stable/11/sys/kern/kern_exit.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/kern_exit.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -99,6 +99,8 @@ SDT_PROBE_DEFINE1(proc, , , exit, "int");
 /* Hook for NFS teardown procedure. */
 void (*nlminfo_release_p)(struct proc *p);
 
+EVENTHANDLER_LIST_DECLARE(process_exit);
+
 struct proc *
 proc_realparent(struct proc *child)
 {
@@ -329,7 +331,7 @@ exit1(struct thread *td, int rval, int signo)
 	 * Event handler could change exit status.
 	 * XXX what if one of these generates an error?
 	 */
-	EVENTHANDLER_INVOKE(process_exit, p);
+	EVENTHANDLER_DIRECT_INVOKE(process_exit, p);
 
 	/*
 	 * If parent is waiting for us to exit or exec,

Modified: stable/11/sys/kern/kern_fork.c
==============================================================================
--- stable/11/sys/kern/kern_fork.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/kern_fork.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -97,6 +97,8 @@ struct fork_args {
 };
 #endif
 
+EVENTHANDLER_LIST_DECLARE(process_fork);
+
 /* ARGSUSED */
 int
 sys_fork(struct thread *td, struct fork_args *uap)
@@ -705,7 +707,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct
 	 * Both processes are set up, now check if any loadable modules want
 	 * to adjust anything.
 	 */
-	EVENTHANDLER_INVOKE(process_fork, p1, p2, fr->fr_flags);
+	EVENTHANDLER_DIRECT_INVOKE(process_fork, p1, p2, fr->fr_flags);
 
 	/*
 	 * Set the child start time and mark the process as being complete.

Modified: stable/11/sys/kern/kern_proc.c
==============================================================================
--- stable/11/sys/kern/kern_proc.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/kern_proc.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -151,6 +151,17 @@ const int thread_off_td_oncpu = offsetof(struct thread
 const int thread_off_td_pcb = offsetof(struct thread, td_pcb);
 const int thread_off_td_plist = offsetof(struct thread, td_plist);
 
+EVENTHANDLER_LIST_DEFINE(process_ctor);
+EVENTHANDLER_LIST_DEFINE(process_dtor);
+EVENTHANDLER_LIST_DEFINE(process_init);
+EVENTHANDLER_LIST_DEFINE(process_fini);
+EVENTHANDLER_LIST_DEFINE(process_exit);
+EVENTHANDLER_LIST_DEFINE(process_fork);
+EVENTHANDLER_LIST_DEFINE(process_exec);
+
+EVENTHANDLER_LIST_DECLARE(thread_ctor);
+EVENTHANDLER_LIST_DECLARE(thread_dtor);
+
 int kstack_pages = KSTACK_PAGES;
 SYSCTL_INT(_kern, OID_AUTO, kstack_pages, CTLFLAG_RD, &kstack_pages, 0,
     "Kernel stack size in pages");
@@ -195,12 +206,12 @@ proc_ctor(void *mem, int size, void *arg, int flags)
 
 	p = (struct proc *)mem;
 	SDT_PROBE4(proc, , ctor , entry, p, size, arg, flags);
-	EVENTHANDLER_INVOKE(process_ctor, p);
+	EVENTHANDLER_DIRECT_INVOKE(process_ctor, p);
 	SDT_PROBE4(proc, , ctor , return, p, size, arg, flags);
 	td = FIRST_THREAD_IN_PROC(p);
 	if (td != NULL) {
 		/* Make sure all thread constructors are executed */
-		EVENTHANDLER_INVOKE(thread_ctor, td);
+		EVENTHANDLER_DIRECT_INVOKE(thread_ctor, td);
 	}
 	return (0);
 }
@@ -230,9 +241,9 @@ proc_dtor(void *mem, int size, void *arg)
 		MPASS(td->td_su == NULL);
 
 		/* Make sure all thread destructors are executed */
-		EVENTHANDLER_INVOKE(thread_dtor, td);
+		EVENTHANDLER_DIRECT_INVOKE(thread_dtor, td);
 	}
-	EVENTHANDLER_INVOKE(process_dtor, p);
+	EVENTHANDLER_DIRECT_INVOKE(process_dtor, p);
 	if (p->p_ksi != NULL)
 		KASSERT(! KSI_ONQ(p->p_ksi), ("SIGCHLD queue"));
 	SDT_PROBE3(proc, , dtor, return, p, size, arg);
@@ -256,7 +267,7 @@ proc_init(void *mem, int size, int flags)
 	cv_init(&p->p_pwait, "ppwait");
 	cv_init(&p->p_dbgwait, "dbgwait");
 	TAILQ_INIT(&p->p_threads);	     /* all threads in proc */
-	EVENTHANDLER_INVOKE(process_init, p);
+	EVENTHANDLER_DIRECT_INVOKE(process_init, p);
 	p->p_stats = pstats_alloc();
 	p->p_pgrp = NULL;
 	SDT_PROBE3(proc, , init, return, p, size, flags);
@@ -274,7 +285,7 @@ proc_fini(void *mem, int size)
 	struct proc *p;
 
 	p = (struct proc *)mem;
-	EVENTHANDLER_INVOKE(process_fini, p);
+	EVENTHANDLER_DIRECT_INVOKE(process_fini, p);
 	pstats_free(p->p_stats);
 	thread_free(FIRST_THREAD_IN_PROC(p));
 	mtx_destroy(&p->p_mtx);

Modified: stable/11/sys/kern/kern_thread.c
==============================================================================
--- stable/11/sys/kern/kern_thread.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/kern_thread.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -144,6 +144,11 @@ struct	tidhashhead *tidhashtbl;
 u_long	tidhash;
 struct	rwlock tidhash_lock;
 
+EVENTHANDLER_LIST_DEFINE(thread_ctor);
+EVENTHANDLER_LIST_DEFINE(thread_dtor);
+EVENTHANDLER_LIST_DEFINE(thread_init);
+EVENTHANDLER_LIST_DEFINE(thread_fini);
+
 static lwpid_t
 tid_alloc(void)
 {
@@ -201,7 +206,7 @@ thread_ctor(void *mem, int size, void *arg, int flags)
 	 */
 	td->td_critnest = 1;
 	td->td_lend_user_pri = PRI_MAX;
-	EVENTHANDLER_INVOKE(thread_ctor, td);
+	EVENTHANDLER_DIRECT_INVOKE(thread_ctor, td);
 #ifdef AUDIT
 	audit_thread_alloc(td);
 #endif
@@ -247,7 +252,7 @@ thread_dtor(void *mem, int size, void *arg)
 	td_softdep_cleanup(td);
 	MPASS(td->td_su == NULL);
 
-	EVENTHANDLER_INVOKE(thread_dtor, td);
+	EVENTHANDLER_DIRECT_INVOKE(thread_dtor, td);
 	tid_free(td->td_tid);
 }
 
@@ -264,7 +269,7 @@ thread_init(void *mem, int size, int flags)
 	td->td_sleepqueue = sleepq_alloc();
 	td->td_turnstile = turnstile_alloc();
 	td->td_rlqe = NULL;
-	EVENTHANDLER_INVOKE(thread_init, td);
+	EVENTHANDLER_DIRECT_INVOKE(thread_init, td);
 	umtx_thread_init(td);
 	td->td_kstack = 0;
 	td->td_sel = NULL;
@@ -280,7 +285,7 @@ thread_fini(void *mem, int size)
 	struct thread *td;
 
 	td = (struct thread *)mem;
-	EVENTHANDLER_INVOKE(thread_fini, td);
+	EVENTHANDLER_DIRECT_INVOKE(thread_fini, td);
 	rlqentry_free(td->td_rlqe);
 	turnstile_free(td->td_turnstile);
 	sleepq_free(td->td_sleepqueue);

Modified: stable/11/sys/kern/subr_eventhandler.c
==============================================================================
--- stable/11/sys/kern/subr_eventhandler.c	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/kern/subr_eventhandler.c	Thu Mar 29 04:41:45 2018	(r331727)
@@ -38,7 +38,7 @@ __FBSDID("$FreeBSD$");
 
 static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
 
-/* List of 'slow' lists */
+/* List of all eventhandler lists */
 static TAILQ_HEAD(, eventhandler_list)	eventhandler_lists;
 static int				eventhandler_lists_initted = 0;
 static struct mtx			eventhandler_mutex;
@@ -64,25 +64,11 @@ eventhandler_init(void *dummy __unused)
 SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init,
     NULL);
 
-/* 
- * Insertion is O(n) due to the priority scan, but optimises to O(1)
- * if all priorities are identical.
- */
-static eventhandler_tag
-eventhandler_register_internal(struct eventhandler_list *list,
-    const char *name, eventhandler_tag epn)
+static struct eventhandler_list *
+eventhandler_find_or_create_list(const char *name)
 {
-    struct eventhandler_list		*new_list;
-    struct eventhandler_entry		*ep;
-    
-    KASSERT(eventhandler_lists_initted, ("eventhandler registered too early"));
-    KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__));
+	struct eventhandler_list *list, *new_list;
 
-    /* lock the eventhandler lists */
-    mtx_lock(&eventhandler_mutex);
-
-    /* Do we need to find/create the (slow) list? */
-    if (list == NULL) {
 	/* look for a matching, existing list */
 	list = _eventhandler_find_list(name);
 
@@ -90,8 +76,8 @@ eventhandler_register_internal(struct eventhandler_lis
 	if (list == NULL) {
 	    mtx_unlock(&eventhandler_mutex);
 
-	    new_list = malloc(sizeof(struct eventhandler_list) +
-		strlen(name) + 1, M_EVENTHANDLER, M_WAITOK);
+	    new_list = malloc(sizeof(*new_list) + strlen(name) + 1,
+		M_EVENTHANDLER, M_WAITOK | M_ZERO);
 
 	    /* If someone else created it already, then use that one. */
 	    mtx_lock(&eventhandler_mutex);
@@ -101,21 +87,36 @@ eventhandler_register_internal(struct eventhandler_lis
 	    } else {
 		CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name);
 		list = new_list;
-		list->el_flags = 0;
-		list->el_runcount = 0;
-		bzero(&list->el_lock, sizeof(list->el_lock));
-		list->el_name = (char *)list + sizeof(struct eventhandler_list);
+		TAILQ_INIT(&list->el_entries);
+		list->el_name = (char *)(list + 1);
 		strcpy(list->el_name, name);
+		mtx_init(&list->el_lock, list->el_name, "eventhandler list",
+		    MTX_DEF);
 		TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link);
 	    }
 	}
+	return (list);
+}
+
+/* 
+ * Insertion is O(n) due to the priority scan, but optimises to O(1)
+ * if all priorities are identical.
+ */
+static eventhandler_tag
+eventhandler_register_internal(struct eventhandler_list *list,
+    const char *name, eventhandler_tag epn)
+{
+    struct eventhandler_entry		*ep;
+    
+    KASSERT(eventhandler_lists_initted, ("eventhandler registered too early"));
+    KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__));
+
+    /* Do we need to find/create the list? */
+    if (list == NULL) {
+	    mtx_lock(&eventhandler_mutex);
+	    list = eventhandler_find_or_create_list(name);
+	    mtx_unlock(&eventhandler_mutex);
     }
-    if (!(list->el_flags & EHL_INITTED)) {
-	TAILQ_INIT(&list->el_entries);
-	mtx_init(&list->el_lock, name, "eventhandler list", MTX_DEF);
-	atomic_store_rel_int(&list->el_flags, EHL_INITTED);
-    }
-    mtx_unlock(&eventhandler_mutex);
 
     KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
 	("%s: handler for %s registered with dead priority", __func__, name));
@@ -293,4 +294,23 @@ eventhandler_prune_list(struct eventhandler_list *list
     }
     if (pruned > 0)
 	    wakeup(list);
+}
+
+/*
+ * Create (or get the existing) list so the pointer can be stored by
+ * EVENTHANDLER_LIST_DEFINE.
+ */
+struct eventhandler_list *
+eventhandler_create_list(const char *name)
+{
+	struct eventhandler_list *list;
+
+	KASSERT(eventhandler_lists_initted,
+	    ("eventhandler list created too early"));
+
+	mtx_lock(&eventhandler_mutex);
+	list = eventhandler_find_or_create_list(name);
+	mtx_unlock(&eventhandler_mutex);
+
+	return (list);
 }

Modified: stable/11/sys/sys/eventhandler.h
==============================================================================
--- stable/11/sys/sys/eventhandler.h	Thu Mar 29 04:14:37 2018	(r331726)
+++ stable/11/sys/sys/eventhandler.h	Thu Mar 29 04:41:45 2018	(r331727)
@@ -51,8 +51,7 @@ struct eventhandler_entry_vimage {
 
 struct eventhandler_list {
 	char				*el_name;
-	int				el_flags;
-#define EHL_INITTED	(1<<0)
+	int				el_flags;	/* Unused. */
 	u_int				el_runcount;
 	struct mtx			el_lock;
 	TAILQ_ENTRY(eventhandler_list)	el_link;
@@ -72,8 +71,6 @@ typedef struct eventhandler_entry	*eventhandler_tag;
 	struct eventhandler_entry *_ep;					\
 	struct eventhandler_entry_ ## name *_t;				\
 									\
-	KASSERT((list)->el_flags & EHL_INITTED,				\
- 	   ("eventhandler_invoke: running non-inited list"));		\
 	EHL_LOCK_ASSERT((list), MA_OWNED);				\
 	(list)->el_runcount++;						\
 	KASSERT((list)->el_runcount > 0,				\
@@ -98,10 +95,41 @@ typedef struct eventhandler_entry	*eventhandler_tag;
 } while (0)
 
 /*
- * Slow handlers are entirely dynamic; lists are created
- * when entries are added to them, and thus have no concept of "owner",
- *
- * Slow handlers need to be declared, but do not need to be defined. The
+ * You can optionally use the EVENTHANDLER_LIST and EVENTHANDLER_DIRECT macros
+ * to pre-define a symbol for the eventhandler list. This symbol can be used by
+ * EVENTHANDLER_DIRECT_INVOKE, which has the advantage of not needing to do a
+ * locked search of the global list of eventhandler lists. At least
+ * EVENTHANDLER_LIST_DEFINE must be be used for EVENTHANDLER_DIRECT_INVOKE to
+ * work. EVENTHANDLER_LIST_DECLARE is only needed if the call to
+ * EVENTHANDLER_DIRECT_INVOKE is in a different compilation unit from
+ * EVENTHANDLER_LIST_DEFINE. If the events are even relatively high frequency
+ * it is suggested that you directly define a list for them.
+ */
+#define	EVENTHANDLER_LIST_DECLARE(name)					\
+extern struct eventhandler_list *_eventhandler_list_ ## name		\
+
+#define	EVENTHANDLER_LIST_DEFINE(name)					\
+struct eventhandler_list *_eventhandler_list_ ## name ;			\
+static void _ehl_init_ ## name (void * ctx __unused)			\
+{									\
+	_eventhandler_list_ ## name = eventhandler_create_list(#name);	\
+}									\
+SYSINIT(name ## _ehl_init, SI_SUB_EVENTHANDLER, SI_ORDER_ANY,		\
+	    _ehl_init_ ## name, NULL);					\
+	struct __hack
+
+#define	EVENTHANDLER_DIRECT_INVOKE(name, ...) do {			\
+	struct eventhandler_list *_el;					\
+									\
+	_el = _eventhandler_list_ ## name ;				\
+	if (!TAILQ_EMPTY(&_el->el_entries)) {				\
+		EHL_LOCK(_el);						\
+		_EVENTHANDLER_INVOKE(name, _el , ## __VA_ARGS__);	\
+	}								\
+} while (0)
+
+/*
+ * Event handlers need to be declared, but do not need to be defined. The
  * declaration must be in scope wherever the handler is to be invoked.
  */
 #define EVENTHANDLER_DECLARE(name, type)				\
@@ -158,6 +186,7 @@ void	eventhandler_deregister_nowait(struct eventhandle
 	    eventhandler_tag tag);
 struct eventhandler_list *eventhandler_find_list(const char *name);
 void	eventhandler_prune_list(struct eventhandler_list *list);
+struct eventhandler_list *eventhandler_create_list(const char *name);
 
 #ifdef VIMAGE
 typedef	void (*vimage_iterator_func_t)(void *, ...);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201803290441.w2T4fkTs033056>