File:  [NetBSD Developer Wiki] / wikisrc / projects / project / kernel_continuations.mdwn
Revision 1.3: download - view: text, annotated - select for diffs
Mon Feb 16 06:46:30 2015 UTC (5 years, 1 month ago) by dholland
Branches: MAIN
CVS tags: HEAD
Clarify that these "continuations" are not continuations; they're

I'm aware that there's a body of practice that uses the term
"continuations" for these constructions, presumably because they can
be used (if used in a certain stylized way) to manually and painfully
implement what you might otherwise do with callcc. But in conventional
usage they aren't continuations or anything like continuations.

Note that I'm not going so far as to change the name or anything
(although I'd suggest that in the long run), just adding a

If anyone wants to argue this you know where to find me :-)

    1: [[!template id=project
    3: title="Kernel continuations"
    5: contact="""
    6: [tech-kern](,
    7: [board](,
    8: [core](
    9: """
   11: category="kernel"
   12: difficulty="hard"
   13: funded="The NetBSD Foundation"
   15: description="""
   16: This project proposal is a subtask of [[smp_networking]] and is elegible
   17: for funding independently.
   19: The goal of this project is to implement continuations at the kernel level.
   20: Most of the pieces are already available in the kernel, so this can be
   21: reworded as: combine *callouts*, *softints*, and *workqueues* into a single
   22: framework.  Continuations are meant to be cheap; very cheap.
   24: These continuations are a dispatching system for making callbacks at
   25: scheduled times or in different thread/interrupt contexts.
   26: They aren't "continuations" in the usual sense such as you might find
   27: in Scheme code.
   29: Please note that the main goal of this project is to simplify the
   30: implementation of [[SMP networking|smp_networking]], so care must be taken
   31: in the design of the interface to support all the features required for
   32: this other project.
   34: The proposed interface looks like the following.  This interface is mostly
   35: derived from the `callout(9)` API and is a superset of the softint(9) API.
   36: The most significant change is that workqueue items are not tied to a
   37: specific kernel thread.
   39: * `kcont_t *kcont_create(kcont_wq_t *wq, kmutex_t *lock, void
   40:   (*func)(void *, kcont_t *), void *arg, int flags);`
   42:   A `wq` must be supplied.  It may be one returned by
   43:   `kcont_workqueue_acquire` or a predefined workqueue such as (sorted from
   44:   highest priority to lowest):
   46:   * `wq_softserial`, `wq_softnet`, `wq_softbio`, `wq_softclock`
   47:   * `wq_prihigh`, `wq_primedhigh`, `wq_primedlow`, `wq_prilow`
   49:   `lock`, if non-NULL, should be locked before calling `func(arg)` and
   50:   released afterwards.  However, if the lock is released and/or destroyed
   51:   before the called function returns, then, before returning,
   52:   `kcont_set_mutex` must be called with either a new mutex to be released
   53:   or `NULL`.  If acquiring lock would block, other pending kernel
   54:   continuations which depend on other locks may be dispatched in the
   55:   meantime.  However, all continuations sharing the same set of `{ wq, lock,
   56:   [ci] }` need to be processed in the order they were scheduled.
   58:   `flags` must be 0.  This field is just provided for extensibility.
   60: * `int kcont_schedule(kcont_t *kc, struct cpu_info *ci, int nticks);`
   62:   If the continuation is marked as *INVOKING*, an error of `EBUSY` should
   63:   be returned.  If `nticks` is 0, the continuation is marked as *INVOKING*
   64:   while *EXPIRED* and *PENDING* are cleared, and the continuation is
   65:   scheduled to be invoked without delay.  Otherwise, the continuation is
   66:   marked as *PENDING* while *EXPIRED* status is cleared, and the timer
   67:   reset to `nticks`.  Once the timer expires, the continuation is marked as
   68:   *EXPIRED* and *INVOKING*, and the *PENDING* status is cleared.  If `ci`
   69:   is non-NULL, the continuation is invoked on the specified CPU if the
   70:   continuations's workqueue has per-cpu queues.  If that workqueue does not
   71:   provide per-cpu queues, an error of `ENOENT` is returned.  Otherwise when
   72:   `ci` is `NULL`, the continuation is invoked on either the current CPU or
   73:   the next available CPU depending on whether the continuation's workqueue
   74:   has per-cpu queues or not, respectively.
   76: * `void kcont_destroy(kcont_t *kc);`
   78: * `kmutex_t *kcont_getmutex(kcont_t *kc);`
   80:   Returns the lock currently associated with the continuation `kc`.
   82: * `void kcont_setarg(kcont_t *kc, void *arg);`
   84:   Updates `arg` in the continuation `kc`.  If no lock is associated with
   85:   the continuation, then `arg` may be changed at any time; however, if the
   86:   continuation is being invoked, it may not pick up the change.  Otherwise,
   87:   `kcont_setarg` must only be called when the associated lock is locked.
   89: * `kmutex_t *kcont_setmutex(kcont_t *kc, kmutex_t *lock);`
   91:   Updates the lock associated with the continuation `kc` and returns the
   92:   previous lock.  If no lock is currently associated with the continuation,
   93:   then calling this function with a lock other than NULL will trigger an
   94:   assertion failure.  Otherwise, `kcont_setmutex` must be called only when
   95:   the existing lock (which will be replaced) is locked.  If
   96:   `kcont_setmutex` is called as a result of the invokation of func, then
   97:   after kcont_setmutex has been called but before func returns, the
   98:   replaced lock must have been released, and the replacement lock, if
   99:   non-NULL, must be locked upon return.
  101: * `void kcont_setfunc(kcont_t *kc, void (*func)(void *), void *arg);`
  103:   Updates `func` and `arg` in the continuation `kc`.  If no lock is
  104:   associated with the continuation, then only arg may be changed.
  105:   Otherwise, `kcont_setfunc` must be called only when the associated lock
  106:   is locked.
  108: * `bool kcont_stop(kcont_t *kc);`
  110:   The `kcont_stop function` stops the timer associated the continuation
  111:   handle kc.  The *PENDING* and *EXPIRED* status for the continuation
  112:   handle is cleared.  It is safe to call `kcont_stop` on a continuation
  113:   handle that is not pending, so long as it is initialized.  `kcont_stop`
  114:   will return a non-zero value if the continuation was *EXPIRED*.
  116: * `bool kcont_pending(kcont_t *kc);`
  118:   The `kcont_pending` function tests the *PENDING* status of the
  119:   continuation handle `kc`.  A *PENDING* continuation is one who's timer
  120:   has been started and has not expired.  Note that it is possible for a
  121:   continuation's timer to have expired without being invoked if the
  122:   continuation's lock could not be acquired or there are higher priority
  123:   threads preventing its invokation.  Note that it is only safe to test
  124:   *PENDING* status when holding the continuation's lock.
  126: * `bool kcont_expired(kcont_t *kc);`
  128:   Tests to see if the continuation's function has been invoked since the
  129:   last `kcont_schedule`.
  131: * `bool kcont_active(kcont_t *kc);`
  133: * `bool kcont_invoking(kcont_t *kc);`
  135:   Tests the *INVOKING* status of the handle `kc`.  This flag is set just
  136:   before a continuation's function is being called.  Since the scheduling
  137:   of the worker threads may induce delays, other pending higher-priority
  138:   code may run before the continuation function is allowed to run.  This
  139:   may create a race condition if this higher-priority code deallocates
  140:   storage containing one or more continuation structures whose continuation
  141:   functions are about to be run.  In such cases, one technique to prevent
  142:   references to deallocated storage would be to test whether any
  143:   continuation functions are in the *INVOKING* state using
  144:   `kcont_invoking`, and if so, to mark the data structure and defer storage
  145:   deallocation until the continuation function is allowed to run.  For this
  146:   handshake protocol to work, the continuation function will have to use
  147:   the `kcont_ack` function to clear this flag.
  149: * `bool kcont_ack(kcont_t *kc);`
  151:   Clears the *INVOKING* state in the continuation handle `kc`.  This is
  152:   used in situations where it is necessary to protect against the race
  153:   condition described under `kcont_invoking`.
  155: * `kcont_wq_t *kcont_workqueue_acquire(pri_t pri, int flags);`
  157:   Returns a workqueue that matches the specified criteria.  Thus if
  158:   multiple requesters ask for the same criteria, they are all returned the
  159:   same workqueue.  `pri` specifies the priority at which the kernel thread
  160:   which empties the workqueue should run.
  162:   If `flags` is 0 then the standard operation is required.  However, the
  163:   following flag(s) may be bitwise ORed together:
  165:   * `WQ_PERCPU` specifies that the workqueue should have a separate queue
  166:     for each CPU, thus allowing continuations to be invoked on specific CPUs.
  168: * `int kcont_workqueue_release(kcont_wq_t *wq);`
  170:   Releases an acquired workqueue.  On the last release, the workqueue's
  171:   resources are freed and the workqueue is destroyed.
  172: """
  173: ]]
  175: [[!tag smp_networking]]

CVSweb for NetBSD wikisrc <> software: FreeBSD-CVSweb