We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
gunicorn keeps booting workers when exceptions raised at startup events
Hi, I have the same issues  when deploy FastAPI using gunicorn + uvicorn，it keeps booting workers when exceptions raised at startup events.

**main.py**

```python
from fastapi import FastAPI

app = FastAPI()


@app.on_event("startup")
async def startup():
    raise Exception("error")


@app.get("/")
async def hello():
    return {"msg": "hello world"}
```

**gunicorn.conf.py**

```python
import json
import os

worker_class = "uvicorn.workers.UvicornWorker"

host = os.getenv("HOST", "0.0.0.0")
port = os.getenv("PORT", "8000")
bind_env = os.getenv("BIND", None)
use_loglevel = os.getenv("LOG_LEVEL", "info")
if bind_env:
    use_bind = bind_env
else:
    use_bind = f"{host}:{port}"


accesslog_var = os.getenv("ACCESS_LOG", "-")
use_accesslog = accesslog_var or None
errorlog_var = os.getenv("ERROR_LOG", "-")
use_errorlog = errorlog_var or None
graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120")
timeout_str = os.getenv("TIMEOUT", "120")
keepalive_str = os.getenv("KEEP_ALIVE", "5")

# Gunicorn config variables
loglevel = use_loglevel
bind = use_bind
errorlog = use_errorlog
accesslog = use_accesslog
graceful_timeout = int(graceful_timeout_str)
timeout = int(timeout_str)
keepalive = int(keepalive_str)

# For debugging and testing
log_data = {
    "loglevel": loglevel,
    "bind": bind,
    "graceful_timeout": graceful_timeout,
    "timeout": timeout,
    "keepalive": keepalive,
    "errorlog": errorlog,
    "accesslog": accesslog,
}
print(json.dumps(log_data))
```

**gunicorn log**

```text
{"loglevel": "info", "bind": "0.0.0.0:8000", "graceful_timeout": 120, "timeout": 120, "keepalive": 5, "errorlog": "-", "accesslog": "-", "host": "0.0.0.0", "port": "8000"}
[2021-06-01 21:13:05 +0800] [40946] [INFO] Starting gunicorn 20.0.4
[2021-06-01 21:13:05 +0800] [40946] [INFO] Listening at: http://0.0.0.0:8000 (40946)
[2021-06-01 21:13:05 +0800] [40946] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2021-06-01 21:13:05 +0800] [40949] [INFO] Booting worker with pid: 40949
[2021-06-01 21:13:05 +0800] [40949] [INFO] Started server process [40949]
[2021-06-01 21:13:05 +0800] [40949] [INFO] Waiting for application startup.
[2021-06-01 21:13:05 +0800] [40949] [ERROR] Traceback (most recent call last):
  File "./envs/test/lib/python3.7/site-packages/starlette/routing.py", line 526, in lifespan
    async for item in self.lifespan_context(app):
  File "./envs/test/lib/python3.7/site-packages/starlette/routing.py", line 467, in default_lifespan
    await self.startup()
  File "./envs/test/lib/python3.7/site-packages/starlette/routing.py", line 502, in startup
    await handler()
  File "./app/main.py", line 16, in startup
    raise Exception("error")
Exception: error

[2021-06-01 21:13:05 +0800] [40949] [ERROR] Application startup failed. Exiting.
[2021-06-01 21:13:05 +0800] [40949] [INFO] Worker exiting (pid: 40949)
[2021-06-01 21:13:05 +0800] [40950] [INFO] Booting worker with pid: 40950
[2021-06-01 21:13:06 +0800] [40950] [INFO] Started server process [40950]
[2021-06-01 21:13:06 +0800] [40950] [INFO] Waiting for application startup.
[2021-06-01 21:13:06 +0800] [40950] [ERROR] Traceback (most recent call last):
  File "./envs/test/lib/python3.7/site-packages/starlette/routing.py", line 526, in lifespan
    async for item in self.lifespan_context(app):
  File "./envs/test/lib/python3.7/site-packages/starlette/routing.py", line 467, in default_lifespan
    await self.startup()
  File "./envs/test/lib/python3.7/site-packages/starlette/routing.py", line 502, in startup
    await handler()
  File "./app/main.py", line 16, in startup
    raise Exception("error")
Exception: error

[2021-06-01 21:13:06 +0800] [40950] [ERROR] Application startup failed. Exiting.
```

I hope the gunicorn would exit when exceptions raised, but it keeps booting workers. Can someone helps me, thanks?

_Originally posted by @fanchunke1991 in https://github.com/encode/uvicorn/issues/1031#issuecomment-852121760_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `uvicorn/workers.py`
Content:
```
1 import asyncio
2 import logging
3 import signal
4 from typing import Any
5 
6 from gunicorn.workers.base import Worker
7 
8 from uvicorn.config import Config
9 from uvicorn.main import Server
10 
11 
12 class UvicornWorker(Worker):
13     """
14     A worker class for Gunicorn that interfaces with an ASGI consumer callable,
15     rather than a WSGI callable.
16     """
17 
18     CONFIG_KWARGS = {"loop": "auto", "http": "auto"}
19 
20     def __init__(self, *args: Any, **kwargs: Any) -> None:
21         super(UvicornWorker, self).__init__(*args, **kwargs)
22 
23         logger = logging.getLogger("uvicorn.error")
24         logger.handlers = self.log.error_log.handlers
25         logger.setLevel(self.log.error_log.level)
26         logger.propagate = False
27 
28         logger = logging.getLogger("uvicorn.access")
29         logger.handlers = self.log.access_log.handlers
30         logger.setLevel(self.log.access_log.level)
31         logger.propagate = False
32 
33         config_kwargs = {
34             "app": None,
35             "log_config": None,
36             "timeout_keep_alive": self.cfg.keepalive,
37             "timeout_notify": self.timeout,
38             "callback_notify": self.callback_notify,
39             "limit_max_requests": self.max_requests,
40             "forwarded_allow_ips": self.cfg.forwarded_allow_ips,
41         }
42 
43         if self.cfg.is_ssl:
44             ssl_kwargs = {
45                 "ssl_keyfile": self.cfg.ssl_options.get("keyfile"),
46                 "ssl_certfile": self.cfg.ssl_options.get("certfile"),
47                 "ssl_keyfile_password": self.cfg.ssl_options.get("password"),
48                 "ssl_version": self.cfg.ssl_options.get("ssl_version"),
49                 "ssl_cert_reqs": self.cfg.ssl_options.get("cert_reqs"),
50                 "ssl_ca_certs": self.cfg.ssl_options.get("ca_certs"),
51                 "ssl_ciphers": self.cfg.ssl_options.get("ciphers"),
52             }
53             config_kwargs.update(ssl_kwargs)
54 
55         if self.cfg.settings["backlog"].value:
56             config_kwargs["backlog"] = self.cfg.settings["backlog"].value
57 
58         config_kwargs.update(self.CONFIG_KWARGS)
59 
60         self.config = Config(**config_kwargs)
61 
62     def init_process(self) -> None:
63         self.config.setup_event_loop()
64         super(UvicornWorker, self).init_process()
65 
66     def init_signals(self) -> None:
67         # Reset signals so Gunicorn doesn't swallow subprocess return codes
68         # other signals are set up by Server.install_signal_handlers()
69         # See: https://github.com/encode/uvicorn/issues/894
70         for s in self.SIGNALS:
71             signal.signal(s, signal.SIG_DFL)
72 
73     def run(self) -> None:
74         self.config.app = self.wsgi
75         server = Server(config=self.config)
76         loop = asyncio.get_event_loop()
77         loop.run_until_complete(server.serve(sockets=self.sockets))
78 
79     async def callback_notify(self) -> None:
80         self.notify()
81 
82 
83 class UvicornH11Worker(UvicornWorker):
84     CONFIG_KWARGS = {"loop": "asyncio", "http": "h11"}
85
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/uvicorn/workers.py b/uvicorn/workers.py
--- a/uvicorn/workers.py
+++ b/uvicorn/workers.py
@@ -1,8 +1,10 @@
 import asyncio
 import logging
 import signal
+import sys
 from typing import Any
 
+from gunicorn.arbiter import Arbiter
 from gunicorn.workers.base import Worker
 
 from uvicorn.config import Config
@@ -75,6 +77,11 @@
         server = Server(config=self.config)
         loop = asyncio.get_event_loop()
         loop.run_until_complete(server.serve(sockets=self.sockets))
+        # Exit with status 3 when worker starts failed, so Gunicorn
+        # can shut it down to avoid infinite start/stop cycles.
+        # See: https://github.com/encode/uvicorn/issues/1066
+        if not server.started:
+            sys.exit(Arbiter.WORKER_BOOT_ERROR)
 
     async def callback_notify(self) -> None:
         self.notify()

{"golden_diff": "diff --git a/uvicorn/workers.py b/uvicorn/workers.py\n--- a/uvicorn/workers.py\n+++ b/uvicorn/workers.py\n@@ -1,8 +1,10 @@\n import asyncio\n import logging\n import signal\n+import sys\n from typing import Any\n \n+from gunicorn.arbiter import Arbiter\n from gunicorn.workers.base import Worker\n \n from uvicorn.config import Config\n@@ -75,6 +77,11 @@\n         server = Server(config=self.config)\n         loop = asyncio.get_event_loop()\n         loop.run_until_complete(server.serve(sockets=self.sockets))\n+        # Exit with status 3 when worker starts failed, so Gunicorn\n+        # can shut it down to avoid infinite start/stop cycles.\n+        # See: https://github.com/encode/uvicorn/issues/1066\n+        if not server.started:\n+            sys.exit(Arbiter.WORKER_BOOT_ERROR)\n \n     async def callback_notify(self) -> None:\n         self.notify()\n", "issue": "gunicorn keeps booting workers when exceptions raised at startup events\nHi, I have the same issues  when deploy FastAPI using gunicorn + uvicorn\uff0cit keeps booting workers when exceptions raised at startup events.\r\n\r\n**main.py**\r\n\r\n```python\r\nfrom fastapi import FastAPI\r\n\r\napp = FastAPI()\r\n\r\n\r\n@app.on_event(\"startup\")\r\nasync def startup():\r\n    raise Exception(\"error\")\r\n\r\n\r\n@app.get(\"/\")\r\nasync def hello():\r\n    return {\"msg\": \"hello world\"}\r\n```\r\n\r\n**gunicorn.conf.py**\r\n\r\n```python\r\nimport json\r\nimport os\r\n\r\nworker_class = \"uvicorn.workers.UvicornWorker\"\r\n\r\nhost = os.getenv(\"HOST\", \"0.0.0.0\")\r\nport = os.getenv(\"PORT\", \"8000\")\r\nbind_env = os.getenv(\"BIND\", None)\r\nuse_loglevel = os.getenv(\"LOG_LEVEL\", \"info\")\r\nif bind_env:\r\n    use_bind = bind_env\r\nelse:\r\n    use_bind = f\"{host}:{port}\"\r\n\r\n\r\naccesslog_var = os.getenv(\"ACCESS_LOG\", \"-\")\r\nuse_accesslog = accesslog_var or None\r\nerrorlog_var = os.getenv(\"ERROR_LOG\", \"-\")\r\nuse_errorlog = errorlog_var or None\r\ngraceful_timeout_str = os.getenv(\"GRACEFUL_TIMEOUT\", \"120\")\r\ntimeout_str = os.getenv(\"TIMEOUT\", \"120\")\r\nkeepalive_str = os.getenv(\"KEEP_ALIVE\", \"5\")\r\n\r\n# Gunicorn config variables\r\nloglevel = use_loglevel\r\nbind = use_bind\r\nerrorlog = use_errorlog\r\naccesslog = use_accesslog\r\ngraceful_timeout = int(graceful_timeout_str)\r\ntimeout = int(timeout_str)\r\nkeepalive = int(keepalive_str)\r\n\r\n# For debugging and testing\r\nlog_data = {\r\n    \"loglevel\": loglevel,\r\n    \"bind\": bind,\r\n    \"graceful_timeout\": graceful_timeout,\r\n    \"timeout\": timeout,\r\n    \"keepalive\": keepalive,\r\n    \"errorlog\": errorlog,\r\n    \"accesslog\": accesslog,\r\n}\r\nprint(json.dumps(log_data))\r\n```\r\n\r\n**gunicorn log**\r\n\r\n```text\r\n{\"loglevel\": \"info\", \"bind\": \"0.0.0.0:8000\", \"graceful_timeout\": 120, \"timeout\": 120, \"keepalive\": 5, \"errorlog\": \"-\", \"accesslog\": \"-\", \"host\": \"0.0.0.0\", \"port\": \"8000\"}\r\n[2021-06-01 21:13:05 +0800] [40946] [INFO] Starting gunicorn 20.0.4\r\n[2021-06-01 21:13:05 +0800] [40946] [INFO] Listening at: http://0.0.0.0:8000 (40946)\r\n[2021-06-01 21:13:05 +0800] [40946] [INFO] Using worker: uvicorn.workers.UvicornWorker\r\n[2021-06-01 21:13:05 +0800] [40949] [INFO] Booting worker with pid: 40949\r\n[2021-06-01 21:13:05 +0800] [40949] [INFO] Started server process [40949]\r\n[2021-06-01 21:13:05 +0800] [40949] [INFO] Waiting for application startup.\r\n[2021-06-01 21:13:05 +0800] [40949] [ERROR] Traceback (most recent call last):\r\n  File \"./envs/test/lib/python3.7/site-packages/starlette/routing.py\", line 526, in lifespan\r\n    async for item in self.lifespan_context(app):\r\n  File \"./envs/test/lib/python3.7/site-packages/starlette/routing.py\", line 467, in default_lifespan\r\n    await self.startup()\r\n  File \"./envs/test/lib/python3.7/site-packages/starlette/routing.py\", line 502, in startup\r\n    await handler()\r\n  File \"./app/main.py\", line 16, in startup\r\n    raise Exception(\"error\")\r\nException: error\r\n\r\n[2021-06-01 21:13:05 +0800] [40949] [ERROR] Application startup failed. Exiting.\r\n[2021-06-01 21:13:05 +0800] [40949] [INFO] Worker exiting (pid: 40949)\r\n[2021-06-01 21:13:05 +0800] [40950] [INFO] Booting worker with pid: 40950\r\n[2021-06-01 21:13:06 +0800] [40950] [INFO] Started server process [40950]\r\n[2021-06-01 21:13:06 +0800] [40950] [INFO] Waiting for application startup.\r\n[2021-06-01 21:13:06 +0800] [40950] [ERROR] Traceback (most recent call last):\r\n  File \"./envs/test/lib/python3.7/site-packages/starlette/routing.py\", line 526, in lifespan\r\n    async for item in self.lifespan_context(app):\r\n  File \"./envs/test/lib/python3.7/site-packages/starlette/routing.py\", line 467, in default_lifespan\r\n    await self.startup()\r\n  File \"./envs/test/lib/python3.7/site-packages/starlette/routing.py\", line 502, in startup\r\n    await handler()\r\n  File \"./app/main.py\", line 16, in startup\r\n    raise Exception(\"error\")\r\nException: error\r\n\r\n[2021-06-01 21:13:06 +0800] [40950] [ERROR] Application startup failed. Exiting.\r\n```\r\n\r\nI hope the gunicorn would exit when exceptions raised, but it keeps booting workers. Can someone helps me, thanks?\r\n\r\n_Originally posted by @fanchunke1991 in https://github.com/encode/uvicorn/issues/1031#issuecomment-852121760_\n", "before_files": [{"content": "import asyncio\nimport logging\nimport signal\nfrom typing import Any\n\nfrom gunicorn.workers.base import Worker\n\nfrom uvicorn.config import Config\nfrom uvicorn.main import Server\n\n\nclass UvicornWorker(Worker):\n    \"\"\"\n    A worker class for Gunicorn that interfaces with an ASGI consumer callable,\n    rather than a WSGI callable.\n    \"\"\"\n\n    CONFIG_KWARGS = {\"loop\": \"auto\", \"http\": \"auto\"}\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        super(UvicornWorker, self).__init__(*args, **kwargs)\n\n        logger = logging.getLogger(\"uvicorn.error\")\n        logger.handlers = self.log.error_log.handlers\n        logger.setLevel(self.log.error_log.level)\n        logger.propagate = False\n\n        logger = logging.getLogger(\"uvicorn.access\")\n        logger.handlers = self.log.access_log.handlers\n        logger.setLevel(self.log.access_log.level)\n        logger.propagate = False\n\n        config_kwargs = {\n            \"app\": None,\n            \"log_config\": None,\n            \"timeout_keep_alive\": self.cfg.keepalive,\n            \"timeout_notify\": self.timeout,\n            \"callback_notify\": self.callback_notify,\n            \"limit_max_requests\": self.max_requests,\n            \"forwarded_allow_ips\": self.cfg.forwarded_allow_ips,\n        }\n\n        if self.cfg.is_ssl:\n            ssl_kwargs = {\n                \"ssl_keyfile\": self.cfg.ssl_options.get(\"keyfile\"),\n                \"ssl_certfile\": self.cfg.ssl_options.get(\"certfile\"),\n                \"ssl_keyfile_password\": self.cfg.ssl_options.get(\"password\"),\n                \"ssl_version\": self.cfg.ssl_options.get(\"ssl_version\"),\n                \"ssl_cert_reqs\": self.cfg.ssl_options.get(\"cert_reqs\"),\n                \"ssl_ca_certs\": self.cfg.ssl_options.get(\"ca_certs\"),\n                \"ssl_ciphers\": self.cfg.ssl_options.get(\"ciphers\"),\n            }\n            config_kwargs.update(ssl_kwargs)\n\n        if self.cfg.settings[\"backlog\"].value:\n            config_kwargs[\"backlog\"] = self.cfg.settings[\"backlog\"].value\n\n        config_kwargs.update(self.CONFIG_KWARGS)\n\n        self.config = Config(**config_kwargs)\n\n    def init_process(self) -> None:\n        self.config.setup_event_loop()\n        super(UvicornWorker, self).init_process()\n\n    def init_signals(self) -> None:\n        # Reset signals so Gunicorn doesn't swallow subprocess return codes\n        # other signals are set up by Server.install_signal_handlers()\n        # See: https://github.com/encode/uvicorn/issues/894\n        for s in self.SIGNALS:\n            signal.signal(s, signal.SIG_DFL)\n\n    def run(self) -> None:\n        self.config.app = self.wsgi\n        server = Server(config=self.config)\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(server.serve(sockets=self.sockets))\n\n    async def callback_notify(self) -> None:\n        self.notify()\n\n\nclass UvicornH11Worker(UvicornWorker):\n    CONFIG_KWARGS = {\"loop\": \"asyncio\", \"http\": \"h11\"}\n", "path": "uvicorn/workers.py"}], "after_files": [{"content": "import asyncio\nimport logging\nimport signal\nimport sys\nfrom typing import Any\n\nfrom gunicorn.arbiter import Arbiter\nfrom gunicorn.workers.base import Worker\n\nfrom uvicorn.config import Config\nfrom uvicorn.main import Server\n\n\nclass UvicornWorker(Worker):\n    \"\"\"\n    A worker class for Gunicorn that interfaces with an ASGI consumer callable,\n    rather than a WSGI callable.\n    \"\"\"\n\n    CONFIG_KWARGS = {\"loop\": \"auto\", \"http\": \"auto\"}\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        super(UvicornWorker, self).__init__(*args, **kwargs)\n\n        logger = logging.getLogger(\"uvicorn.error\")\n        logger.handlers = self.log.error_log.handlers\n        logger.setLevel(self.log.error_log.level)\n        logger.propagate = False\n\n        logger = logging.getLogger(\"uvicorn.access\")\n        logger.handlers = self.log.access_log.handlers\n        logger.setLevel(self.log.access_log.level)\n        logger.propagate = False\n\n        config_kwargs = {\n            \"app\": None,\n            \"log_config\": None,\n            \"timeout_keep_alive\": self.cfg.keepalive,\n            \"timeout_notify\": self.timeout,\n            \"callback_notify\": self.callback_notify,\n            \"limit_max_requests\": self.max_requests,\n            \"forwarded_allow_ips\": self.cfg.forwarded_allow_ips,\n        }\n\n        if self.cfg.is_ssl:\n            ssl_kwargs = {\n                \"ssl_keyfile\": self.cfg.ssl_options.get(\"keyfile\"),\n                \"ssl_certfile\": self.cfg.ssl_options.get(\"certfile\"),\n                \"ssl_keyfile_password\": self.cfg.ssl_options.get(\"password\"),\n                \"ssl_version\": self.cfg.ssl_options.get(\"ssl_version\"),\n                \"ssl_cert_reqs\": self.cfg.ssl_options.get(\"cert_reqs\"),\n                \"ssl_ca_certs\": self.cfg.ssl_options.get(\"ca_certs\"),\n                \"ssl_ciphers\": self.cfg.ssl_options.get(\"ciphers\"),\n            }\n            config_kwargs.update(ssl_kwargs)\n\n        if self.cfg.settings[\"backlog\"].value:\n            config_kwargs[\"backlog\"] = self.cfg.settings[\"backlog\"].value\n\n        config_kwargs.update(self.CONFIG_KWARGS)\n\n        self.config = Config(**config_kwargs)\n\n    def init_process(self) -> None:\n        self.config.setup_event_loop()\n        super(UvicornWorker, self).init_process()\n\n    def init_signals(self) -> None:\n        # Reset signals so Gunicorn doesn't swallow subprocess return codes\n        # other signals are set up by Server.install_signal_handlers()\n        # See: https://github.com/encode/uvicorn/issues/894\n        for s in self.SIGNALS:\n            signal.signal(s, signal.SIG_DFL)\n\n    def run(self) -> None:\n        self.config.app = self.wsgi\n        server = Server(config=self.config)\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(server.serve(sockets=self.sockets))\n        # Exit with status 3 when worker starts failed, so Gunicorn\n        # can shut it down to avoid infinite start/stop cycles.\n        # See: https://github.com/encode/uvicorn/issues/1066\n        if not server.started:\n            sys.exit(Arbiter.WORKER_BOOT_ERROR)\n\n    async def callback_notify(self) -> None:\n        self.notify()\n\n\nclass UvicornH11Worker(UvicornWorker):\n    CONFIG_KWARGS = {\"loop\": \"asyncio\", \"http\": \"h11\"}\n", "path": "uvicorn/workers.py"}]}