    def __init__(
        self,
        filename: Union[str, PathLike],
        *,
        map_size: int = 1024 * 1024 * 1024 * 1024,
        read_only: bool = False,
    ) -> None:
        """
        Creates a `TensorCache` by either opening an existing one on disk, or creating
        a new one. Its interface is almost exactly like a Python dictionary, where the
        keys are strings and the values are `torch.Tensor`.

        Parameters
        ----------
        filename: `str`
            Path to the location of the cache
        map_size: `int`, optional, defaults to 1TB
            This is the maximum size the cache will ever grow to. On reasonable operating
            systems, there is no penalty to making this a large value.
            `TensorCache` uses a memory-mapped file to store the data. When the file is
            first opened, we have to give the maximum size it can ever grow to. This is
            that number. Reasonable operating systems don't actually allocate that space
            until it is really needed.
        """
        self.lmdb_env: lmdb.Environment
        if hasattr(self, "lmdb_env"):
            # We're being initialized again after a cache hit in _active_tensor_caches, thanks
            # to __new__. In this case, we may have to upgrade to read/write, but other than
            # that we are good to go.
            if read_only:
                return
            if not self.read_only:
                return

            # Upgrade a read-only lmdb env to a read/write lmdb env.
            filename = self.lmdb_env.path()
            old_info = self.lmdb_env.info()

            self.lmdb_env.close()
            self.lmdb_env = lmdb.open(
                filename,
                map_size=old_info["map_size"],
                subdir=False,
                metasync=False,
                sync=True,
                readahead=False,
                meminit=False,
                readonly=False,
                lock=True,
            )
        else:
            filename = str(filename)

            cpu_count = os.cpu_count() or 1
            if os.path.exists(filename):
                if os.path.isfile(filename):
                    # If the file is not writable, set read_only to True, but issue a warning.
                    if not os.access(filename, os.W_OK):
                        if not read_only:
                            warnings.warn(
                                f"File '{filename}' is read-only, so cache will be read-only",
                                UserWarning,
                            )
                        read_only = True
                else:
                    # If it's not a file, raise an error.
                    raise ValueError("Expect a file, found a directory instead")

            use_lock = True
            if read_only:
                # Check if the lock file is writable. If it's not, then we won't be able to use the lock.

                # This is always how lmdb names the lock file.
                lock_filename = filename + "-lock"
                if os.path.isfile(lock_filename):
                    use_lock = os.access(lock_filename, os.W_OK)
                else:
                    # If the lock file doesn't exist yet, then the directory needs to be writable in
                    # order to create and use the lock file.
                    use_lock = os.access(os.path.dirname(lock_filename), os.W_OK)

            if not use_lock:
                warnings.warn(
                    f"Lacking permissions to use lock file on cache '{filename}'.\nUse at your own risk!",
                    UserWarning,
                )

            self.lmdb_env = lmdb.open(
                filename,
                subdir=False,
                map_size=map_size,
                max_readers=cpu_count * 4,
                max_spare_txns=cpu_count * 4,
                metasync=False,
                sync=True,
                readahead=False,
                meminit=False,
                readonly=read_only,
                lock=use_lock,
            )
            _active_tensor_caches[_unique_file_id(filename)] = self

            # We have another cache here that makes sure we return the same object for the same key. Without it,
            # you would get a different tensor, using different memory, every time you call __getitem__(), even
            # if you call it with the same key.
            # The downside is that we can't keep self.cache_cache up to date when multiple processes modify the
            # cache at the same time. We can guarantee though that it is up to date as long as processes either
            # write new values, or read existing ones.
            self.cache_cache: MutableMapping[str, Tensor] = WeakValueDictionary()
Creates a `TensorCache` by either opening an existing one on disk, or creating
a new one. Its interface is almost exactly like a Python dictionary, where the
keys are strings and the values are `torch.Tensor`.

Parameters
----------
filename: `str`
Path to the location of the cache
map_size: `int`, optional, defaults to 1TB
This is the maximum size the cache will ever grow to. On reasonable operating
systems, there is no penalty to making this a large value.
`TensorCache` uses a memory-mapped file to store the data. When the file is
first opened, we have to give the maximum size it can ever grow to. This is
that number. Reasonable operating systems don't actually allocate that space
until it is really needed.
allennlp/common/file_utils.py