Restore the ability to deregister an eventhandler from within the callback.

When the EVENTHANDLER(9) subsystem was created, it was a documented feature
that an eventhandler callback function could safely deregister itself. In
r200652 that feature was inadvertantly broken by adding drain-wait logic to
eventhandler_deregister(), so that it would be safe to unload a module upon
return from deregistering its event handlers.

There are now 145 callers of EVENTHANDLER_DEREGISTER(), and it's likely many
of them are depending on the drain-wait logic that has been in place for 8
years. So instead of creating a separate eventhandler_drain() and adding it
to some or all of those 145 call sites, this creates a separate
eventhandler_drain_nowait() function for the specific purpose of
deregistering a callback from within the running callback.

Differential Revision:	https://reviews.freebsd.org/D12561
This commit is contained in:
Ian Lepore 2017-10-08 17:21:16 +00:00
parent 37244a84fd
commit fc09164658
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324413
3 changed files with 63 additions and 5 deletions

View File

@ -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 @@ This macro removes a previously registered callback associated with tag
.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
@ -182,6 +200,21 @@ 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
.Fa tag
from the event handler list pointed to by
.Fa list .
This function is safe to call from inside an event handler
callback.
.It Fn eventhandler_find_list

View File

@ -180,8 +180,9 @@ vimage_eventhandler_register(struct eventhandler_list *list, const char *name,
}
#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,11 +216,26 @@ eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
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);
}
/*
* Internal version for use when eventhandler list is already locked.
*/

View File

@ -141,12 +141,21 @@ do { \
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);