prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Custom cache directory to be thread/process safe
  **Is your feature request related to a problem? Please describe.**
  
  The cache files are not thread/process safe. I've checked multiple projects simultaneously in different processes but get permission errors when one process is writing to the cache while another is reading from it. On linux the fix is pretty easy: set a different temp directory for each process using the ``XDG_CACHE_HOME`` environment variable but there is no equivalent for Windows (there's somewhat an equivalent but you need a rather [specific setup](https://github.com/platformdirs/platformdirs/blob/main/src/platformdirs/windows.py#L157-L165) to use the environment variable).
  
  **Describe the solution you'd like**
  
  The solution that will work for my use case (and I think is the least amount of changes) is to create a custom environment variable that overrides using platformdirs to get the cache directory. Think this could look like:
  ```python
  CACHE_DIR = Path(os.environ.get("BLACK_CACHE_DIR", user_cache_dir("black", version=__version__)))
  if not CACHE_DIR.exists():
      raise RuntimeError(f"{CACHE_DIR} does not exist")
  ```
  
  **Describe alternatives you've considered**
  
  1. Add the command line option to turn off cacheing (as this ticket as #248 asked for) (guess is this  more work)
  2. Add command line option to set the cache directory (guess is this is more work but not a lot)
  3. Make the cache dir thread/process safe (guess is this is more work)
  
  </issue>
  <code>
  [start of src/black/cache.py]
  1 """Caching of formatted files with feature-based invalidation."""
  2 
  3 import os
  4 import pickle
  5 from pathlib import Path
  6 import tempfile
  7 from typing import Dict, Iterable, Set, Tuple
  8 
  9 from platformdirs import user_cache_dir
  10 
  11 from black.mode import Mode
  12 
  13 from _black_version import version as __version__
  14 
  15 
  16 # types
  17 Timestamp = float
  18 FileSize = int
  19 CacheInfo = Tuple[Timestamp, FileSize]
  20 Cache = Dict[str, CacheInfo]
  21 
  22 
  23 CACHE_DIR = Path(user_cache_dir("black", version=__version__))
  24 
  25 
  26 def read_cache(mode: Mode) -> Cache:
  27     """Read the cache if it exists and is well formed.
  28 
  29     If it is not well formed, the call to write_cache later should resolve the issue.
  30     """
  31     cache_file = get_cache_file(mode)
  32     if not cache_file.exists():
  33         return {}
  34 
  35     with cache_file.open("rb") as fobj:
  36         try:
  37             cache: Cache = pickle.load(fobj)
  38         except (pickle.UnpicklingError, ValueError, IndexError):
  39             return {}
  40 
  41     return cache
  42 
  43 
  44 def get_cache_file(mode: Mode) -> Path:
  45     return CACHE_DIR / f"cache.{mode.get_cache_key()}.pickle"
  46 
  47 
  48 def get_cache_info(path: Path) -> CacheInfo:
  49     """Return the information used to check if a file is already formatted or not."""
  50     stat = path.stat()
  51     return stat.st_mtime, stat.st_size
  52 
  53 
  54 def filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]:
  55     """Split an iterable of paths in `sources` into two sets.
  56 
  57     The first contains paths of files that modified on disk or are not in the
  58     cache. The other contains paths to non-modified files.
  59     """
  60     todo, done = set(), set()
  61     for src in sources:
  62         res_src = src.resolve()
  63         if cache.get(str(res_src)) != get_cache_info(res_src):
  64             todo.add(src)
  65         else:
  66             done.add(src)
  67     return todo, done
  68 
  69 
  70 def write_cache(cache: Cache, sources: Iterable[Path], mode: Mode) -> None:
  71     """Update the cache file."""
  72     cache_file = get_cache_file(mode)
  73     try:
  74         CACHE_DIR.mkdir(parents=True, exist_ok=True)
  75         new_cache = {
  76             **cache,
  77             **{str(src.resolve()): get_cache_info(src) for src in sources},
  78         }
  79         with tempfile.NamedTemporaryFile(dir=str(cache_file.parent), delete=False) as f:
  80             pickle.dump(new_cache, f, protocol=4)
  81         os.replace(f.name, cache_file)
  82     except OSError:
  83         pass
  84
  [end of src/black/cache.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/src/black/cache.py b/src/black/cache.py
  --- a/src/black/cache.py
  +++ b/src/black/cache.py
  @@ -20,7 +20,23 @@
   Cache = Dict[str, CacheInfo]
   
   
  -CACHE_DIR = Path(user_cache_dir("black", version=__version__))
  +def get_cache_dir() -> Path:
  +    """Get the cache directory used by black.
  +
  +    Users can customize this directory on all systems using `BLACK_CACHE_DIR`
  +    environment variable. By default, the cache directory is the user cache directory
  +    under the black application.
  +
  +    This result is immediately set to a constant `black.cache.CACHE_DIR` as to avoid
  +    repeated calls.
  +    """
  +    # NOTE: Function mostly exists as a clean way to test getting the cache directory.
  +    default_cache_dir = user_cache_dir("black", version=__version__)
  +    cache_dir = Path(os.environ.get("BLACK_CACHE_DIR", default_cache_dir))
  +    return cache_dir
  +
  +
  +CACHE_DIR = get_cache_dir()
   
   
   def read_cache(mode: Mode) -> Cache:
  
verification_info: {"golden_diff": "diff --git a/src/black/cache.py b/src/black/cache.py\n--- a/src/black/cache.py\n+++ b/src/black/cache.py\n@@ -20,7 +20,23 @@\n Cache = Dict[str, CacheInfo]\n \n \n-CACHE_DIR = Path(user_cache_dir(\"black\", version=__version__))\n+def get_cache_dir() -> Path:\n+    \"\"\"Get the cache directory used by black.\n+\n+    Users can customize this directory on all systems using `BLACK_CACHE_DIR`\n+    environment variable. By default, the cache directory is the user cache directory\n+    under the black application.\n+\n+    This result is immediately set to a constant `black.cache.CACHE_DIR` as to avoid\n+    repeated calls.\n+    \"\"\"\n+    # NOTE: Function mostly exists as a clean way to test getting the cache directory.\n+    default_cache_dir = user_cache_dir(\"black\", version=__version__)\n+    cache_dir = Path(os.environ.get(\"BLACK_CACHE_DIR\", default_cache_dir))\n+    return cache_dir\n+\n+\n+CACHE_DIR = get_cache_dir()\n \n \n def read_cache(mode: Mode) -> Cache:\n", "issue": "Custom cache directory to be thread/process safe\n**Is your feature request related to a problem? Please describe.**\r\n\r\nThe cache files are not thread/process safe. I've checked multiple projects simultaneously in different processes but get permission errors when one process is writing to the cache while another is reading from it. On linux the fix is pretty easy: set a different temp directory for each process using the ``XDG_CACHE_HOME`` environment variable but there is no equivalent for Windows (there's somewhat an equivalent but you need a rather [specific setup](https://github.com/platformdirs/platformdirs/blob/main/src/platformdirs/windows.py#L157-L165) to use the environment variable).\r\n\r\n**Describe the solution you'd like**\r\n\r\nThe solution that will work for my use case (and I think is the least amount of changes) is to create a custom environment variable that overrides using platformdirs to get the cache directory. Think this could look like:\r\n```python\r\nCACHE_DIR = Path(os.environ.get(\"BLACK_CACHE_DIR\", user_cache_dir(\"black\", version=__version__)))\r\nif not CACHE_DIR.exists():\r\n    raise RuntimeError(f\"{CACHE_DIR} does not exist\")\r\n```\r\n\r\n**Describe alternatives you've considered**\r\n\r\n1. Add the command line option to turn off cacheing (as this ticket as #248 asked for) (guess is this  more work)\r\n2. Add command line option to set the cache directory (guess is this is more work but not a lot)\r\n3. Make the cache dir thread/process safe (guess is this is more work)\n", "before_files": [{"content": "\"\"\"Caching of formatted files with feature-based invalidation.\"\"\"\n\nimport os\nimport pickle\nfrom pathlib import Path\nimport tempfile\nfrom typing import Dict, Iterable, Set, Tuple\n\nfrom platformdirs import user_cache_dir\n\nfrom black.mode import Mode\n\nfrom _black_version import version as __version__\n\n\n# types\nTimestamp = float\nFileSize = int\nCacheInfo = Tuple[Timestamp, FileSize]\nCache = Dict[str, CacheInfo]\n\n\nCACHE_DIR = Path(user_cache_dir(\"black\", version=__version__))\n\n\ndef read_cache(mode: Mode) -> Cache:\n    \"\"\"Read the cache if it exists and is well formed.\n\n    If it is not well formed, the call to write_cache later should resolve the issue.\n    \"\"\"\n    cache_file = get_cache_file(mode)\n    if not cache_file.exists():\n        return {}\n\n    with cache_file.open(\"rb\") as fobj:\n        try:\n            cache: Cache = pickle.load(fobj)\n        except (pickle.UnpicklingError, ValueError, IndexError):\n            return {}\n\n    return cache\n\n\ndef get_cache_file(mode: Mode) -> Path:\n    return CACHE_DIR / f\"cache.{mode.get_cache_key()}.pickle\"\n\n\ndef get_cache_info(path: Path) -> CacheInfo:\n    \"\"\"Return the information used to check if a file is already formatted or not.\"\"\"\n    stat = path.stat()\n    return stat.st_mtime, stat.st_size\n\n\ndef filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]:\n    \"\"\"Split an iterable of paths in `sources` into two sets.\n\n    The first contains paths of files that modified on disk or are not in the\n    cache. The other contains paths to non-modified files.\n    \"\"\"\n    todo, done = set(), set()\n    for src in sources:\n        res_src = src.resolve()\n        if cache.get(str(res_src)) != get_cache_info(res_src):\n            todo.add(src)\n        else:\n            done.add(src)\n    return todo, done\n\n\ndef write_cache(cache: Cache, sources: Iterable[Path], mode: Mode) -> None:\n    \"\"\"Update the cache file.\"\"\"\n    cache_file = get_cache_file(mode)\n    try:\n        CACHE_DIR.mkdir(parents=True, exist_ok=True)\n        new_cache = {\n            **cache,\n            **{str(src.resolve()): get_cache_info(src) for src in sources},\n        }\n        with tempfile.NamedTemporaryFile(dir=str(cache_file.parent), delete=False) as f:\n            pickle.dump(new_cache, f, protocol=4)\n        os.replace(f.name, cache_file)\n    except OSError:\n        pass\n", "path": "src/black/cache.py"}]}