Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -1641,6 +1641,7 @@ timeout.9 callout_active.9 \ timeout.9 callout_deactivate.9 \ timeout.9 callout_drain.9 \ + timeout.9 callout_drain_async.9 \ timeout.9 callout_handle_init.9 \ timeout.9 callout_init.9 \ timeout.9 callout_init_mtx.9 \ Index: share/man/man9/timeout.9 =================================================================== --- share/man/man9/timeout.9 +++ share/man/man9/timeout.9 @@ -36,6 +36,7 @@ .Nm callout_active , .Nm callout_deactivate , .Nm callout_drain , +.Nm callout_drain_async , .Nm callout_handle_init , .Nm callout_init , .Nm callout_init_mtx , @@ -70,6 +71,8 @@ .Fn callout_deactivate "struct callout *c" .Ft int .Fn callout_drain "struct callout *c" +.Ft int +.Fn callout_drain_async "struct callout *c" "callout_func_t *fn" "void *arg" .Ft void .Fn callout_handle_init "struct callout_handle *handle" .Bd -literal @@ -264,6 +267,24 @@ .Fn callout_drain returns. .Pp +The function +.Fn callout_drain_async +is non-blocking and works the same as the +.Fn callout_stop +function. +When this function returns non-zero, do not call it again until the callback function given by +.Fa fn +has been called with argument +.Fa arg . +Only one of +.Fn callout_drain +or +.Fn callout_drain_async +should be called at a time to drain a callout. +If this function returns zero, it is safe to free the callout structure pointed to by the +.Fa c +argument immediately. +.Pp The .Fn callout_reset and Index: sys/kern/kern_timeout.c =================================================================== --- sys/kern/kern_timeout.c +++ sys/kern/kern_timeout.c @@ -1145,6 +1145,45 @@ } int +callout_drain_async(struct callout *c, callout_func_t *func, void *arg) +{ + struct callout_cpu *cc; + struct lock_class *class; + int retval; + int direct; + + /* stop callout */ + callout_stop(c); + + /* check if callback is being called */ + cc = callout_lock(c); + if (c->c_iflags & CALLOUT_DIRECT) { + direct = 1; + } else { + direct = 0; + } + retval = (cc_exec_curr(cc, direct) == c); + + /* drop locks, if any */ + if (retval && c->c_lock != NULL && + c->c_lock != &Giant.lock_object) { + /* ensure we are properly locked */ + class = LOCK_CLASS(c->c_lock); + class->lc_assert(c->c_lock, LA_XLOCKED); + /* the final callback should not be called locked */ + c->c_lock = NULL; + c->c_iflags |= CALLOUT_RETURNUNLOCKED; + } + CC_UNLOCK(cc); + + /* check if we should queue final callback */ + if (retval) + callout_reset(c, 1, func, arg); + + return (retval); +} + +int _callout_stop_safe(struct callout *c, int safe) { struct callout_cpu *cc, *old_cc; Index: sys/sys/_callout.h =================================================================== --- sys/sys/_callout.h +++ sys/sys/_callout.h @@ -46,6 +46,8 @@ SLIST_HEAD(callout_slist, callout); TAILQ_HEAD(callout_tailq, callout); +typedef void callout_func_t(void *); + struct callout { union { LIST_ENTRY(callout) le; Index: sys/sys/callout.h =================================================================== --- sys/sys/callout.h +++ sys/sys/callout.h @@ -82,6 +82,7 @@ #define callout_active(c) ((c)->c_flags & CALLOUT_ACTIVE) #define callout_deactivate(c) ((c)->c_flags &= ~CALLOUT_ACTIVE) #define callout_drain(c) _callout_stop_safe(c, 1) +int callout_drain_async(struct callout *, callout_func_t *, void *); void callout_init(struct callout *, int); void _callout_init_lock(struct callout *, struct lock_object *, int); #define callout_init_mtx(c, mtx, flags) \