Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH] bin/ebuild-ipc.py: nonblocking fifo write
Date: Thu, 09 Oct 2014 18:50:30
Message-Id: 5436D8ED.6090501@gentoo.org
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