New submission from Aymeric Augustin <aymeric.augus...@m4x.org>:

**Summary**

1. Is it correct for `Server.wait_closed()` (as implemented in asyncio) to be a 
no-op after `Server.close()`?
2. How can I tell that all incoming connections have been received by 
`connection_made()` after `Server.close()`?

**Details**

After calling `Server.close()`, `_sockets is None`, which makes 
`Server.wait_closed()` a no-op: it returns immediately without doing anything 
(as mentioned in https://bugs.python.org/issue33727).

I'm not sure why the docs suggest to call `wait_closed()` after `close()` if 
it's a no-op. My best guess is: "this design supports third-party event loops 
that requires an asynchronous API for closing servers, but the built-in event 
loops don't need that". Does someone know?

I wrote a very simple server that merely accepts connections. I ran experiments 
where I saturate the server with incoming client connections and close it. I 
checked what happens around `close()` (and `wait_closed()` -- but as it doesn't 
do anything after `close()` I'll just say `close()` from now on.)

The current implementation appears to work as documented, assuming an rather 
low level interpretation of the docs of `Server.close()`.

> Stop serving: close listening sockets and set the sockets attribute to None.

Correct -- I'm not seeing any `accept` calls in 
`BaseSelectorEventLoop._accept_connection` after `close()`.

> The sockets that represent existing incoming client connections are left open.

Correct -- if "existing incoming client connections" is interpreted as "client 
connections that have gone through `accept`".

> The server is closed asynchronously, use the wait_closed() coroutine to wait 
> until the server is closed.
 
I'm seeing calls to `connection_made()` _after_ `close()` because 
`BaseSelectorEventLoop._accept_connection2` triggers `connection_made()` 
asynchronously with `call_soon()`.

This is surprising for someone approaching asyncio from the public API rather 
than the internal implementation. `connection_made()` is the first contact with 
new connections. The concept of "an existing incoming client connection for 
which `connection_made()` wasn't called yet" is unexpected.

This has practical consequences.

Consider a server that keeps track of established connections via 
`connection_made` and `connection_lost`. If this server calls `Server.close()`, 
awaits `Server.wait_closed()`, makes a list of established connections and 
terminates them, there's no guarantee that all connections will be closed. 
Indeed, new connections may appear and call `connection_made()` after `close()` 
and `wait_closed()` returned!

`wait_closed()` seems ineffective for this use case.

----------
components: asyncio
messages: 326725
nosy: asvetlov, aymeric.augustin, yselivanov
priority: normal
severity: normal
status: open
title: Counter-intuitive behavior of Server.close() / wait_closed()
type: behavior
versions: Python 3.7

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue34852>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to