diff --git a/docs/source/reference-core/blocking-trio-portal-example.py b/docs/source/reference-core/blocking-trio-portal-example.py new file mode 100644 index 0000000000..998fec9bd2 --- /dev/null +++ b/docs/source/reference-core/blocking-trio-portal-example.py @@ -0,0 +1,44 @@ +import trio +import threading + +def thread_fn(portal, receive_from_trio, send_to_trio): + while True: + # Since we're in a thread, we can't call methods on Trio + # objects directly -- so we use our portal to call them. + try: + request = portal.run(receive_from_trio.receive) + except trio.EndOfChannel: + portal.run(send_to_trio.aclose) + return + else: + response = request + 1 + portal.run(send_to_trio.send, response) + +async def main(): + portal = trio.BlockingTrioPortal() + send_to_thread, receive_from_trio = trio.open_memory_channel(0) + send_to_trio, receive_from_thread = trio.open_memory_channel(0) + + async with trio.open_nursery() as nursery: + # In a background thread, run: + # thread_fn(portal, receive_from_trio, send_to_trio) + nursery.start_soon( + trio.run_sync_in_worker_thread, + thread_fn, portal, receive_from_trio, send_to_trio + ) + + # prints "1" + await send_to_thread.send(0) + print(await receive_from_thread.receive()) + + # prints "2" + await send_to_thread.send(1) + print(await receive_from_thread.receive()) + + # When we close the channel, it signals the thread to exit. + await send_to_thread.aclose() + + # When we exit the nursery, it waits for the background thread to + # exit. + +trio.run(main) diff --git a/docs/source/reference-core/channels-mpmc-broken.py b/docs/source/reference-core/channels-mpmc-broken.py new file mode 100644 index 0000000000..2a755acba3 --- /dev/null +++ b/docs/source/reference-core/channels-mpmc-broken.py @@ -0,0 +1,30 @@ +# This example usually crashes! + +import trio +import random + +async def main(): + async with trio.open_nursery() as nursery: + send_channel, receive_channel = trio.open_memory_channel(0) + # Start two producers + nursery.start_soon(producer, "A", send_channel) + nursery.start_soon(producer, "B", send_channel) + # And two consumers + nursery.start_soon(consumer, "X", receive_channel) + nursery.start_soon(consumer, "Y", receive_channel) + +async def producer(name, send_channel): + async with send_channel: + for i in range(3): + await send_channel.send("{} from producer {}".format(i, name)) + # Random sleeps help trigger the problem more reliably + await trio.sleep(random.random()) + +async def consumer(name, receive_channel): + async with receive_channel: + async for value in receive_channel: + print("consumer {} got value {!r}".format(name, value)) + # Random sleeps help trigger the problem more reliably + await trio.sleep(random.random()) + +trio.run(main) diff --git a/docs/source/reference-core/channels-mpmc-fixed.py b/docs/source/reference-core/channels-mpmc-fixed.py new file mode 100644 index 0000000000..a3e7044fe7 --- /dev/null +++ b/docs/source/reference-core/channels-mpmc-fixed.py @@ -0,0 +1,29 @@ +import trio +import random + +async def main(): + async with trio.open_nursery() as nursery: + send_channel, receive_channel = trio.open_memory_channel(0) + async with send_channel, receive_channel: + # Start two producers, giving each its own private clone + nursery.start_soon(producer, "A", send_channel.clone()) + nursery.start_soon(producer, "B", send_channel.clone()) + # And two consumers, giving each its own private clone + nursery.start_soon(consumer, "X", receive_channel.clone()) + nursery.start_soon(consumer, "Y", receive_channel.clone()) + +async def producer(name, send_channel): + async with send_channel: + for i in range(3): + await send_channel.send("{} from producer {}".format(i, name)) + # Random sleeps help trigger the problem more reliably + await trio.sleep(random.random()) + +async def consumer(name, receive_channel): + async with receive_channel: + async for value in receive_channel: + print("consumer {} got value {!r}".format(name, value)) + # Random sleeps help trigger the problem more reliably + await trio.sleep(random.random()) + +trio.run(main)