tractor/tests
Tyler Goodlet 408a74784e Catch `.pause_from_sync()` in root bg thread bugs!
Originally discovered as while using `tractor.pause_from_sync()`
from the `i3ipc` client running in a bg-thread that uses `asyncio`
inside `modden`.

Turns out we definitely aren't correctly handling `.pause_from_sync()`
from the root actor when called from a `trio.to_thread.run_sync()`
bg thread:
- root-actor bg threads which can't `Lock._debug_lock.acquire()` since
  they aren't in `trio.Task`s.
- even if scheduled via `.to_thread.run_sync(_debug._pause)` the
  acquirer won't be the task/thread which calls `Lock.release()` from
  `PdbREPL` hooks; this results in a RTE raised by `trio`..
- multiple threads will step on each other's stdio since cpython's GIL
  seems to ctx switch threads on every input from the user to the REPL
  loop..

Reproduce via reworking our example and test so that they catch and fail
for all edge cases:
- rework the `/examples/debugging/sync_bp.py` example to demonstrate the
  above issues, namely the stdio clobbering in the REPL when multiple
  threads and/or a subactor try to debug simultaneously.
  |_ run one thread using a task nursery to ensure it runs conc with the
     nursery's parent task.
  |_ ensure the bg threads run conc a subactor usage of
     `.pause_from_sync()`.
  |_ gravely detail all the special cases inside a TODO comment.
  |_ add some control flags to `sync_pause()` helper and don't use
     `breakpoint()` by default.
- extend and adjust `test_debugger.test_pause_from_sync` to match (and
  thus currently fail) by ensuring exclusive `PdbREPL` attachment when
  the 2 bg root-actor threads are concurrently interacting alongside the
  subactor:
  |_ should only see one of the `_pause_msg` logs at a time for either
     one of the threads or the subactor.
  |_ ensure each attaches (in no particular order) before expecting the
     script to exit.

Impl adjustments to `.devx._debug`:
- drop `Lock.repl`, no longer used.
- add `Lock._owned_by_root: bool` for the `.ctx_in_debug == None`
  root-actor-task active case.
- always `log.exception()` for any `._debug_lock.release()` ownership
  RTE emitted by `trio`, like we used to..
- add special `Lock.release()` log message for the stale lock but
  `._owned_by_root == True` case; oh yeah and actually
  `log.devx(message)`..
- rename `Lock.acquire()` -> `.acquire_for_ctx()` since it's only ever
  used from subactor IPC usage; well that and for local root-task
  usage we should prolly add a `.acquire_from_root_task()`?
- buncha `._pause()` impl improvements:
 |_ type `._pause()`'s `debug_func` as a `partial` as well.
 |_ offer `called_from_sync: bool` and `called_from_bg_thread: bool`
    for the special case handling when called from `.pause_from_sync()`
 |_ only set `DebugStatus.repl/repl_task` when `debug_func != None`
   (OW ensure the `.repl_task` is not the current one).
 |_ handle error logging even when `debug_func is None`..
 |_ lotsa detailed commentary around root-actor-bg-thread special cases.
- when `._set_trace(hide_tb=False)` do `pdbp.set_trace(frame=currentframe())`
  so the `._debug` internal frames are always included.
- by default always hide tracebacks for `.pause[_from_sync]()` internals.
- improve `.pause_from_sync()` to avoid root-bg-thread crashes:
 |_ pass new `called_from_xxx_` flags and ensure `DebugStatus.repl_task`
    is actually set to the `threading.current_thread()` when needed.
 |_ manually call `Lock._debug_lock.acquire_nowait()` for the non-bg
    thread case.
 |_ TODO: still need to implement the bg-thread case using a bg
    `trio.Task`-in-thread with an `trio.Event` set by thread REPL exit.
2024-06-06 16:56:30 -04:00
..
conftest.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_2way.py Move context-streaming operational tests into one mod 2021-12-06 16:45:44 -05:00
test_advanced_faults.py Update tests for `PldRx` and `Context` changes 2024-05-09 16:48:53 -04:00
test_advanced_streaming.py Tweak some tests for spurious failues 2024-03-11 10:37:34 -04:00
test_cancellation.py Update tests for `PldRx` and `Context` changes 2024-05-09 16:48:53 -04:00
test_caps_based_msging.py More msg-spec tests tidying 2024-04-14 19:50:09 -04:00
test_child_manages_service_nursery.py Adjust all `RemoteActorError.type` using tests 2024-03-19 18:08:54 -04:00
test_clustering.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_context_stream_semantics.py Add a "current IPC `Context`" `ContextVar` 2024-05-07 15:35:45 -04:00
test_debugger.py Catch `.pause_from_sync()` in root bg thread bugs! 2024-06-06 16:56:30 -04:00
test_discovery.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_docs_examples.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_infected_asyncio.py Update tests for `PldRx` and `Context` changes 2024-05-09 16:48:53 -04:00
test_inter_peer_cancellation.py Update tests for `PldRx` and `Context` changes 2024-05-09 16:48:53 -04:00
test_legacy_one_way_streaming.py Flip a last `MultiError` to a beg, add todo on `@stream` func 2024-04-14 19:39:57 -04:00
test_local.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_multi_program.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_pldrx_limiting.py Woops, set `post_mortem=False` by default again! 2024-05-30 18:33:25 -04:00
test_pubsub.py Add (back) a `tractor._testing` sub-pkg 2024-03-13 09:09:08 -04:00
test_resource_cache.py Bump timeout on resource cache test a bitty bit. 2024-01-03 22:27:05 -05:00
test_rpc.py Adjust all `RemoteActorError.type` using tests 2024-03-19 18:08:54 -04:00
test_runtime.py Drop now-deprecated deps on modern `trio`/Python 2024-03-13 18:41:24 -04:00
test_shm.py Pass `str` dtype for `use_str` case 2023-06-15 12:20:20 -04:00
test_spawning.py Update tests for `PldRx` and `Context` changes 2024-05-09 16:48:53 -04:00
test_task_broadcasting.py Port all tests to new `reg_addr` fixture name 2023-10-18 15:39:20 -04:00
test_trioisms.py Drop now-deprecated deps on modern `trio`/Python 2024-03-13 18:41:24 -04:00