We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Non-daemon worker thread prevents gunicorn from shutting down cleanly.
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh HEAD e605297
gunicorn (version 20.0.4)
Python 3.7.4
macOS 10.14.6

#### Description of expected behavior and the observed behavior
I am learning about embedding Bokeh in a Flask project and tried the sample script flask_gunicorn_embed.py from the macOS terminal.  After viewing the working web page in Safari, I then pressed Ctrl-C in the terminal to stop the gunicorn server.  The expected behaviour was a clean shutdown of gunicorn, but instead it hangs.

Marking the bk_worker thread as a daemon before starting it resolves the hang.

#### Stack traceback and/or browser JavaScript console output
greent7@avocado:~/development/bokeh/examples/howto/server_embed$ BOKEH_ALLOW_WS_ORIGIN=127.0.0.1:8000 gunicorn -w 4 flask_gunicorn_embed:app
[2019-11-29 01:06:31 -0700] [53812] [INFO] Starting gunicorn 20.0.4
[2019-11-29 01:06:31 -0700] [53812] [INFO] Listening at: http://127.0.0.1:8000 (53812)
[2019-11-29 01:06:31 -0700] [53812] [INFO] Using worker: sync
[2019-11-29 01:06:31 -0700] [53815] [INFO] Booting worker with pid: 53815
[2019-11-29 01:06:32 -0700] [53816] [INFO] Booting worker with pid: 53816
[2019-11-29 01:06:32 -0700] [53817] [INFO] Booting worker with pid: 53817
[2019-11-29 01:06:32 -0700] [53818] [INFO] Booting worker with pid: 53818
^C[2019-11-29 01:06:33 -0700] [53812] [INFO] Handling signal: int
[2019-11-29 01:06:33 -0700] [53818] [INFO] Worker exiting (pid: 53818)
[2019-11-29 01:06:33 -0700] [53815] [INFO] Worker exiting (pid: 53815)
[2019-11-29 01:06:33 -0700] [53817] [INFO] Worker exiting (pid: 53817)
[2019-11-29 01:06:33 -0700] [53816] [INFO] Worker exiting (pid: 53816)

If I hit Ctrl-C again, it continues and exits noisily:

^CException ignored in: <module 'threading' from '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1308, in _shutdown
    lock.acquire()
  File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 196, in handle_quit
    sys.exit(0)
SystemExit: 0
[2019-11-29 01:06:56 -0700] [53812] [INFO] Shutting down: Master

[BUG] Non-daemon worker thread prevents gunicorn from shutting down cleanly.
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh HEAD e605297
gunicorn (version 20.0.4)
Python 3.7.4
macOS 10.14.6

#### Description of expected behavior and the observed behavior
I am learning about embedding Bokeh in a Flask project and tried the sample script flask_gunicorn_embed.py from the macOS terminal.  After viewing the working web page in Safari, I then pressed Ctrl-C in the terminal to stop the gunicorn server.  The expected behaviour was a clean shutdown of gunicorn, but instead it hangs.

Marking the bk_worker thread as a daemon before starting it resolves the hang.

#### Stack traceback and/or browser JavaScript console output
greent7@avocado:~/development/bokeh/examples/howto/server_embed$ BOKEH_ALLOW_WS_ORIGIN=127.0.0.1:8000 gunicorn -w 4 flask_gunicorn_embed:app
[2019-11-29 01:06:31 -0700] [53812] [INFO] Starting gunicorn 20.0.4
[2019-11-29 01:06:31 -0700] [53812] [INFO] Listening at: http://127.0.0.1:8000 (53812)
[2019-11-29 01:06:31 -0700] [53812] [INFO] Using worker: sync
[2019-11-29 01:06:31 -0700] [53815] [INFO] Booting worker with pid: 53815
[2019-11-29 01:06:32 -0700] [53816] [INFO] Booting worker with pid: 53816
[2019-11-29 01:06:32 -0700] [53817] [INFO] Booting worker with pid: 53817
[2019-11-29 01:06:32 -0700] [53818] [INFO] Booting worker with pid: 53818
^C[2019-11-29 01:06:33 -0700] [53812] [INFO] Handling signal: int
[2019-11-29 01:06:33 -0700] [53818] [INFO] Worker exiting (pid: 53818)
[2019-11-29 01:06:33 -0700] [53815] [INFO] Worker exiting (pid: 53815)
[2019-11-29 01:06:33 -0700] [53817] [INFO] Worker exiting (pid: 53817)
[2019-11-29 01:06:33 -0700] [53816] [INFO] Worker exiting (pid: 53816)

If I hit Ctrl-C again, it continues and exits noisily:

^CException ignored in: <module 'threading' from '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1308, in _shutdown
    lock.acquire()
  File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 196, in handle_quit
    sys.exit(0)
SystemExit: 0
[2019-11-29 01:06:56 -0700] [53812] [INFO] Shutting down: Master


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `examples/howto/server_embed/flask_gunicorn_embed.py`
Content:
```
1 try:
2     import asyncio
3 except ImportError:
4     raise RuntimeError("This example requries Python3 / asyncio")
5 
6 from threading import Thread
7 
8 from flask import Flask, render_template
9 from tornado.httpserver import HTTPServer
10 from tornado.ioloop import IOLoop
11 
12 from bokeh.application import Application
13 from bokeh.application.handlers import FunctionHandler
14 from bokeh.embed import server_document
15 from bokeh.layouts import column
16 from bokeh.models import ColumnDataSource, Slider
17 from bokeh.plotting import figure
18 from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
19 from bokeh.server.server import BaseServer
20 from bokeh.server.tornado import BokehTornado
21 from bokeh.server.util import bind_sockets
22 from bokeh.themes import Theme
23 
24 if __name__ == '__main__':
25     print('This script is intended to be run with gunicorn. e.g.')
26     print()
27     print('    gunicorn -w 4 flask_gunicorn_embed:app')
28     print()
29     print('will start the app on four processes')
30     import sys
31     sys.exit()
32 
33 
34 app = Flask(__name__)
35 
36 def bkapp(doc):
37     df = sea_surface_temperature.copy()
38     source = ColumnDataSource(data=df)
39 
40     plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
41                   title="Sea Surface Temperature at 43.18, -70.43")
42     plot.line('time', 'temperature', source=source)
43 
44     def callback(attr, old, new):
45         if new == 0:
46             data = df
47         else:
48             data = df.rolling('{0}D'.format(new)).mean()
49         source.data = ColumnDataSource.from_df(data)
50 
51     slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
52     slider.on_change('value', callback)
53 
54     doc.add_root(column(slider, plot))
55 
56     doc.theme = Theme(filename="theme.yaml")
57 
58 # can't use shortcuts here, since we are passing to low level BokehTornado
59 bkapp = Application(FunctionHandler(bkapp))
60 
61 # This is so that if this app is run using something like "gunicorn -w 4" then
62 # each process will listen on its own port
63 sockets, port = bind_sockets("localhost", 0)
64 
65 @app.route('/', methods=['GET'])
66 def bkapp_page():
67     script = server_document('http://localhost:%d/bkapp' % port)
68     return render_template("embed.html", script=script, template="Flask")
69 
70 def bk_worker():
71     asyncio.set_event_loop(asyncio.new_event_loop())
72 
73     bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["localhost:8000"])
74     bokeh_http = HTTPServer(bokeh_tornado)
75     bokeh_http.add_sockets(sockets)
76 
77     server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)
78     server.start()
79     server.io_loop.start()
80 
81 Thread(target=bk_worker).start()
82
```
--- 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/examples/howto/server_embed/flask_gunicorn_embed.py b/examples/howto/server_embed/flask_gunicorn_embed.py
--- a/examples/howto/server_embed/flask_gunicorn_embed.py
+++ b/examples/howto/server_embed/flask_gunicorn_embed.py
@@ -78,4 +78,6 @@
     server.start()
     server.io_loop.start()
 
-Thread(target=bk_worker).start()
+t = Thread(target=bk_worker)
+t.daemon = True
+t.start()

{"golden_diff": "diff --git a/examples/howto/server_embed/flask_gunicorn_embed.py b/examples/howto/server_embed/flask_gunicorn_embed.py\n--- a/examples/howto/server_embed/flask_gunicorn_embed.py\n+++ b/examples/howto/server_embed/flask_gunicorn_embed.py\n@@ -78,4 +78,6 @@\n     server.start()\n     server.io_loop.start()\n \n-Thread(target=bk_worker).start()\n+t = Thread(target=bk_worker)\n+t.daemon = True\n+t.start()\n", "issue": "[BUG] Non-daemon worker thread prevents gunicorn from shutting down cleanly.\n#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)\r\nbokeh HEAD e605297\r\ngunicorn (version 20.0.4)\r\nPython 3.7.4\r\nmacOS 10.14.6\r\n\r\n#### Description of expected behavior and the observed behavior\r\nI am learning about embedding Bokeh in a Flask project and tried the sample script flask_gunicorn_embed.py from the macOS terminal.  After viewing the working web page in Safari, I then pressed Ctrl-C in the terminal to stop the gunicorn server.  The expected behaviour was a clean shutdown of gunicorn, but instead it hangs.\r\n\r\nMarking the bk_worker thread as a daemon before starting it resolves the hang.\r\n\r\n#### Stack traceback and/or browser JavaScript console output\r\ngreent7@avocado:~/development/bokeh/examples/howto/server_embed$ BOKEH_ALLOW_WS_ORIGIN=127.0.0.1:8000 gunicorn -w 4 flask_gunicorn_embed:app\r\n[2019-11-29 01:06:31 -0700] [53812] [INFO] Starting gunicorn 20.0.4\r\n[2019-11-29 01:06:31 -0700] [53812] [INFO] Listening at: http://127.0.0.1:8000 (53812)\r\n[2019-11-29 01:06:31 -0700] [53812] [INFO] Using worker: sync\r\n[2019-11-29 01:06:31 -0700] [53815] [INFO] Booting worker with pid: 53815\r\n[2019-11-29 01:06:32 -0700] [53816] [INFO] Booting worker with pid: 53816\r\n[2019-11-29 01:06:32 -0700] [53817] [INFO] Booting worker with pid: 53817\r\n[2019-11-29 01:06:32 -0700] [53818] [INFO] Booting worker with pid: 53818\r\n^C[2019-11-29 01:06:33 -0700] [53812] [INFO] Handling signal: int\r\n[2019-11-29 01:06:33 -0700] [53818] [INFO] Worker exiting (pid: 53818)\r\n[2019-11-29 01:06:33 -0700] [53815] [INFO] Worker exiting (pid: 53815)\r\n[2019-11-29 01:06:33 -0700] [53817] [INFO] Worker exiting (pid: 53817)\r\n[2019-11-29 01:06:33 -0700] [53816] [INFO] Worker exiting (pid: 53816)\r\n\r\nIf I hit Ctrl-C again, it continues and exits noisily:\r\n\r\n^CException ignored in: <module 'threading' from '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>\r\nTraceback (most recent call last):\r\n  File \"/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py\", line 1308, in _shutdown\r\n    lock.acquire()\r\n  File \"/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py\", line 196, in handle_quit\r\n    sys.exit(0)\r\nSystemExit: 0\r\n[2019-11-29 01:06:56 -0700] [53812] [INFO] Shutting down: Master\r\n\n[BUG] Non-daemon worker thread prevents gunicorn from shutting down cleanly.\n#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)\r\nbokeh HEAD e605297\r\ngunicorn (version 20.0.4)\r\nPython 3.7.4\r\nmacOS 10.14.6\r\n\r\n#### Description of expected behavior and the observed behavior\r\nI am learning about embedding Bokeh in a Flask project and tried the sample script flask_gunicorn_embed.py from the macOS terminal.  After viewing the working web page in Safari, I then pressed Ctrl-C in the terminal to stop the gunicorn server.  The expected behaviour was a clean shutdown of gunicorn, but instead it hangs.\r\n\r\nMarking the bk_worker thread as a daemon before starting it resolves the hang.\r\n\r\n#### Stack traceback and/or browser JavaScript console output\r\ngreent7@avocado:~/development/bokeh/examples/howto/server_embed$ BOKEH_ALLOW_WS_ORIGIN=127.0.0.1:8000 gunicorn -w 4 flask_gunicorn_embed:app\r\n[2019-11-29 01:06:31 -0700] [53812] [INFO] Starting gunicorn 20.0.4\r\n[2019-11-29 01:06:31 -0700] [53812] [INFO] Listening at: http://127.0.0.1:8000 (53812)\r\n[2019-11-29 01:06:31 -0700] [53812] [INFO] Using worker: sync\r\n[2019-11-29 01:06:31 -0700] [53815] [INFO] Booting worker with pid: 53815\r\n[2019-11-29 01:06:32 -0700] [53816] [INFO] Booting worker with pid: 53816\r\n[2019-11-29 01:06:32 -0700] [53817] [INFO] Booting worker with pid: 53817\r\n[2019-11-29 01:06:32 -0700] [53818] [INFO] Booting worker with pid: 53818\r\n^C[2019-11-29 01:06:33 -0700] [53812] [INFO] Handling signal: int\r\n[2019-11-29 01:06:33 -0700] [53818] [INFO] Worker exiting (pid: 53818)\r\n[2019-11-29 01:06:33 -0700] [53815] [INFO] Worker exiting (pid: 53815)\r\n[2019-11-29 01:06:33 -0700] [53817] [INFO] Worker exiting (pid: 53817)\r\n[2019-11-29 01:06:33 -0700] [53816] [INFO] Worker exiting (pid: 53816)\r\n\r\nIf I hit Ctrl-C again, it continues and exits noisily:\r\n\r\n^CException ignored in: <module 'threading' from '/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>\r\nTraceback (most recent call last):\r\n  File \"/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py\", line 1308, in _shutdown\r\n    lock.acquire()\r\n  File \"/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py\", line 196, in handle_quit\r\n    sys.exit(0)\r\nSystemExit: 0\r\n[2019-11-29 01:06:56 -0700] [53812] [INFO] Shutting down: Master\r\n\n", "before_files": [{"content": "try:\n    import asyncio\nexcept ImportError:\n    raise RuntimeError(\"This example requries Python3 / asyncio\")\n\nfrom threading import Thread\n\nfrom flask import Flask, render_template\nfrom tornado.httpserver import HTTPServer\nfrom tornado.ioloop import IOLoop\n\nfrom bokeh.application import Application\nfrom bokeh.application.handlers import FunctionHandler\nfrom bokeh.embed import server_document\nfrom bokeh.layouts import column\nfrom bokeh.models import ColumnDataSource, Slider\nfrom bokeh.plotting import figure\nfrom bokeh.sampledata.sea_surface_temperature import sea_surface_temperature\nfrom bokeh.server.server import BaseServer\nfrom bokeh.server.tornado import BokehTornado\nfrom bokeh.server.util import bind_sockets\nfrom bokeh.themes import Theme\n\nif __name__ == '__main__':\n    print('This script is intended to be run with gunicorn. e.g.')\n    print()\n    print('    gunicorn -w 4 flask_gunicorn_embed:app')\n    print()\n    print('will start the app on four processes')\n    import sys\n    sys.exit()\n\n\napp = Flask(__name__)\n\ndef bkapp(doc):\n    df = sea_surface_temperature.copy()\n    source = ColumnDataSource(data=df)\n\n    plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',\n                  title=\"Sea Surface Temperature at 43.18, -70.43\")\n    plot.line('time', 'temperature', source=source)\n\n    def callback(attr, old, new):\n        if new == 0:\n            data = df\n        else:\n            data = df.rolling('{0}D'.format(new)).mean()\n        source.data = ColumnDataSource.from_df(data)\n\n    slider = Slider(start=0, end=30, value=0, step=1, title=\"Smoothing by N Days\")\n    slider.on_change('value', callback)\n\n    doc.add_root(column(slider, plot))\n\n    doc.theme = Theme(filename=\"theme.yaml\")\n\n# can't use shortcuts here, since we are passing to low level BokehTornado\nbkapp = Application(FunctionHandler(bkapp))\n\n# This is so that if this app is run using something like \"gunicorn -w 4\" then\n# each process will listen on its own port\nsockets, port = bind_sockets(\"localhost\", 0)\n\n@app.route('/', methods=['GET'])\ndef bkapp_page():\n    script = server_document('http://localhost:%d/bkapp' % port)\n    return render_template(\"embed.html\", script=script, template=\"Flask\")\n\ndef bk_worker():\n    asyncio.set_event_loop(asyncio.new_event_loop())\n\n    bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=[\"localhost:8000\"])\n    bokeh_http = HTTPServer(bokeh_tornado)\n    bokeh_http.add_sockets(sockets)\n\n    server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)\n    server.start()\n    server.io_loop.start()\n\nThread(target=bk_worker).start()\n", "path": "examples/howto/server_embed/flask_gunicorn_embed.py"}], "after_files": [{"content": "try:\n    import asyncio\nexcept ImportError:\n    raise RuntimeError(\"This example requries Python3 / asyncio\")\n\nfrom threading import Thread\n\nfrom flask import Flask, render_template\nfrom tornado.httpserver import HTTPServer\nfrom tornado.ioloop import IOLoop\n\nfrom bokeh.application import Application\nfrom bokeh.application.handlers import FunctionHandler\nfrom bokeh.embed import server_document\nfrom bokeh.layouts import column\nfrom bokeh.models import ColumnDataSource, Slider\nfrom bokeh.plotting import figure\nfrom bokeh.sampledata.sea_surface_temperature import sea_surface_temperature\nfrom bokeh.server.server import BaseServer\nfrom bokeh.server.tornado import BokehTornado\nfrom bokeh.server.util import bind_sockets\nfrom bokeh.themes import Theme\n\nif __name__ == '__main__':\n    print('This script is intended to be run with gunicorn. e.g.')\n    print()\n    print('    gunicorn -w 4 flask_gunicorn_embed:app')\n    print()\n    print('will start the app on four processes')\n    import sys\n    sys.exit()\n\n\napp = Flask(__name__)\n\ndef bkapp(doc):\n    df = sea_surface_temperature.copy()\n    source = ColumnDataSource(data=df)\n\n    plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',\n                  title=\"Sea Surface Temperature at 43.18, -70.43\")\n    plot.line('time', 'temperature', source=source)\n\n    def callback(attr, old, new):\n        if new == 0:\n            data = df\n        else:\n            data = df.rolling('{0}D'.format(new)).mean()\n        source.data = ColumnDataSource.from_df(data)\n\n    slider = Slider(start=0, end=30, value=0, step=1, title=\"Smoothing by N Days\")\n    slider.on_change('value', callback)\n\n    doc.add_root(column(slider, plot))\n\n    doc.theme = Theme(filename=\"theme.yaml\")\n\n# can't use shortcuts here, since we are passing to low level BokehTornado\nbkapp = Application(FunctionHandler(bkapp))\n\n# This is so that if this app is run using something like \"gunicorn -w 4\" then\n# each process will listen on its own port\nsockets, port = bind_sockets(\"localhost\", 0)\n\n@app.route('/', methods=['GET'])\ndef bkapp_page():\n    script = server_document('http://localhost:%d/bkapp' % port)\n    return render_template(\"embed.html\", script=script, template=\"Flask\")\n\ndef bk_worker():\n    asyncio.set_event_loop(asyncio.new_event_loop())\n\n    bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=[\"localhost:8000\"])\n    bokeh_http = HTTPServer(bokeh_tornado)\n    bokeh_http.add_sockets(sockets)\n\n    server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)\n    server.start()\n    server.io_loop.start()\n\nt = Thread(target=bk_worker)\nt.daemon = True\nt.start()\n", "path": "examples/howto/server_embed/flask_gunicorn_embed.py"}]}