prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  file-system race condition while testing with mkdocs serve
  when mkdocs serve is shutting down, it cleans up after itself removing the temporary directory created earlier.
  
  the condition whether the directory needs to be removed is unchecked and prone to a file system race condition (shared resource).
  
  given the directory is already removed on the file-system while shutting down, mkdocs serve throws an exception and exits in failure.
  
  this error can be safely defined out of existence by removing the temporary directory only if it exists, will suggest a PR for this report. /E: created #2421
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `mkdocs/commands/serve.py`
  Content:
  ```
  1 import logging
  2 import shutil
  3 import tempfile
  4 
  5 from os.path import isfile, join
  6 from mkdocs.commands.build import build
  7 from mkdocs.config import load_config
  8 from mkdocs.exceptions import Abort
  9 from mkdocs.livereload import LiveReloadServer
  10 
  11 log = logging.getLogger(__name__)
  12 
  13 
  14 def serve(config_file=None, dev_addr=None, strict=None, theme=None,
  15           theme_dir=None, livereload='livereload', watch_theme=False, **kwargs):
  16     """
  17     Start the MkDocs development server
  18 
  19     By default it will serve the documentation on http://localhost:8000/ and
  20     it will rebuild the documentation and refresh the page automatically
  21     whenever a file is edited.
  22     """
  23 
  24     # Create a temporary build directory, and set some options to serve it
  25     # PY2 returns a byte string by default. The Unicode prefix ensures a Unicode
  26     # string is returned. And it makes MkDocs temp dirs easier to identify.
  27     site_dir = tempfile.mkdtemp(prefix='mkdocs_')
  28 
  29     def builder():
  30         log.info("Building documentation...")
  31         config = load_config(
  32             config_file=config_file,
  33             dev_addr=dev_addr,
  34             strict=strict,
  35             theme=theme,
  36             theme_dir=theme_dir,
  37             site_dir=site_dir,
  38             **kwargs
  39         )
  40         # Override a few config settings after validation
  41         config['site_url'] = 'http://{}/'.format(config['dev_addr'])
  42 
  43         live_server = livereload in ['dirty', 'livereload']
  44         dirty = livereload == 'dirty'
  45         build(config, live_server=live_server, dirty=dirty)
  46         return config
  47 
  48     try:
  49         # Perform the initial build
  50         config = builder()
  51 
  52         host, port = config['dev_addr']
  53 
  54         server = LiveReloadServer(builder=builder, host=host, port=port, root=site_dir)
  55 
  56         def error_handler(code):
  57             if code in (404, 500):
  58                 error_page = join(site_dir, f'{code}.html')
  59                 if isfile(error_page):
  60                     with open(error_page, 'rb') as f:
  61                         return f.read()
  62 
  63         server.error_handler = error_handler
  64 
  65         if livereload in ['livereload', 'dirty']:
  66             # Watch the documentation files, the config file and the theme files.
  67             server.watch(config['docs_dir'])
  68             server.watch(config['config_file_path'])
  69 
  70             if watch_theme:
  71                 for d in config['theme'].dirs:
  72                     server.watch(d)
  73 
  74             # Run `serve` plugin events.
  75             server = config['plugins'].run_event('serve', server, config=config, builder=builder)
  76 
  77         try:
  78             server.serve()
  79         except KeyboardInterrupt:
  80             log.info("Shutting down...")
  81         finally:
  82             server.shutdown()
  83     except OSError as e:  # pragma: no cover
  84         # Avoid ugly, unhelpful traceback
  85         raise Abort(str(e))
  86     finally:
  87         shutil.rmtree(site_dir)
  88
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/mkdocs/commands/serve.py b/mkdocs/commands/serve.py
  --- a/mkdocs/commands/serve.py
  +++ b/mkdocs/commands/serve.py
  @@ -2,7 +2,7 @@
   import shutil
   import tempfile
   
  -from os.path import isfile, join
  +from os.path import isdir, isfile, join
   from mkdocs.commands.build import build
   from mkdocs.config import load_config
   from mkdocs.exceptions import Abort
  @@ -84,4 +84,5 @@
           # Avoid ugly, unhelpful traceback
           raise Abort(str(e))
       finally:
  -        shutil.rmtree(site_dir)
  +        if isdir(site_dir):
  +            shutil.rmtree(site_dir)
  
verification_info: {"golden_diff": "diff --git a/mkdocs/commands/serve.py b/mkdocs/commands/serve.py\n--- a/mkdocs/commands/serve.py\n+++ b/mkdocs/commands/serve.py\n@@ -2,7 +2,7 @@\n import shutil\n import tempfile\n \n-from os.path import isfile, join\n+from os.path import isdir, isfile, join\n from mkdocs.commands.build import build\n from mkdocs.config import load_config\n from mkdocs.exceptions import Abort\n@@ -84,4 +84,5 @@\n         # Avoid ugly, unhelpful traceback\n         raise Abort(str(e))\n     finally:\n-        shutil.rmtree(site_dir)\n+        if isdir(site_dir):\n+            shutil.rmtree(site_dir)\n", "issue": "file-system race condition while testing with mkdocs serve\nwhen mkdocs serve is shutting down, it cleans up after itself removing the temporary directory created earlier.\r\n\r\nthe condition whether the directory needs to be removed is unchecked and prone to a file system race condition (shared resource).\r\n\r\ngiven the directory is already removed on the file-system while shutting down, mkdocs serve throws an exception and exits in failure.\r\n\r\nthis error can be safely defined out of existence by removing the temporary directory only if it exists, will suggest a PR for this report. /E: created #2421\n", "before_files": [{"content": "import logging\nimport shutil\nimport tempfile\n\nfrom os.path import isfile, join\nfrom mkdocs.commands.build import build\nfrom mkdocs.config import load_config\nfrom mkdocs.exceptions import Abort\nfrom mkdocs.livereload import LiveReloadServer\n\nlog = logging.getLogger(__name__)\n\n\ndef serve(config_file=None, dev_addr=None, strict=None, theme=None,\n          theme_dir=None, livereload='livereload', watch_theme=False, **kwargs):\n    \"\"\"\n    Start the MkDocs development server\n\n    By default it will serve the documentation on http://localhost:8000/ and\n    it will rebuild the documentation and refresh the page automatically\n    whenever a file is edited.\n    \"\"\"\n\n    # Create a temporary build directory, and set some options to serve it\n    # PY2 returns a byte string by default. The Unicode prefix ensures a Unicode\n    # string is returned. And it makes MkDocs temp dirs easier to identify.\n    site_dir = tempfile.mkdtemp(prefix='mkdocs_')\n\n    def builder():\n        log.info(\"Building documentation...\")\n        config = load_config(\n            config_file=config_file,\n            dev_addr=dev_addr,\n            strict=strict,\n            theme=theme,\n            theme_dir=theme_dir,\n            site_dir=site_dir,\n            **kwargs\n        )\n        # Override a few config settings after validation\n        config['site_url'] = 'http://{}/'.format(config['dev_addr'])\n\n        live_server = livereload in ['dirty', 'livereload']\n        dirty = livereload == 'dirty'\n        build(config, live_server=live_server, dirty=dirty)\n        return config\n\n    try:\n        # Perform the initial build\n        config = builder()\n\n        host, port = config['dev_addr']\n\n        server = LiveReloadServer(builder=builder, host=host, port=port, root=site_dir)\n\n        def error_handler(code):\n            if code in (404, 500):\n                error_page = join(site_dir, f'{code}.html')\n                if isfile(error_page):\n                    with open(error_page, 'rb') as f:\n                        return f.read()\n\n        server.error_handler = error_handler\n\n        if livereload in ['livereload', 'dirty']:\n            # Watch the documentation files, the config file and the theme files.\n            server.watch(config['docs_dir'])\n            server.watch(config['config_file_path'])\n\n            if watch_theme:\n                for d in config['theme'].dirs:\n                    server.watch(d)\n\n            # Run `serve` plugin events.\n            server = config['plugins'].run_event('serve', server, config=config, builder=builder)\n\n        try:\n            server.serve()\n        except KeyboardInterrupt:\n            log.info(\"Shutting down...\")\n        finally:\n            server.shutdown()\n    except OSError as e:  # pragma: no cover\n        # Avoid ugly, unhelpful traceback\n        raise Abort(str(e))\n    finally:\n        shutil.rmtree(site_dir)\n", "path": "mkdocs/commands/serve.py"}], "after_files": [{"content": "import logging\nimport shutil\nimport tempfile\n\nfrom os.path import isdir, isfile, join\nfrom mkdocs.commands.build import build\nfrom mkdocs.config import load_config\nfrom mkdocs.exceptions import Abort\nfrom mkdocs.livereload import LiveReloadServer\n\nlog = logging.getLogger(__name__)\n\n\ndef serve(config_file=None, dev_addr=None, strict=None, theme=None,\n          theme_dir=None, livereload='livereload', watch_theme=False, **kwargs):\n    \"\"\"\n    Start the MkDocs development server\n\n    By default it will serve the documentation on http://localhost:8000/ and\n    it will rebuild the documentation and refresh the page automatically\n    whenever a file is edited.\n    \"\"\"\n\n    # Create a temporary build directory, and set some options to serve it\n    # PY2 returns a byte string by default. The Unicode prefix ensures a Unicode\n    # string is returned. And it makes MkDocs temp dirs easier to identify.\n    site_dir = tempfile.mkdtemp(prefix='mkdocs_')\n\n    def builder():\n        log.info(\"Building documentation...\")\n        config = load_config(\n            config_file=config_file,\n            dev_addr=dev_addr,\n            strict=strict,\n            theme=theme,\n            theme_dir=theme_dir,\n            site_dir=site_dir,\n            **kwargs\n        )\n        # Override a few config settings after validation\n        config['site_url'] = 'http://{}/'.format(config['dev_addr'])\n\n        live_server = livereload in ['dirty', 'livereload']\n        dirty = livereload == 'dirty'\n        build(config, live_server=live_server, dirty=dirty)\n        return config\n\n    try:\n        # Perform the initial build\n        config = builder()\n\n        host, port = config['dev_addr']\n\n        server = LiveReloadServer(builder=builder, host=host, port=port, root=site_dir)\n\n        def error_handler(code):\n            if code in (404, 500):\n                error_page = join(site_dir, f'{code}.html')\n                if isfile(error_page):\n                    with open(error_page, 'rb') as f:\n                        return f.read()\n\n        server.error_handler = error_handler\n\n        if livereload in ['livereload', 'dirty']:\n            # Watch the documentation files, the config file and the theme files.\n            server.watch(config['docs_dir'])\n            server.watch(config['config_file_path'])\n\n            if watch_theme:\n                for d in config['theme'].dirs:\n                    server.watch(d)\n\n            # Run `serve` plugin events.\n            server = config['plugins'].run_event('serve', server, config=config, builder=builder)\n\n        try:\n            server.serve()\n        except KeyboardInterrupt:\n            log.info(\"Shutting down...\")\n        finally:\n            server.shutdown()\n    except OSError as e:  # pragma: no cover\n        # Avoid ugly, unhelpful traceback\n        raise Abort(str(e))\n    finally:\n        if isdir(site_dir):\n            shutil.rmtree(site_dir)\n", "path": "mkdocs/commands/serve.py"}]}