DELAYED_TASK(9) Kernel Developer's Manual DELAYED_TASK(9)

NAME

delayed_taskasynchronous actions scheduled after a delay

SYNOPSIS

#include <sys/task.h>

void
delayed_task_init(struct delayed_task *dt, void (*fn)(struct delayed_task *));

void
delayed_task_destroy(struct delayed_task *dt);

void
delayed_task_schedule(struct delayed_task *dt, nsec_t nsec, jitter_t jitter);

bool
delayed_task_reschedule(struct delayed_task *dt, nsec_t nsec, jitter_t jitter);

bool
delayed_task_timedout(struct delayed_task *dt);

void
delayed_task_done(struct delayed_task *dt);

bool
delayed_task_cancel(struct delayed_task *dt, kmutex_t *interlock);

bool
delayed_task_cancel_async(struct delayed_task *dt);

DESCRIPTION

The delayed_task abstraction is provided to schedule actions to be run asynchronously after a delay.

Delayed tasks are similar to task(9) tasks, about which see notes there for rules on what the actions may do.

Task state is stored in the struct delayed_task structure. Callers of the delayed_task abstraction must allocate memory for struct delayed_task objects, but should consider them opaque, and should not inspect or copy them. Each delayed task represented by a struct delayed_task object will be run only once at a time, until the action associated with it returns or calls delayed_task_done() to signal that it may be reused.

FUNCTIONS

delayed_task_init(dt, fn)
Initialize the delayed task structure task, whose memory must be allocated by the caller, with the action fn. To change the action of a delayed task, you must use delayed_task_destroy() first and then call delayed_task_init() again.

delayed_task_init() may be used in any context, including hard interrupt context.

delayed_task_destroy(dt)
Destroy dt, which may not be used again unless reinitialized with delayed_task_init. dt must not be scheduled to run. If it may still be scheduled, you can use delayed_task_cancel() to cancel it. However, delayed_task_cancel_async() is not enough.

delayed_task_destroy() may be used in any context, including hard interrupt context.

delayed_task_schedule(dt, nsec, jitter)
Schedule task to run after nsec nanoseconds with the specified jitter. If dt is already scheduled to run, this has no effect. If dt has already begun to run but has not yet completed, this schedules it to run again as soon as possible after it completes. To change the time at which dt is scheduled to run, use delayed_task_reschedule() instead.

delayed_task_schedule() may be used in any context, including hard interrupt context, except at interrupt priority levels above IPL_VM.

delayed_task_reschedule(dt, nsec, jitter)
If dt is scheduled but has not yet run, then reschedule it to run after nsec nanoseconds with the specified jitter, and return true. Otherwise, do nothing and return false.

delayed_task_reschedule() may be used in any context, including hard interrupt context.

delayed_task_timedout(dt)
If dt was run after being scheduled with nonzero delay, return true. Otherwise, return false. This allows you to use the same delayed task to handle a response or time out, by calling delayed_task_schedule() with a timeout when a request is submitted, delayed_task_reschedule() with a zero delay in an interrupt handler when a response arrives, and delayed_task_timeout() to discriminate between the two cases.

delayed_task_timedout() may be called only by a delayed task's action.

delayed_task_done(dt)
Mark dt as done so that it can be scheduled again. After this, you may destroy dt and free the memory containing it. If you do not call delayed_task_done(), the thread running dt will continue to use it after the action returns.

delayed_task_done() may be called only by a delayed task's action.

delayed_task_cancel(dt, interlock)
Try to cancel dt. If it is scheduled and successfully cancelled, return true. If it is not scheduled, or if it has already begun to run, return false. If dt has already begun to run, delayed_task_cancel() will release interlock, wait for the delayed task's action either to return or to call delayed_task_done(), and then re-acquire interlock. May sleep.

The interlock is provided so that if the delayed task's action needs it and the caller of delayed_task_cancel() holds it, then delayed_task_cancel() can release the interlock after acquiring locks internal to the delayed_task abstraction in order to avoid racing or deadlocking with the delayed task's action. You should always assume that interlock will be released and re-acquired, and recheck any invariants you rely on it to preserve.

delayed_task_cancel_async(dt)
Try to cancel dt like delayed_task_cancel(), but if it has already begun to run, then return immediately instead of waiting for it to complete.

task_cancel_async() may be used in any context, including hard interrupt context.

EXAMPLES

The following code illustrates a driver that issues requests with timeouts to a device and processes either the response or the timeout in the same task.

struct mydev_softc { 
	... 
	kmutex_t		sc_lock; 
	struct mydev_req	*sc_req; 
	struct delayed_task	sc_timeout_task; 
	... 
}; 
 
static int 
mydev_submit_request(struct mydev_softc *sc, struct mydev_req *req) 
{ 
	int error; 
 
	mutex_enter(&sc->sc_lock); 
	if (sc->sc_req != NULL) { 
		error = EBUSY; 
	} else { 
		...write hardware registers to submit request... 
		sc->sc_req = req; 
		delayed_task_schedule(&sc->sc_timeout_task, mstons(1000), 
		    mstons(10)); 
		error = 0; 
	} 
	mutex_exit(&sc->sc_lock); 
 
	return error; 
} 
 
static void 
mydev_intr(void *arg) 
{ 
	struct mydev_softc *sc = arg; 
 
	mutex_enter(&sc->sc_lock); 
	if (!delayed_task_reschedule(&sc->sc_timeout_task, 0, 0)) { 
		/* Too late -- timed out already.  */ 
		...write hardware registers to rescind the request... 
	} 
	mutex_exit(&sc->sc_lock); 
} 
 
static void 
mydev_timeout_task(struct delayed_task *dt) 
{ 
	struct mydev_softc *sc = container_of(dt, struct mydev_softc, 
	    sc_timeout_task); 
 
	mutex_enter(&sc->sc_lock); 
	if (delayed_task_timedout(dt)) { 
		mydev_req_timeout(sc->sc_req); 
	} else { 
		...read/write hardware registers to get/ack the response... 
		mydev_req_response(sc->sc_req, response); 
	} 
	mutex_exit(&sc->sc_lock); 
}

CODE REFERENCES

The delayed_task abstraction is implemented in sys/kern/kern_task.c.

SEE ALSO

callout(9), softint(9), task(9), taskqueue(9), workqueue(9)
March 27, 2014 NetBSD 6.1_RC3