# Buggy code
def serve(
    panels,
    port=0,
    address=None,
    websocket_origin=None,
    loop=None,
    show=True,
    start=True,
    title=None,
    verbose=True,
    location=True,
    **kwargs,
):
    """
    Allows serving one or more panel objects on a single server.
    The panels argument should be either a Panel object or a function
    returning a Panel object or a dictionary of these two. If a
    dictionary is supplied the keys represent the slugs at which
    each app is served, e.g. `serve({'app': panel1, 'app2': panel2})`
    will serve apps at /app and /app2 on the server.

    Arguments
    ---------
    panel: Viewable, function or {str: Viewable or function}
      A Panel object, a function returning a Panel object or a
      dictionary mapping from the URL slug to either.
    port: int (optional, default=0)
      Allows specifying a specific port
    address : str
      The address the server should listen on for HTTP requests.
    websocket_origin: str or list(str) (optional)
      A list of hosts that can connect to the websocket.

      This is typically required when embedding a server app in
      an external web site.

      If None, "localhost" is used.
    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())
      The tornado IOLoop to run the Server on
    show : boolean (optional, default=False)
      Whether to open the server in a new browser tab on start
    start : boolean(optional, default=False)
      Whether to start the Server
    title: str or {str: str} (optional, default=None)
      An HTML title for the application or a dictionary mapping
      from the URL slug to a customized title
    verbose: boolean (optional, default=True)
      Whether to print the address and port
    location : boolean or panel.io.location.Location
      Whether to create a Location component to observe and
      set the URL location.
    kwargs: dict
      Additional keyword arguments to pass to Server instance
    """
    return get_server(
        panels,
        port,
        address,
        websocket_origin,
        loop,
        show,
        start,
        title,
        verbose,
        location,
        **kwargs,
    )


# Traceback (RuntimeError)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-15-da8b0df4fb70> in <module>
----> 1 color_mapper.update(high=100)

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/has_props.py in update(self, **kwargs)
368         '''
369         for k,v in kwargs.items():
--> 370             setattr(self, k, v)
371
372     def update_from_json(self, json_attributes, models=None, setter=None):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/has_props.py in __setattr__(self, name, value)
272
273         if name in props or (descriptor is not None and descriptor.fset is not None):
--> 274             super().__setattr__(name, value)
275         else:
276             matches, text = difflib.get_close_matches(name.lower(), props), "similar"

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in __set__(self, obj, value, setter)
537             raise RuntimeError("%s.%s is a readonly property" % (obj.__class__.__name__, self.name))
538
--> 539         self._internal_set(obj, value, setter=setter)
540
541     def __delete__(self, obj):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in _internal_set(self, obj, value, hint, setter)
761
762         old = self.__get__(obj, obj.__class__)
--> 763         self._real_set(obj, old, value, hint=hint, setter=setter)
764
765     def _real_set(self, obj, old, value, hint=None, setter=None):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in _real_set(self, obj, old, value, hint, setter)
830
831         # for notification purposes, "old" should be the logical old
--> 832         self._trigger(obj, old, value, hint=hint, setter=setter)
833
834     # called when a container is mutated "behind our back" and

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in _trigger(self, obj, old, value, hint, setter)
907         '''
908         if hasattr(obj, 'trigger'):
--> 909             obj.trigger(self.name, old, value, hint, setter)
910
911

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/model.py in trigger(self, attr, old, new, hint, setter)
659                     self._document._invalidate_all_models()
660         # chain up to invoke callbacks
--> 661         super().trigger(attr, old, new, hint=hint, setter=setter)
662
663     def _attach_document(self, doc):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/util/callback_manager.py in trigger(self, attr, old, new, hint, setter)
155                     callback(attr, old, new)
156         if hasattr(self, '_document') and self._document is not None:
--> 157             self._document._notify_change(self, attr, old, new, hint, setter, invoke)
158         else:
159             invoke()

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in _notify_change(self, model, attr, old, new, hint, setter, callback_invoker)
1040
1041         event = ModelChangedEvent(self, model, attr, old, new, serializable_new, hint, setter, callback_invoker)
-> 1042         self._trigger_on_change(event)
1043
1044     def _push_all_models_freeze(self):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in _trigger_on_change(self, event)
1135             for cb in self._callbacks.values():
1136                 cb(event)
-> 1137         self._with_self_as_curdoc(invoke_callbacks)
1138
1139     def _with_self_as_curdoc(self, f):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in _with_self_as_curdoc(self, f)
1148             else:
1149                 set_curdoc(self)
-> 1150             return f()
1151         finally:
1152             set_curdoc(old_doc)

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in invoke_callbacks()
1134         def invoke_callbacks():
1135             for cb in self._callbacks.values():
-> 1136                 cb(event)
1137         self._with_self_as_curdoc(invoke_callbacks)
1138

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in <lambda>(event)
702     def on_change_dispatch_to(self, receiver):
703         if not receiver in self._callbacks:
--> 704             self._callbacks[receiver] = lambda event: event.dispatch(receiver)
705
706     def on_session_destroyed(self, *callbacks):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/events.py in dispatch(self, receiver)
267
268         '''
--> 269         super().dispatch(receiver)
270         if hasattr(receiver, '_document_model_changed'):
271             receiver._document_model_changed(self)

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/events.py in dispatch(self, receiver)
122         super().dispatch(receiver)
123         if hasattr(receiver, '_document_patched'):
--> 124             receiver._document_patched(self)
125
126     def generate(self, references, buffers):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/server/session.py in _document_patched(self, event)
216
217         if self._pending_writes is None:
--> 218             raise RuntimeError("_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes")
219
220         # TODO (havocp): our "change sync" protocol is flawed because if both

RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes

# Fixed code
def serve(
    panels,
    port=0,
    address=None,
    websocket_origin=None,
    loop=None,
    show=True,
    start=True,
    title=None,
    verbose=True,
    location=True,
    threaded=False,
    **kwargs,
):
    """
    Allows serving one or more panel objects on a single server.
    The panels argument should be either a Panel object or a function
    returning a Panel object or a dictionary of these two. If a
    dictionary is supplied the keys represent the slugs at which
    each app is served, e.g. `serve({'app': panel1, 'app2': panel2})`
    will serve apps at /app and /app2 on the server.

    Arguments
    ---------
    panel: Viewable, function or {str: Viewable or function}
      A Panel object, a function returning a Panel object or a
      dictionary mapping from the URL slug to either.
    port: int (optional, default=0)
      Allows specifying a specific port
    address : str
      The address the server should listen on for HTTP requests.
    websocket_origin: str or list(str) (optional)
      A list of hosts that can connect to the websocket.

      This is typically required when embedding a server app in
      an external web site.

      If None, "localhost" is used.
    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())
      The tornado IOLoop to run the Server on
    show : boolean (optional, default=False)
      Whether to open the server in a new browser tab on start
    start : boolean(optional, default=False)
      Whether to start the Server
    title: str or {str: str} (optional, default=None)
      An HTML title for the application or a dictionary mapping
      from the URL slug to a customized title
    verbose: boolean (optional, default=True)
      Whether to print the address and port
    location : boolean or panel.io.location.Location
      Whether to create a Location component to observe and
      set the URL location.
    threaded: boolean (default=False)
      Whether to start the server on a new Thread
    kwargs: dict
      Additional keyword arguments to pass to Server instance
    """
    kwargs = dict(
        kwargs,
        **dict(
            port=port,
            address=address,
            websocket_origin=websocket_origin,
            loop=loop,
            show=show,
            start=start,
            title=title,
            verbose=verbose,
            location=location,
        ),
    )
    if threaded:
        from tornado.ioloop import IOLoop

        kwargs["loop"] = loop = IOLoop() if loop is None else loop
        server = StoppableThread(
            target=get_server, io_loop=loop, args=(panels,), kwargs=kwargs
        )
        server.start()
    else:
        server = get_server(panels, **kwargs)
    return server