| 578 | |
| 579 | @contextlib.contextmanager |
| 580 | def _running(self, run_interp, exec_interp): |
| 581 | token = b'\0' |
| 582 | r_in, w_in = self.pipe() |
| 583 | r_out, w_out = self.pipe() |
| 584 | |
| 585 | def close(): |
| 586 | _close_file(r_in) |
| 587 | _close_file(w_in) |
| 588 | _close_file(r_out) |
| 589 | _close_file(w_out) |
| 590 | |
| 591 | # Start running (and wait). |
| 592 | script = dedent(f""" |
| 593 | import os |
| 594 | try: |
| 595 | # handshake |
| 596 | token = os.read({r_in}, 1) |
| 597 | os.write({w_out}, token) |
| 598 | # Wait for the "done" message. |
| 599 | os.read({r_in}, 1) |
| 600 | except BrokenPipeError: |
| 601 | pass |
| 602 | except OSError as exc: |
| 603 | if exc.errno != 9: |
| 604 | raise # re-raise |
| 605 | # It was closed already. |
| 606 | """) |
| 607 | failed = None |
| 608 | def run(): |
| 609 | nonlocal failed |
| 610 | try: |
| 611 | run_interp(script) |
| 612 | except Exception as exc: |
| 613 | failed = exc |
| 614 | close() |
| 615 | t = threading.Thread(target=run) |
| 616 | t.start() |
| 617 | |
| 618 | # handshake |
| 619 | try: |
| 620 | os.write(w_in, token) |
| 621 | token2 = os.read(r_out, 1) |
| 622 | assert token2 == token, (token2, token) |
| 623 | except OSError: |
| 624 | t.join() |
| 625 | if failed is not None: |
| 626 | raise failed |
| 627 | |
| 628 | # CM __exit__() |
| 629 | try: |
| 630 | try: |
| 631 | yield |
| 632 | finally: |
| 633 | # Send "done". |
| 634 | os.write(w_in, b'\0') |
| 635 | finally: |
| 636 | close() |
| 637 | t.join() |