0

I have client and server module, each one can be started by a function. I just need to find a way to run booth in parallel which:

  1. in case of an exception in the client/server would stop the other so the test runner would not stay stuck

  2. in case of an exception in client/server would print the exception or propagate it to the runner so I could see it and debug the client/server using the test suite

  3. would preferably use threads for performance reasons

The first tentative with simple threads ended with an ugly os._exit(1) when catching a exception in the run method of the thread (which kills the test runner...) Edit: with the threading package

The second tentative (to try to avoid os._exit()) was with concurrent.futures.ThreadPoolExecutor. It allows to get the exception out of the thread but I still can't find a way to abort the other thread.

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    server_future = executor.submit(server)
    client_future = executor.submit(client)

    concurrent.futures.wait([server_future, client_future],
                           return_when=concurrent.futures.FIRST_EXCEPTION)

    if client_future.done() && client_future.exception():
        # we can handle the client exception here
        # but how to stop the server from waiting the client?
        # also, raise is blocking

    if server_future.done() && server_future.exception():
        # same here
  • Is there a way to achieve this with threads?
  • If not with threads, is there a simple way to test a client server app at all? (I think the two first requirements are enough to have a usable solution)

Edit: The client or the server would be blocked on an accept() or a receive() call so I can't periodically pool a flag a decide to exit.(one of classic method to stop a thread)

tuxayo
  • 1,150
  • 1
  • 13
  • 20

1 Answers1

1

You can use the threading package. Be aware though that force killing thread is not a good idea, as discussed here. It seems there is no official way to kill Thread in Python, but you can follow one of the example given on the linked post.

Now you need to wait for one thread to exit before stopping the other one, avoiding your test runner to be stuck. You can use Threads wrapping your server/client launch, and have your main Thread waiting for either client/server Thread to exit before killing the other one.

You can define your client/server Thread like this:

# Server thread (replace 
class testServerThread (threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        # Do stuff if required

    def run(self):
        try:
            startServer() # Or startClient() for your client thread
        except: Exception 
          # Print your exception here, so you can debug

Then, start both client and server thread, and wait for one of them to exit. Once one of them is not alive anymore, you can kill the other and continue on testing.

# Create and start client/server
serverThread = testServerThread ()
clientThread = testClientThread ()

serverThread.start()
clientThread.start()

# Wait at most 5 seconds for them to exit, and loop if they're still both alive
while(serverThread.is_alive() and clientThread.is_alive()):
    serverThread.join(5)
    clientThread.join(5)

# Either client or server exited. Kill the other one.
# Note: the kill function you'll have to define yourself, as said above
if(serverThread.is_alive()):
    serverThread.kill()

if(clientThread.islive()):
    clientThread.kill()

# Done! Your Test runner can continue its work

The central piece of code is the join() function:

Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception –, or until the optional timeout occurs.

So in our case, it will wait 5 seconds for the client and 5 seconds for the server, and if both of them are still alive afterward it will loop again. Whenever one of them exit, the loop will stop, and the remaining thread will be killed.

Community
  • 1
  • 1
Pierre B.
  • 11,612
  • 1
  • 37
  • 58
  • The first try was with the threading package, I should have mentioned it. I've searched about how to kill a thread but couldn't apply a solution because I had no control on what the threads where doing(can't periodically check a flag). In my case they would block doing an accept() or a receive() on a socket. But after searching specifically how to interrupt those operations I found out that I could simply close() the socket! Thanks a lot for indirectly guiding me to that solution! – tuxayo Mar 26 '16 at 00:41