Belle II Software  release-08-01-10
signals.py
1 #!/usr/bin/env python3
2 
3 
10 
11 # Tests basf2 behaviour under various POSIX signals
12 # different possibilities for termination
13 # - SIGKILL: immediate, no cleanup (won't test this for obvious reasons)
14 # - SIGINT= safe and slow: abort event processing quickly, but call processTerminate()
15 # - most signals: immediate, but IPC structures cleaned up (segmentation fault, or termination in initialize())
16 # This should also work in parallel processing.
17 # In initialize(), termination should always be immediate.
18 
19 import sys
20 import os
21 import signal
22 import tempfile
23 import shutil
24 import basf2
25 
26 
27 class TestModule(basf2.Module):
28  """test"""
29 
30  def __init__(self, init_signal, event_signal):
31  """Setup module for given signal settings"""
32  super().__init__()
33 
34  self.init_signalinit_signal = init_signal
35 
36  self.event_signalevent_signal = event_signal
37 
38  def initialize(self):
39  """If init_signal is true, kill on init, otherwise just print info"""
40 
41  if self.init_signalinit_signal:
42  pid = os.getpid()
43  basf2.B2INFO("Killing %s in init (sig %d)" % (pid, self.init_signalinit_signal))
44  os.kill(pid, self.init_signalinit_signal)
45  basf2.B2INFO("initialize()")
46 
47  def event(self):
48  """If init_signal is true raise error, if event_signal is true kill process, otherwise print info"""
49  if self.init_signalinit_signal:
50  basf2.B2FATAL("Processing should have been stopped in init!")
51  if self.event_signalevent_signal:
52  pid = os.getpid()
53  basf2.B2INFO("Killing %s in event (sig %d)" % (pid, self.event_signalevent_signal))
54  os.kill(pid, self.event_signalevent_signal)
55  basf2.B2INFO("event()")
56 
57 
58 # Tests running in Bamboo have SIGQUIT blocked via sigmask(3),
59 # so let's unblock it for this test.
60 # See Jira ticket BII-1948 for details
61 signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGQUIT])
62 
63 # we test for stray resources later, so let's clean up first
64 os.system('clear_basf2_ipc')
65 
66 tmpdir = tempfile.mkdtemp(prefix='b2signal_test')
67 
68 basf2.set_random_seed("something important")
69 
70 
71 def run_test(init_signal, event_signal, abort, test_in_process):
72  """
73  @param init_signal kill in initialize()
74  @param event_signal kill in event()
75  @param abort Should this test give a non-zero return code?
76  @param test_in_process 0 input, 1 parallel, 2 output
77  """
78 
79  testFile = tempfile.NamedTemporaryFile(dir=tmpdir, delete=False)
80 
81  # main thread just monitors exit status
82  if os.fork() != 0:
83  retbytes = os.wait()[1]
84  retcode = (retbytes & 0xff00) >> 8 # high byte
85  killsig = retbytes & 0x00ff % 128 # low byte minus core dump bit
86  status_ok = False
87  if abort:
88  if killsig in (init_signal, event_signal):
89  status_ok = True
90  if retcode != 0 or killsig == signal.SIGTERM:
91  status_ok = True
92  else:
93  if killsig == 0 and retcode == 0:
94  status_ok = True
95 
96  if not status_ok:
97  print(killsig, retcode)
98  if killsig:
99  raise RuntimeError("Killed with wrong signal %d?" % (killsig))
100  else:
101  raise RuntimeError("Wrong exit code %d" % (retcode))
102 
103  fileExists = os.path.isfile(testFile.name)
104  if fileExists and (not abort or event_signal == signal.SIGINT):
105  # is ROOT file ok?
106  file_ok_ret = os.system('b2file-metadata-show ' + testFile.name)
107  basf2.B2WARNING("file_ok_ret: " + str(file_ok_ret))
108  if file_ok_ret != 0:
109  raise RuntimeError("Root file not properly closed!")
110 
111  # clear_basf2_ipc shouldn't find anything to clean up
112  ret = os.system('clear_basf2_ipc')
113  if ret != 0:
114  raise RuntimeError("Some IPC structures were not cleaned up")
115 
116  basf2.B2WARNING("test ok.")
117  return
118 
119  # actual test
120  num_events = 5
121  if abort:
122  num_events = int(1e8) # larger number to test we abort early.
123 
124  # Create paths
125  main = basf2.Path()
126  main.add_module('EventInfoSetter', evtNumList=[num_events])
127  if test_in_process == 0:
128  testmod = main.add_module(TestModule(init_signal, event_signal))
129  main.add_module('ProgressBar').set_property_flags(basf2.ModulePropFlags.PARALLELPROCESSINGCERTIFIED)
130  elif test_in_process == 1:
131  testmod = main.add_module(TestModule(init_signal, event_signal))
132  testmod.set_property_flags(basf2.ModulePropFlags.PARALLELPROCESSINGCERTIFIED)
133  elif test_in_process == 2:
134  main.add_module('ProgressBar').set_property_flags(basf2.ModulePropFlags.PARALLELPROCESSINGCERTIFIED)
135  testmod = main.add_module(TestModule(init_signal, event_signal))
136  main.add_module('RootOutput', outputFileName=testFile.name, updateFileCatalog=False)
137 
138  basf2.B2WARNING("Running with PID " + str(os.getpid()))
139  basf2.process(main)
140  sys.exit(0)
141 
142 
143 for nproc in [0, 3]:
144  basf2.set_nprocesses(nproc)
145  for in_proc in [0, 1, 2]:
146  if nproc == 0 and in_proc != 0:
147  break # running more tests in single process mode doesn't make sense
148  basf2.B2WARNING("== starting tests with nproc=%d, test_in_process=%d" % (nproc, in_proc))
149 
150  try:
151  run_test(None, None, abort=False, test_in_process=in_proc)
152  if in_proc != 1: # worker processes do not handle the events
153  run_test(signal.SIGINT, None, abort=True, test_in_process=in_proc)
154  run_test(None, signal.SIGINT, abort=True, test_in_process=in_proc)
155  run_test(signal.SIGTERM, None, abort=True, test_in_process=in_proc)
156  run_test(None, signal.SIGTERM, abort=True, test_in_process=in_proc)
157  run_test(signal.SIGQUIT, None, abort=True, test_in_process=in_proc)
158  run_test(None, signal.SIGQUIT, abort=True, test_in_process=in_proc)
159 
160  # crashes in any process should also result in reasonable cleanup
161  run_test(signal.SIGSEGV, None, abort=True, test_in_process=in_proc)
162  run_test(None, signal.SIGSEGV, abort=True, test_in_process=in_proc)
163 
164  # SIGPIPE would be nice, too. just stops immediately now
165  except Exception as e:
166  # Note: Without specifying exception type, we might get those from forked processes, too
167  basf2.B2WARNING("Exception occured for nproc=%d, test_in_process=%d" % (nproc, in_proc))
168  raise e
169 
170 print("\n")
171 print("=========================================================================")
172 print("All signal tests finished successfully!")
173 print("=========================================================================")
174 
175 shutil.rmtree(tmpdir)
event_signal
signal to emit to ourselves during event()
Definition: signals.py:36
init_signal
signal to emit to ourselves during initialize()
Definition: signals.py:34
def __init__(self, init_signal, event_signal)
Definition: signals.py:30
def initialize(self)
Definition: signals.py:38
def event(self)
Definition: signals.py:47