I have a multithreaded program and I'm having some problems with exiting it cleanly when using a keyboard interrupt. Is there a way I could exit the program cleanly even when there are multiple threads running?
Here is the code that gives the error when the program is shut down from main thread.
def oneround(self):
try:
threadLocal.s.send(threadLocal.message)
threadLocal.next_time += threadLocal.delay
threadLocal.sch.enterabs(threadLocal.next_time, 0, self.oneround, ())
if self.duration != 0 and time.time() > self.timeout:
print '\n' + 'Client socket closed after ' + str(self.duration) + ' seconds'
threadLocal.s.close()
os.kill(os.getpid(), signal.SIGINT)
except IOError as (errno, strerror):
if errno == 111:
print 'Connection failed'
threadLocal.s.close()
Here is the whole error message:
Exception in thread Thread-1 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
File "/usr/lib/python2.7/threading.py", line 763, in run
File "trafgen6.py", line 592, in init
File "/usr/lib/python2.7/sched.py", line 117, in run
File "trafgen6.py", line 598, in oneround
type 'exceptions.AttributeError': 'NoneType' object has no attribute 's'
EDIT Here is the rest of the code for client-class
class Client(QThread):
def __init__(self, host, port, rate, duration, parent = None):
super(Client, self).__init__(parent)
threadLocal.sch = sched.scheduler(time.time, time.sleep)
threadLocal.next_time = time.time()
threadLocal.sch.enterabs(threadLocal.next_time, 0, self.oneround, ())
host = host
port = port
speed = rate
self.duration = duration
threadLocal.delay = 1.0 / (speed*128)
n = bool(True)
openfile = open('1024')
threadLocal.message = openfile.read()
threadLocal.s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
threadLocal.s.setblocking(0)
try:
threadLocal.s.connect((host,port))
threadLocal.s.send(threadLocal.message)
time.sleep(0.1)
foo = threadLocal.s.recvfrom(1500)
if foo != " ":
pass
except IOError as (errno, strerror):
if errno == -9:
n = bool(False)
print 'Invalid address'
os.kill(os.getpid(), signal.SIGINT)
if errno == 11:
n = bool(False)
print 'Connection failed'
os.kill(os.getpid(), signal.SIGINT)
if n == True:
self.timeout = time.time() + duration
threadLocal.sch.run()
EDIT 2 Here is the code for the main thread
if __name__ == '__main__':
f = open('1024','w')
for i in range(0, 962):
f.write('@')
f.close()
def valid_interfaces():
for i in ni.interfaces():
ip = ni.ifaddresses(i)[ni.AF_INET6][0]['addr']
if not ip.startswith('fe80') and not i.startswith('lo'):
list.insert(0,i)
parser = argparse.ArgumentParser(description='Send Ipv6 traffic through your network')
group1 = parser.add_mutually_exclusive_group()
group2 = parser.add_mutually_exclusive_group()
threadLocal = threading.local()
list = []
valid_interfaces()
group1.add_argument('-g', '--gui',
action='store_true', dest='gui',
help='Start graphical user interface. Do not use other arguments with this option')
group1.add_argument('-s', '--server',
action='store_true', dest='server',
help='Set the program to run in server mode')
group1.add_argument('-c', '--client',
action='store_true', dest='client',
help='Set the program to run in client mode')
group2.add_argument('-i', '--interface',
action='store', dest='interface', choices=list,
help='Specifies the interface for server')
group2.add_argument('-d', '--destip',
action='store', dest='targetip',
help='Sets the destination IPv6 address eg.2001:dead:beef::1')
parser.add_argument('-p', '--port',
action='store', dest='port', type=int,
help='Specifies the port used by server or client | Default 5001', default=5001)
parser.add_argument('-t', '--time',
action='store', dest='time', type=int,
help='Specifies how long traffic will be sent| Default (0) is unlimited', default=0)
parser.add_argument('-r', '--rate',
action='store', dest='rate', type=int,
help='Sets the rate at which traffic will be sent | Default 1Mb/s', default=1)
args = parser.parse_args()
if args.gui:
app = QApplication(sys.argv)
app.setStyle('cleanlooks')
mainwindow = Application()
mainwindow.show()
sys.exit(app.exec_())
if args.server and args.interface:
thread1 = threading.Thread(target=Server, args=[args.interface, args.port])
thread1.setDaemon(True)
thread1.start()
mode = 1
if args.client and args.targetip:
thread1 = threading.Thread(target=Client, args=[args.targetip, args.port, args.rate, args.time])
thread1.setDaemon(True)
thread1.start()
mode = 2
if threading.active_count() > 0:
calculator(mode)
And here is the code for the main thread
class calculator(QThread):
def __init__(self, mode, parent = None):
super(calculator, self).__init__(parent)
mb = 1048576.00
try:
while True:
try:
sent = {}
recv = {}
inwards = []
out = []
total = []
n = 1
for i in ni.interfaces():
counter = psutil.net_io_counters(pernic=True)[i]
sent.update({n:counter.bytes_sent})
recv.update({n:counter.bytes_recv})
n +=1
time.sleep(1)
n = 1
for i in ni.interfaces():
counter = psutil.net_io_counters(pernic=True)[i]
totalsent = counter.bytes_sent - sent[n]
totalrecv = counter.bytes_recv - recv[n]
alldata = totalsent + totalrecv
self.totalout = (totalsent/mb) * 8
self.totalin = (totalrecv/mb) * 8
self.totalinout = (alldata/mb) * 8
inwards.insert((n-1), float("%.2f" % self.totalin))
out.insert((n-1), float("%.2f" % self.totalout))
total.insert((n-1), float("%.2f" % self.totalinout))
n += 1
inwards = sum(inwards)
out = sum(out)
total = sum(total)
sys.stdout.write('\r' + 'Outbound traffic ' + str(out) + 'Mb/s ' + 'Inbound traffic ' + str$
sys.stdout.flush()
except KeyboardInterrupt:
if mode == 1:
print '\n' + 'Server stopped'
raise
elif mode == 2:
print '\n' + 'Client stopped'
raise
except KeyboardInterrupt:
sys.exit()
threading.local()
creates an object that's specific to the currently running thread when the call is made. You only do this once on the main thread and then share it with all of the others. Once the interpreter starts shutting down and this goes out of scope, the other threads have a broken reference.
threadLocal = threading.local()
must be moved into your actual thread, i.e. the top of Client.__init__
.