@@ -706,23 +706,50 @@ mod _multiprocessing { |
706 | 706 |
|
707 | 707 | // if (res < 0 && errno == EAGAIN && blocking) |
708 | 708 | if res < 0 && Errno::last() == Errno::EAGAIN && blocking { |
709 | | - // Couldn't acquire immediately, need to block |
| 709 | + // Couldn't acquire immediately, need to block. |
| 710 | + // |
| 711 | + // Save errno inside the allow_threads closure, before |
| 712 | + // attach_thread() runs — matches CPython which saves |
| 713 | + // `err = errno` before Py_END_ALLOW_THREADS. |
| 714 | + let mut saved_errno = Errno::UnknownErrno; |
| 715 | + |
710 | 716 | #[cfg(not(target_vendor = "apple"))] |
711 | 717 | { |
712 | 718 | loop { |
713 | 719 | let sem_ptr = self.handle.as_ptr(); |
714 | 720 | // Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS |
715 | | - res = if let Some(ref dl) = deadline { |
716 | | - vm.allow_threads(|| unsafe { libc::sem_timedwait(sem_ptr, dl) }) |
| 721 | + let (r, e) = if let Some(ref dl) = deadline { |
| 722 | + vm.allow_threads(|| { |
| 723 | + let r = unsafe { libc::sem_timedwait(sem_ptr, dl) }; |
| 724 | + ( |
| 725 | + r, |
| 726 | + if r < 0 { |
| 727 | + Errno::last() |
| 728 | + } else { |
| 729 | + Errno::from_raw(0) |
| 730 | + }, |
| 731 | + ) |
| 732 | + }) |
717 | 733 | } else { |
718 | | - vm.allow_threads(|| unsafe { libc::sem_wait(sem_ptr) }) |
| 734 | + vm.allow_threads(|| { |
| 735 | + let r = unsafe { libc::sem_wait(sem_ptr) }; |
| 736 | + ( |
| 737 | + r, |
| 738 | + if r < 0 { |
| 739 | + Errno::last() |
| 740 | + } else { |
| 741 | + Errno::from_raw(0) |
| 742 | + }, |
| 743 | + ) |
| 744 | + }) |
719 | 745 | }; |
| 746 | + res = r; |
| 747 | + saved_errno = e; |
720 | 748 |
|
721 | 749 | if res >= 0 { |
722 | 750 | break; |
723 | 751 | } |
724 | | - let err = Errno::last(); |
725 | | - if err == Errno::EINTR { |
| 752 | + if saved_errno == Errno::EINTR { |
726 | 753 | vm.check_signals()?; |
727 | 754 | continue; |
728 | 755 | } |
@@ -751,29 +778,47 @@ mod _multiprocessing { |
751 | 778 | // No timeout: use sem_wait (available on macOS) |
752 | 779 | loop { |
753 | 780 | let sem_ptr = self.handle.as_ptr(); |
754 | | - res = vm.allow_threads(|| unsafe { libc::sem_wait(sem_ptr) }); |
| 781 | + let (r, e) = vm.allow_threads(|| { |
| 782 | + let r = unsafe { libc::sem_wait(sem_ptr) }; |
| 783 | + ( |
| 784 | + r, |
| 785 | + if r < 0 { |
| 786 | + Errno::last() |
| 787 | + } else { |
| 788 | + Errno::from_raw(0) |
| 789 | + }, |
| 790 | + ) |
| 791 | + }); |
| 792 | + res = r; |
| 793 | + saved_errno = e; |
755 | 794 | if res >= 0 { |
756 | 795 | break; |
757 | 796 | } |
758 | | - let err = Errno::last(); |
759 | | - if err == Errno::EINTR { |
| 797 | + if saved_errno == Errno::EINTR { |
760 | 798 | vm.check_signals()?; |
761 | 799 | continue; |
762 | 800 | } |
763 | 801 | break; |
764 | 802 | } |
765 | 803 | } |
766 | 804 | } |
767 | | - } |
768 | 805 |
|
769 | | - // result handling: |
770 | | - if res < 0 { |
| 806 | + // result handling — use saved_errno, not Errno::last() |
| 807 | + if res < 0 { |
| 808 | + match saved_errno { |
| 809 | + Errno::EAGAIN | Errno::ETIMEDOUT => return Ok(false), |
| 810 | + Errno::EINTR => { |
| 811 | + return vm.check_signals().map(|_| false); |
| 812 | + } |
| 813 | + _ => return Err(os_error(vm, saved_errno)), |
| 814 | + } |
| 815 | + } |
| 816 | + } else if res < 0 { |
| 817 | + // Non-blocking path failed, or blocking=false |
771 | 818 | let err = Errno::last(); |
772 | 819 | match err { |
773 | 820 | Errno::EAGAIN | Errno::ETIMEDOUT => return Ok(false), |
774 | 821 | Errno::EINTR => { |
775 | | - // EINTR should be handled by the check_signals() loop above |
776 | | - // If we reach here, check signals again and propagate any exception |
777 | 822 | return vm.check_signals().map(|_| false); |
778 | 823 | } |
779 | 824 | _ => return Err(os_error(vm, err)), |
|