aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWyatt Hepler <hepler@google.com>2023-01-26 22:06:43 +0000
committerCQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-01-26 22:06:43 +0000
commit98578de84dcbf93825a9675fe8c92cadb1101177 (patch)
treefa1d043a6ed6ada4d843120ac47edf33f5f789c4
parentb817ca759458db976ae956c35605c33d72d9cc22 (diff)
downloadpigweed-98578de84dcbf93825a9675fe8c92cadb1101177.tar.gz
pw_rpc: Update docs for RPC callback restrictions
Add docs for upcoming PW_RPC_CALLBACK_TIMEOUT_TICKS configuration option. Change-Id: If3de01d41af06c59a53f9f52cd2d56f58da2d4b6 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/126917 Reviewed-by: Alexei Frolov <frolv@google.com> Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com> Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
-rw-r--r--pw_rpc/docs.rst86
1 files changed, 66 insertions, 20 deletions
diff --git a/pw_rpc/docs.rst b/pw_rpc/docs.rst
index 85fa97a58..3e8bf6b1f 100644
--- a/pw_rpc/docs.rst
+++ b/pw_rpc/docs.rst
@@ -1099,27 +1099,55 @@ RPC callbacks are free to perform most actions, including invoking new RPCs or
cancelling pending calls. However, the C++ implementation imposes some
limitations and restrictions that must be observed.
+Destructors & moves wait for callbacks to complete
+...................................................
* Callbacks must not destroy their call object. Attempting to do so will result
- in deadlock. Another thread may destroy a call while its callback is running,
- but that thread will block until all callbacks complete.
-* The ``on_next`` callback has additional restrictions:
-
- * The ``on_next`` callback must not move its call object if it the call is
- still active. It may move its call object after it is terminated first with
- ``Finish()`` or ``Cancel()``. The ``on_next`` callback may move a different
- callback into its call object, since moving closes the destination call.
- * Other threads may move a call object while its ``on_next`` callback is
- running, but they will block until the callback completes.
- * Only one thread may execute the ``on_next`` callback for a specific service
- method at a time. If a second thread calls ``ProcessPacket()`` with a stream
- packet before the ``on_next`` callback for the previous packet completes,
- the second packet will be dropped. The RPC endpoint logs a warning when this
- occurs.
-
-.. important::
-
- C++ RPC callbacks must respect the above `limitations and restrictions`_!
- Otherwise, deadlocks may occur.
+ in deadlock.
+* Other threads may destroy a call while its callback is running, but that
+ thread will block until all callbacks complete.
+* Callbacks must not move their call object if it the call is still active. They
+ may move their call object after it has terminated. Callbacks may move a
+ different call into their call object, since moving closes the destination
+ call.
+* Other threads may move a call object while it has a callback running, but they
+ will block until the callback completes if the call is still active.
+
+.. warning::
+
+ Deadlocks or crashes occur if a callback:
+
+ - attempts to destroy its call object
+ - attempts to move its call object while the call is still active
+ - never returns
+
+If ``pw_rpc`` a callback violates these restrictions, a crash may occur,
+depending on the value of :c:macro:`PW_RPC_CALLBACK_TIMEOUT_TICKS`. These
+crashes have a message like the following:
+
+.. code-block:: text
+
+ A callback for RPC 1:cc0f6de0/31e616ce has not finished after 10000 ticks.
+ This may indicate that an RPC callback attempted to destroy or move its own
+ call object, which is not permitted. Fix this condition or change the value of
+ PW_RPC_CALLBACK_TIMEOUT_TICKS to avoid this crash.
+
+ See https://pigweed.dev/pw_rpc#destructors-moves-wait-for-callbacks-to-complete
+ for details.
+
+Only one thread at a time may execute ``on_next``
+.................................................
+Only one thread may execute the ``on_next`` callback for a specific service
+method at a time. If a second thread calls ``ProcessPacket()`` with a stream
+packet before the ``on_next`` callback for the previous packet completes, the
+second packet will be dropped. The RPC endpoint logs a warning when this occurs.
+
+Example warning for a dropped stream packet:
+
+.. code-block:: text
+
+ WRN Received stream packet for 1:cc0f6de0/31e616ce before the callback for
+ a previous packet completed! This packet will be dropped. This can be
+ avoided by handling packets for a particular RPC on only one thread.
RPC calls introspection
=======================
@@ -1548,6 +1576,24 @@ more details.
platforms, :cpp:func:`pw::this_thread::yield` does not yield to lower
priority tasks and should not be used here.
+.. c:macro:: PW_RPC_CALLBACK_TIMEOUT_TICKS
+
+ pw_rpc call objects wait for their callbacks to complete before they are moved
+ or destoyed. Deadlocks occur if a callback:
+
+ - attempts to destroy its call object,
+ - attempts to move its call object while the call is still active, or
+ - never returns.
+
+ If ``PW_RPC_CALLBACK_TIMEOUT_TICKS`` is greater than 0, then ``PW_CRASH`` is
+ invoked if a thread waits for an RPC callback to complete for more than the
+ specified tick count.
+
+ A "tick" in this context is one iteration of a loop that yields releases the
+ RPC lock and yields the thread according to :c:macro:`PW_RPC_YIELD_MODE`. By
+ default, the thread yields with a 1-tick call to
+ :cpp:func:`pw::this_thread::sleep_for`.
+
.. c:macro:: PW_RPC_DYNAMIC_ALLOCATION
Whether pw_rpc should use dynamic memory allocation internally. If enabled,