try:
    import asyncio
except ImportError:
    raise RuntimeError("This example requries Python3 / asyncio")

from threading import Thread

from flask import Flask, render_template
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.embed import server_document
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
from bokeh.server.server import BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets
from bokeh.themes import Theme

if __name__ == '__main__':
    print('This script is intended to be run with gunicorn. e.g.')
    print()
    print('    gunicorn -w 4 flask_gunicorn_embed:app')
    print()
    print('will start the app on four processes')
    import sys
    sys.exit()


app = Flask(__name__)

def bkapp(doc):
    df = sea_surface_temperature.copy()
    source = ColumnDataSource(data=df)

    plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
                  title="Sea Surface Temperature at 43.18, -70.43")
    plot.line('time', 'temperature', source=source)

    def callback(attr, old, new):
        if new == 0:
            data = df
        else:
            data = df.rolling('{0}D'.format(new)).mean()
        source.data = ColumnDataSource.from_df(data)

    slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
    slider.on_change('value', callback)

    doc.add_root(column(slider, plot))

    doc.theme = Theme(filename="theme.yaml")

# can't use shortcuts here, since we are passing to low level BokehTornado
bkapp = Application(FunctionHandler(bkapp))

# This is so that if this app is run using something like "gunicorn -w 4" then
# each process will listen on its own port
sockets, port = bind_sockets("localhost", 0)

@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://localhost:%d/bkapp' % port)
    return render_template("embed.html", script=script, template="Flask")

def bk_worker():
    asyncio.set_event_loop(asyncio.new_event_loop())

    bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["localhost:8000"])
    bokeh_http = HTTPServer(bokeh_tornado)
    bokeh_http.add_sockets(sockets)

    server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)
    server.start()
    server.io_loop.start()

Thread(target=bk_worker).start()

examples/howto/server_embed/flask_gunicorn_embed.py
try:
    import asyncio
except ImportError:
    raise RuntimeError("This example requries Python3 / asyncio")

from threading import Thread

from flask import Flask, render_template
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.embed import server_document
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
from bokeh.server.server import BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets
from bokeh.themes import Theme

if __name__ == '__main__':
    print('This script is intended to be run with gunicorn. e.g.')
    print()
    print('    gunicorn -w 4 flask_gunicorn_embed:app')
    print()
    print('will start the app on four processes')
    import sys
    sys.exit()


app = Flask(__name__)

def bkapp(doc):
    df = sea_surface_temperature.copy()
    source = ColumnDataSource(data=df)

    plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
                  title="Sea Surface Temperature at 43.18, -70.43")
    plot.line('time', 'temperature', source=source)

    def callback(attr, old, new):
        if new == 0:
            data = df
        else:
            data = df.rolling('{0}D'.format(new)).mean()
        source.data = ColumnDataSource.from_df(data)

    slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
    slider.on_change('value', callback)

    doc.add_root(column(slider, plot))

    doc.theme = Theme(filename="theme.yaml")

# can't use shortcuts here, since we are passing to low level BokehTornado
bkapp = Application(FunctionHandler(bkapp))

# This is so that if this app is run using something like "gunicorn -w 4" then
# each process will listen on its own port
sockets, port = bind_sockets("localhost", 0)

@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://localhost:%d/bkapp' % port)
    return render_template("embed.html", script=script, template="Flask")

def bk_worker():
    asyncio.set_event_loop(asyncio.new_event_loop())

    bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["localhost:8000"])
    bokeh_http = HTTPServer(bokeh_tornado)
    bokeh_http.add_sockets(sockets)

    server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)
    server.start()
    server.io_loop.start()

t = Thread(target=bk_worker)
t.daemon = True
t.start()

examples/howto/server_embed/flask_gunicorn_embed.py
diff --git a/examples/howto/server_embed/flask_gunicorn_embed.py b/examples/howto/server_embed/flask_gunicorn_embed.py
index 4b64f3ba086..b6537eb6b09 100644
--- a/examples/howto/server_embed/flask_gunicorn_embed.py
+++ b/examples/howto/server_embed/flask_gunicorn_embed.py
@@ -78,4 +78,6 @@ def bk_worker():
     server.start()
     server.io_loop.start()
 
-Thread(target=bk_worker).start()
+t = Thread(target=bk_worker)
+t.daemon = True
+t.start()

[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

