0

Socket.close() does not stop any blocking socket.accept() calls that are already running on that socket.

I have several threads in my python program that only run a blocking socket.accept() call on a unix domain socket that has been closed already. I want to kill these threads by making the socket.accept() calls stop or raise an exception.

I am trying to do this by loading new code in the program, without stopping the program. Therefore, changing the code that spawned these threads or that closed the sockets is not an option.

Is there any way to do this?

This is similar to https://stackoverflow.com/a/10090348/3084431, but these solutions wont work for my code:

  1. This point is not true, closing won't raise an exception on the accept. shutdown does, but that can not be called anymore when the thread is closed.
  2. I can not connect to this socket anymore. The socket is closed.
  3. The threads with the accept calls are already running, I can't change them.
  4. Same as 3

For clarification, I have written some example code that has this problem. This code works in both python 2 and python 3.

import socket
import threading
import time

address = "./socket.sock"

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(address)
sock.listen(5)

def accept():
    print(sock.accept())

t = threading.Thread(target=accept, name="acceptorthread")
t.start()

sock.close()

time.sleep(0.5) # give the thread some time to register the closing

print(threading.enumerate()) # the acceptorthread will still be running

What I need is something that I can run after this code has finished that can stop the acceptor thread somehow.

Community
  • 1
  • 1
Troido
  • 78
  • 6
  • What's wrong with the stock socket behaviour? If an `accept` is "already running", a connection was technically accepted _before_ you stopped accepting new connections - so it's all okay. If you wish to kick all the existing clients, that's what you should rather be doing. – ivan_pozdeev May 07 '17 at 18:56
  • 1
    You will never be able to make this work. Your code already has a serious race condition -- what happens if the `sock.close()` executes at the same time as the call to `accept`? Python's socket object is not thread safe -- it cannot have its state modified in one thread while another thread accesses it. There is absolutely no way you can ensure the `accept` is running, as opposed to about to run or getting ready to block. So this *cannot* be done safely, period. – David Schwartz May 07 '17 at 19:05
  • @ivan_pozdeev There are no clients, it is just a blocking `accept` function that is waiting forever and preventing the thread from finishing. @DavidSchwartz `sock.close` is actually executed while `sock.accept` is running. The problem is that the socket is now closed but `sock.accept` is still running – Troido May 07 '17 at 19:17

2 Answers2

2

There is no mechanism in kernel to notify every listener that a socket is closed. You have to write something yourself. A simple solution is to use timeout on socket:

sock.settimeout(1)

def accept():
    while True:
        try:
            print(sock.accept())
        except socket.timeout:
            continue
        break

Now when you close the socket the next call (after a timeout) to .accept() will throw a "bad descriptor" exception.

Also remember that sockets api in Python is not thread safe. Wrapping every socket call with a lock (or other synchronization methods) is advised in multi-threaded environment.

More advanced (and efficient) would be to use wrap your socket with a select call. Note that the socket does not have to be in non-blocking mode in order to use it.

Therefore, changing the code that spawned these threads or that closed the sockets is not an option.

If that's the case, then you are doomed. Without changing the code running in threads it is impossible to achieve. It's like asking "how can I fix my broken car without modifying the car". Won't happen, mate.

freakish
  • 54,167
  • 9
  • 132
  • 169
  • It would have been possible to fix if the socket wouldn't be closed. If it weren't closed, `sock.shutdown` could make the `accept` function error, thereby stopping the thread. Still, it is most likely not possible to do someting like this when the socket is closed. I was just hoping someone would happen to know a trick to do this. – Troido May 07 '17 at 19:48
0

You should only call .accept() on a socket that has given the "readable" result from some selectors. Then, accept doesn't need to be interrupted.

But in case of spurious wakeup, you should have the listening socket in O_NONBLOCK mode anyway.

o11c
  • 15,265
  • 4
  • 50
  • 75