message: Improve the documentation for synchronous and asynchronous OpKernels.
diff: @@ -99,10 +99,18 @@ class OpKernel {
  // subclass OpKernel and override the Compute() method and have it
  // return after completing the supplied work.
  //
  - // A few special kernels might need to be asynchronous to bound the
  - // number of threads (e.g., network receive operations). These
  - // kernels must subclass AsyncOpKernel and override
  - // AsyncOpKernel::ComputeAsync().
  + // A synchronous OpKernel *MUST NOT* block the calling thread on a
  + // synchronization mechanism (condition variable, Notification, etc.) that
  + // will be unblocked by the execution of another OpKernel. Execution may
  + // deadlock in that case, because the executor may use a bounded number of
  + // threads.
  + //
  + // If an OpKernel must block on the execution of another OpKernel (e.g. a
  + // RecvOp, or a DequeueOp), the implementation *MUST* subclass AsyncOpKernel,
  + // and override `AsyncOpKernel::ComputeAsync()`. In addition, because the
  + // unblocking kernel may never run (due to an error or cancellation), in most
  + // cases the AsyncOpKernel should implement cancellation support via
  + // `ctx->cancellation_manager()`.
  //
  // In both cases, implementations of Compute() and ComputeAsync()
  // get inputs and write outputs through the given OpKernelContext
  @@ -221,9 +229,19 @@ class AsyncOpKernel : public OpKernel {
  // Asynchronous compute.
  //
  - // Implementations of ComputeAsync() must run "done" to signal the
  - // completion of the computation. "context" is guaranteed to be
  - // alive until the "done" callback starts.
  + // Implementations of ComputeAsync() must ensure that `done` is (eventually)
  + // called exactly once to signal the completion of the computation. The
  + // implementation of ComputeAsync() must not block on the execution of another
  + // OpKernel. `done` may be called by the current thread, or by another thread
  + // `context` is guaranteed to stay alive until the `done` callback starts.
  + //
  + // Since it is possible that the unblocking kernel may never run (due to an
  + // error or cancellation), in most cases the AsyncOpKernel should implement
  + // cancellation support via `ctx->cancellation_manager()`.
  + //
  + // WARNING: As soon as the `done` callback starts, `context` and `this` may be
  + // deleted. No code depending on these objects should execute after the call
  + // to `done`.
  typedef std::function<void()> DoneCallback;
  virtual void ComputeAsync(OpKernelContext* context, DoneCallback done) = 0;
  