Shutting down a cross-platform multithreaded app

Chris Angelico rosuav at gmail.com
Sat Sep 19 12:14:58 CEST 2015


On Sat, Sep 19, 2015 at 7:49 PM, James Harris <james.harris.1 at gmail.com> wrote:
> "Chris Angelico" <rosuav at gmail.com> wrote in message
> news:mailman.8.1442612439.21674.python-list at python.org...
>>
>> On Sat, Sep 19, 2015 at 3:17 AM, James Harris <james.harris.1 at gmail.com>
>> wrote:
>>>
>>> Needless to say, on a test Windows machine AF_UNIX is not present. The
>>> only
>>> cross-platform option, therefore, seems to be to use each subthread's
>>> select()s to monitor two AF_INET sockets: the one to the client and a
>>> control one from the master thread. I would seem to need IP socket pairs
>>> between the master thread and the subthreads. If the master thead
>>> receives a
>>> shutdown signal it will send a shutdown command to each subthread.
>>>
>>> The above seems logical but would use quite a few IP sockets. I cannot
>>> think
>>> of a better way, though. Any comments on the ideas above?
>>
>>
>> If you're using select() to monitor the sockets, you don't actually
>> then have to _do_ anything with the shutdown socket. You could have a
>> single socket that sends the shutdown signal to all your workers.
>
>
> I don't understand how a single socket could send the signal to all the
> workers. I did consider some form of multicast but thought it too
> complicated (and possibly infeasible).

The way I'm describing it, the workers never actually read from the
socket. Once that socket becomes readable, they immediately shut down,
without making the socket no-longer-readable.

>> Bear in mind, though, that Windows has no protection against other
>> processes shutting you down. You can restrict it to 127.0.0.1 (of
>> course) but any program running on the same computer as the server -
>> regardless of user permissions etc - will be able to connect to your
>> sockets.
>
>
> I was thinking of a connected UDP socket. That way, AIUI, at least in the
> absence of forged datagrams, only the master thread will be able to
> communicate with the worker, due to connected UDP sockets demulitplexing
> datagrams based on their source as well as their destination, i.e. on a
> 5-tuple (UDP, source IP, source port, destination IP, destination port).

TCP sockets also work on that set of five. That's why I suggested a
pre-connected TCP socket, with the original listening socket closed.

(And as mentioned, Python 3.5 supports socketpair() on Windows. That
would definitely be the best option.)

> That sounds similar to what I had in mind but I am not sure why you would
> close the listening socket. Connections could come in at any time and
> threads could therefore be needed at any time so I was thinking that the
> master thread (the one with the listening TCP socket) would just sit waiting
> for new connection requests (or an interrupting signal).

TCP sockets work on the basis of a master socket and any number of
spawned sockets. The master is what gives you an open port; each
spawned socket represents one connection with one client. Once you
have an established connection, the master should be able to be closed
without disrupting that. No other process will be able to connect to
you, but you'll still be able to use one end of the socket to make the
other end readable.

>> This will make it difficult for ordinary userspace code to mess with
>> you. It'd still be possible, I think, for something with raw sockets
>> access to feign a termination signal; I have no idea what protections
>> Windows offers you there.
>
>
> Yes, something which could forge a packet could tell a worker to close down.
> I don't think there's any significant problem here, thought, because:
>
> * other programs are similarly vulnerable to forged packets
> * the only forgery effect is to tell a worker thread to stop - not a big
> loss
> * the shutdown protocol would/should cause the client to re-request
> * a forger would have to know the specific port number used by the master
> thread to communicate with that particular worker, and the port number that
> worker was using.
>
> Overall, I think it would be more than robust enough.

With UDP, any process that can send a UDP packet can flood the system
with them until your workers shut down. You wouldn't even notice until
it succeeds. With TCP, at least an attacker would need raw socket
access. It's still not as protected as a Unix domain socket, but it's
a bit harder for someone to do.

> Notwithstanding your comment about a single socket, above, no one seems so
> far to have objected to the number of sockets this would need, which was my
> main concern, so that's good!

Sure. Sockets are pretty cheap. Even if you had one for every worker,
there's room for you to have thousands (maybe tens of thousands) of
workers without a problem. I think you'll run into other scaling
problems with that many workers on one computer :)

ChrisA


More information about the Python-list mailing list