Index: head/share/man/man9/EVENTHANDLER.9 =================================================================== --- head/share/man/man9/EVENTHANDLER.9 +++ head/share/man/man9/EVENTHANDLER.9 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" $FreeBSD$ .\" -.Dd March 27, 2017 +.Dd October 1, 2017 .Dt EVENTHANDLER 9 .Os .Sh NAME @@ -37,6 +37,7 @@ .Ft eventhandler_tag .Fn EVENTHANDLER_REGISTER name func arg priority .Fn EVENTHANDLER_DEREGISTER name tag +.Fn EVENTHANDLER_DEREGISTER_NOWAIT name tag .Ft eventhandler_tag .Fo eventhandler_register .Fa "struct eventhandler_list *list" @@ -50,6 +51,11 @@ .Fa "struct eventhandler_list *list" .Fa "eventhandler_tag tag" .Fc +.Ft void +.Fo eventhandler_deregister_nowait +.Fa "struct eventhandler_list *list" +.Fa "eventhandler_tag tag" +.Fc .Ft "struct eventhandler_list *" .Fn eventhandler_find_list "const char *name" .Ft void @@ -121,6 +127,18 @@ .Fa tag from the event handler named by argument .Fa name . +It waits until no threads are running handlers for this event before +returning, making it safe to unload a module immediately upon return +from this function. +.It Fn EVENTHANDLER_DEREGISTER_NOWAIT +This macro removes a previously registered callback associated with tag +.Fa tag +from the event handler named by argument +.Fa name . +Upon return, one or more threads could still be running the removed +function(s), but no new calls will be made. +To remove a handler function from within that function, use this +version of deregister, to avoid a deadlock. .It Fn EVENTHANDLER_INVOKE This macro is used to invoke all the callbacks associated with event handler @@ -176,6 +194,21 @@ .Fn eventhandler_deregister to remove the particular callback function. .It Fn eventhandler_deregister +The +.Fn eventhandler_deregister +function removes the callback associated with tag +.Fa tag +from the event handler list pointed to by +.Fa list . +If +.Fa tag +is +.Va NULL , +all callback functions for the event are removed. +This function will not return until all threads have exited from the +removed handler callback function(s). +This function is not safe to call from inside an event handler callback. +.It Fn eventhandler_deregister_nowait The .Fn eventhandler_deregister function removes the callback associated with tag Index: head/sys/kern/subr_eventhandler.c =================================================================== --- head/sys/kern/subr_eventhandler.c +++ head/sys/kern/subr_eventhandler.c @@ -180,8 +180,9 @@ } #endif -void -eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag) +static void +_eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag, + bool wait) { struct eventhandler_entry *ep = tag; @@ -215,9 +216,24 @@ ep->ee_priority = EHE_DEAD_PRIORITY; } } - while (list->el_runcount > 0) + while (wait && list->el_runcount > 0) mtx_sleep(list, &list->el_lock, 0, "evhrm", 0); EHL_UNLOCK(list); +} + +void +eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag) +{ + + _eventhandler_deregister(list, tag, true); +} + +void +eventhandler_deregister_nowait(struct eventhandler_list *list, + eventhandler_tag tag) +{ + + _eventhandler_deregister(list, tag, false); } /* Index: head/sys/sys/eventhandler.h =================================================================== --- head/sys/sys/eventhandler.h +++ head/sys/sys/eventhandler.h @@ -141,11 +141,20 @@ if ((_el = eventhandler_find_list(#name)) != NULL) \ eventhandler_deregister(_el, tag); \ } while(0) - +#define EVENTHANDLER_DEREGISTER_NOWAIT(name, tag) \ +do { \ + struct eventhandler_list *_el; \ + \ + if ((_el = eventhandler_find_list(#name)) != NULL) \ + eventhandler_deregister_nowait(_el, tag); \ +} while(0) + eventhandler_tag eventhandler_register(struct eventhandler_list *list, const char *name, void *func, void *arg, int priority); void eventhandler_deregister(struct eventhandler_list *list, + eventhandler_tag tag); +void eventhandler_deregister_nowait(struct eventhandler_list *list, eventhandler_tag tag); struct eventhandler_list *eventhandler_find_list(const char *name); void eventhandler_prune_list(struct eventhandler_list *list);