Repository: encode/uvicorn

Issue:
uvicorn reload fails on Windows and Python 3.8
I know this was referred to in #477 and #509 but it still isn't solved (making a new issue as I hijacked a closed one before).

Using uvicorn checked out 996aa2d, running with or without `--loop asyncio` changes nothing (that code was removed):

```
ERROR:    Accept failed on a socket
socket: <asyncio.TransportSocket fd=764, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('127.0.0.1', 8000)>
Traceback (most recent call last):
  File "C:\Users\f\scoop\apps\python\current\lib\asyncio\proactor_events.py", line 817, in loop
    f = self._proactor.accept(sock)
  File "C:\Users\f\scoop\apps\python\current\lib\asyncio\windows_events.py", line 545, in accept
    self._register_with_iocp(listener)
  File "C:\Users\f\scoop\apps\python\current\lib\asyncio\windows_events.py", line 714, in _register_with_iocp
    _overlapped.CreateIoCompletionPort(obj.fileno(), self._iocp, 0, 0)
OSError: [WinError 87] The parameter is incorrect
```

```
Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] on win32
```


Discussion:
This does not repro on py37, reloading works fine there.

```
Python 3.7.5 (tags/v3.7.5:5c02a39a0b, Oct 15 2019, 00:11:34) [MSC v.1916 64 bit (AMD64)] on win32
```
Right, so the problem here is to do with IOCP.

In Python 3.8 the default loop policy changes on Windows to use IOCP by default. We probably want to *explcitly* turn this off (and remove our `loop="iocp"` support completly, since it appears to not work.)

Does this only occur when using multiple workers / reload, or does it always occur on Python 3.8 with Windows?
~I can trigger the same on windows and 3.7.5~
passing `--loop="asyncio"` is fine on 3.7.5
passing `--loop="asyncio"` on 3.8.0 the bug still here

>Does this only occur when using multiple workers / reload, or does it always occur on Python 3.8 with Windows?

I'm not sure I understand the question. If I launch uvicorn with --reload and just use it (without changing code), it works OK with no errors. As soon as the reload is requested all hell breaks loose.
~feels like it's more about the way sockets are passed around in child processes since it happens also on asyncio loop and only with reload~
sorry cant trigger on 3.7.5
summary:
3.7.5
no bug with 
C:/Users/benoi/.virtualenvs/uvicorn/Scripts/uvicorn-script.py toto:app --reload 
C:/Users/benoi/.virtualenvs/uvicorn/Scripts/uvicorn-script.py toto:app --reload --loop=asyncio
bug with
C:/Users/benoi/.virtualenvs/uvicorn/Scripts/uvicorn-script.py toto:app --reload --loop=iocp

3.8
bug with all
C:/Users/benoi/.virtualenvs/uvicorn3.8/Scripts/uvicorn-script.py toto:app --reload
C:/Users/benoi/.virtualenvs/uvicorn3.8/Scripts/uvicorn-script.py toto:app --reload --loop=asyncio
C:/Users/benoi/.virtualenvs/uvicorn3.8/Scripts/uvicorn-script.py toto:app --reload --loop=iocp


note also that 
https://github.com/encode/uvicorn/blob/a75fe1381f6b1f78901691c71894f3cf487b5d30/uvicorn/subprocess.py#L33-L36
and
https://github.com/encode/uvicorn/blob/a75fe1381f6b1f78901691c71894f3cf487b5d30/uvicorn/subprocess.py#L65-L66

can be replaced by `multiprocessing.allow_connection_pickling()` which doesnt solve the 3.8 bug but simplifies things

> 3.8 - bug with all

Indeed. The problem is that `loop=asyncio` will use the *default* loop policy for asyncio.
On windows that's IOCP under 3.8, which appears to have issues with sharing sockets between processes.

To resolve this we should:

* Drop our IOCP support completely.
* Force the loop policy to `SelectorEventLoop` on Windows with 3.8+. https://docs.python.org/3/library/asyncio-eventloop.html?highlight=iocp#asyncio.SelectorEventLoop 

Resolving patch:
diff --git a/uvicorn/config.py b/uvicorn/config.py
--- a/uvicorn/config.py
+++ b/uvicorn/config.py
@@ -48,7 +48,6 @@
     "auto": "uvicorn.loops.auto:auto_loop_setup",
     "asyncio": "uvicorn.loops.asyncio:asyncio_setup",
     "uvloop": "uvicorn.loops.uvloop:uvloop_setup",
-    "iocp": "uvicorn.loops.iocp:iocp_setup",
 }
 INTERFACES = ["auto", "asgi3", "asgi2", "wsgi"]
 
diff --git a/uvicorn/loops/asyncio.py b/uvicorn/loops/asyncio.py
--- a/uvicorn/loops/asyncio.py
+++ b/uvicorn/loops/asyncio.py
@@ -1,6 +1,18 @@
 import asyncio
+import platform
+import selectors
+import sys
 
 
 def asyncio_setup():
-    loop = asyncio.new_event_loop()
-    asyncio.set_event_loop(loop)
+    if (
+        sys.version_info.major >= 3
+        and sys.version_info.minor >= 8
+        and platform.system() == "Windows"
+    ):
+        selector = selectors.SelectSelector()
+        loop = asyncio.SelectorEventLoop(selector)
+        asyncio.set_event_loop(loop)
+    else:
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
diff --git a/uvicorn/loops/iocp.py b/uvicorn/loops/iocp.py
deleted file mode 100644
--- a/uvicorn/loops/iocp.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import asyncio
-import sys
-
-
-def iocp_setup():
-    if sys.version_info.major >= 3 and sys.version_info.minor >= 7:
-        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
-    else:
-        asyncio.set_event_loop(asyncio.ProactorEventLoop())


Test patch: