1 |
Use nonblocking write instead of a fork for writing to the fifo. |
2 |
This has the advantage of avoiding a possible python futex deadlock |
3 |
following fork as reported in bug #524328. |
4 |
|
5 |
X-Gentoo-Bug: 524328 |
6 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=524328 |
7 |
--- |
8 |
bin/ebuild-ipc.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++------- |
9 |
1 file changed, 56 insertions(+), 8 deletions(-) |
10 |
|
11 |
diff --git a/bin/ebuild-ipc.py b/bin/ebuild-ipc.py |
12 |
index 3e6d90c..b47ee23 100755 |
13 |
--- a/bin/ebuild-ipc.py |
14 |
+++ b/bin/ebuild-ipc.py |
15 |
@@ -5,6 +5,7 @@ |
16 |
# This is a helper which ebuild processes can use |
17 |
# to communicate with portage's main python process. |
18 |
|
19 |
+import errno |
20 |
import logging |
21 |
import os |
22 |
import pickle |
23 |
@@ -44,19 +45,66 @@ import portage |
24 |
portage._internal_caller = True |
25 |
portage._disable_legacy_globals() |
26 |
|
27 |
-from portage.util._async.ForkProcess import ForkProcess |
28 |
from portage.util._eventloop.global_event_loop import global_event_loop |
29 |
+from _emerge.AbstractPollTask import AbstractPollTask |
30 |
from _emerge.PipeReader import PipeReader |
31 |
|
32 |
-class FifoWriter(ForkProcess): |
33 |
+RETURNCODE_WRITE_FAILED = 2 |
34 |
|
35 |
- __slots__ = ('buf', 'fifo',) |
36 |
+class FifoWriter(AbstractPollTask): |
37 |
|
38 |
- def _run(self): |
39 |
- # Atomically write the whole buffer into the fifo. |
40 |
- with open(self.fifo, 'wb', 0) as f: |
41 |
- f.write(self.buf) |
42 |
- return os.EX_OK |
43 |
+ __slots__ = ('buf', 'fifo', '_fd', '_reg_id',) |
44 |
+ |
45 |
+ def _start(self): |
46 |
+ try: |
47 |
+ self._fd = os.open(self.fifo, os.O_WRONLY|os.O_NONBLOCK) |
48 |
+ except OSError as e: |
49 |
+ if e.errno == errno.ENXIO: |
50 |
+ # This happens if the daemon has been killed. |
51 |
+ self.returncode = RETURNCODE_WRITE_FAILED |
52 |
+ self._unregister() |
53 |
+ self._async_wait() |
54 |
+ return |
55 |
+ else: |
56 |
+ raise |
57 |
+ self._reg_id = self.scheduler.io_add_watch( |
58 |
+ self._fd, |
59 |
+ self.scheduler.IO_OUT | self.scheduler.IO_HUP | \ |
60 |
+ self._exceptional_events, self._output_handler) |
61 |
+ self._registered = True |
62 |
+ |
63 |
+ def _output_handler(self, fd, event): |
64 |
+ if event & self.scheduler.IO_OUT: |
65 |
+ # The whole buf should be able to fit in the fifo with |
66 |
+ # a single write call, so there's no valid reason for |
67 |
+ # os.write to raise EAGAIN here. |
68 |
+ buf = self.buf |
69 |
+ while buf: |
70 |
+ buf = buf[os.write(fd, buf):] |
71 |
+ self.returncode = os.EX_OK |
72 |
+ self._unregister() |
73 |
+ self.wait() |
74 |
+ return False |
75 |
+ else: |
76 |
+ self._unregister_if_appropriate(event) |
77 |
+ if not self._registered: |
78 |
+ self.returncode = RETURNCODE_WRITE_FAILED |
79 |
+ self.wait() |
80 |
+ return False |
81 |
+ return True |
82 |
+ |
83 |
+ def _cancel(self): |
84 |
+ self.returncode = self._cancelled_returncode |
85 |
+ self._unregister() |
86 |
+ |
87 |
+ def _unregister(self): |
88 |
+ self._registered = False |
89 |
+ if self._reg_id is not None: |
90 |
+ self.scheduler.source_remove(self._reg_id) |
91 |
+ self._reg_id = None |
92 |
+ if self._fd is not None: |
93 |
+ os.close(self._fd) |
94 |
+ self._fd = None |
95 |
|
96 |
class EbuildIpc(object): |
97 |
|
98 |
-- |
99 |
1.8.5.5 |