Skip to content

Commit 998acd3

Browse files
committed
initial work on replacing process with thread
1 parent 02750eb commit 998acd3

File tree

1 file changed

+59
-32
lines changed

1 file changed

+59
-32
lines changed

lib/spack/llnl/util/tty/log.py

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import atexit
1111
import errno
1212
import multiprocessing
13+
import threading
1314
import os
1415
import re
1516
import select
@@ -50,6 +51,43 @@ def ignore_signal(signum):
5051
signal.signal(signum, old_handler)
5152

5253

54+
class AltSignalHandler(object):
55+
def __init__(self, orig_handler, alt_handler):
56+
self.alt_handler = alt_handler
57+
self.orig_handler = orig_handler
58+
self.current_handler = orig_handler
59+
60+
def handle(self, signum, frame):
61+
self.current_handler(signum, frame)
62+
63+
def use_alt_handler(self):
64+
self.current_handler = self.alt_handler
65+
66+
def use_orig_handler(self):
67+
self.current_handler = self.orig_handler
68+
69+
70+
class SignalHandler(object):
71+
def __init__(self):
72+
self.sig_to_handler = {}
73+
74+
@staticmethod
75+
def ignore_signal(signum, frame):
76+
pass
77+
78+
def register_alt_handler(self, signum, fn):
79+
orig_handler = signal.signal(signum, fn)
80+
self.sig_to_handler[signum] = AltSignalHandler(orig_handler, fn)
81+
82+
def can_ignore(self, signum):
83+
self.register_alt_handler(signum, SignalHandler.ignore_signal)
84+
85+
def activate_alt_handler(self, signum):
86+
self.sig_to_handler[signum].use_alt_handler()
87+
yield
88+
self.sig_to_handler[signum].use_orig_handler()
89+
90+
5391
def _is_background_tty(stream):
5492
"""True if the stream is a tty and calling process is in the background.
5593
"""
@@ -438,39 +476,33 @@ def __enter__(self):
438476
# OS-level pipe for redirecting output to logger
439477
read_fd, write_fd = os.pipe()
440478

441-
# Use multiprocessing.COnnection to transmit FD from the parent
442-
# process to the child
443-
read_wrapper = multiprocessing.connection.Connection(read_fd)
444-
445479
# Multiprocessing pipe for communication back from the daemon
446480
# Currently only used to save echo value between uses
447481
self.parent_pipe, child_pipe = multiprocessing.Pipe()
448482

449483
# Sets a daemon that writes to file what it reads from a pipe
450484
try:
451-
# need to pass this b/c multiprocessing closes stdin in child.
452485
try:
453-
input_wrapper = multiprocessing.connection.Connection(
454-
os.dup(sys.stdin.fileno())
455-
)
486+
input = os.fdopen(os.dup(sys.stdin.fileno()))
456487
except BaseException:
457-
input_wrapper = None # just don't forward input if this fails
488+
input = None # just don't forward input if this fails
458489

459-
self.process = multiprocessing.Process(
490+
read = os.fdopen(read_fd, 'r', 1)
491+
492+
alt_stdout = os.fdopen(os.dup(self.saved_fds._saved_stdout), 'w')
493+
494+
self.thread = threading.Thread(
460495
target=_writer_daemon,
461496
args=(
462-
input_wrapper, read_wrapper, self.echo, self.log_file,
463-
child_pipe
497+
input, read, alt_stdout,
498+
self.echo, self.log_file, child_pipe
464499
)
465500
)
466-
self.process.daemon = True # must set before start()
467-
self.process.start()
501+
self.thread.start()
468502

469503
finally:
504+
# don't close input here
470505
pass
471-
#if input_wrapper:
472-
# input_wrapper.close()
473-
#read_wrapper.close()
474506

475507
# Flush immediately before redirecting so that anything buffered
476508
# goes to the original stream
@@ -556,7 +588,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
556588

557589
# join the daemon process. The daemon will quit automatically
558590
# when the write pipe is closed; we just wait for it here.
559-
self.process.join()
591+
self.thread.join()
560592

561593
# restore old color and debug settings
562594
tty.color._force_color = self._saved_color
@@ -584,7 +616,7 @@ def force_echo(self):
584616
sys.stdout.flush()
585617

586618

587-
def _writer_daemon(stdin_wrapper, read_wrapper, echo, log_file, control_pipe):
619+
def _writer_daemon(stdin, read, alt_stdout, echo, log_file, control_pipe):
588620
"""Daemon used by ``log_output`` to write to a log file and to ``stdout``.
589621
590622
The daemon receives output from the parent process and writes it both
@@ -632,19 +664,16 @@ def _writer_daemon(stdin_wrapper, read_wrapper, echo, log_file, control_pipe):
632664
"""
633665
# Use line buffering (3rd param = 1) since Python 3 has a bug
634666
# that prevents unbuffered text I/O.
635-
in_pipe = os.fdopen(read_wrapper._handle, 'r', 1)
636-
637-
if stdin_wrapper:
638-
stdin = os.fdopen(stdin_wrapper._handle)
639-
else:
640-
stdin = None
667+
in_pipe = read
641668

642669
# list of streams to select from
643670
istreams = [in_pipe, stdin] if stdin else [in_pipe]
644671
force_echo = False # parent can force echo for certain output
645672

646673
log_file = log_file.unwrap()
647674

675+
alt_stdout.write("<<< Starting thread")
676+
648677
try:
649678
with keyboard_input(stdin) as kb:
650679
while True:
@@ -685,8 +714,8 @@ def _writer_daemon(stdin_wrapper, read_wrapper, echo, log_file, control_pipe):
685714

686715
# Echo to stdout if requested or forced.
687716
if echo or force_echo:
688-
sys.stdout.write(line)
689-
sys.stdout.flush()
717+
alt_stdout.write(line)
718+
alt_stdout.flush()
690719

691720
# Stripped output to log file.
692721
log_file.write(_strip(line))
@@ -699,18 +728,16 @@ def _writer_daemon(stdin_wrapper, read_wrapper, echo, log_file, control_pipe):
699728

700729
except BaseException:
701730
tty.error("Exception occurred in writer daemon!")
702-
traceback.print_exc()
731+
traceback.print_exc(file=alt_stdout)
703732

704733
finally:
705734
# send written data back to parent if we used a StringIO
706735
if isinstance(log_file, StringIO):
707736
control_pipe.send(log_file.getvalue())
708737
log_file.close()
709-
read_wrapper.close()
710-
stdin_wrapper.close()
711738

712-
# send echo value back to the parent so it can be preserved.
713-
control_pipe.send(echo)
739+
# send echo value back to the parent so it can be preserved.
740+
control_pipe.send(echo)
714741

715742

716743
def _retry(function):

0 commit comments

Comments
 (0)