| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 from compiled_file_system import CompiledFileSystem | 5 from compiled_file_system import CompiledFileSystem |
| 6 from file_system import FileNotFoundError | 6 from file_system import FileNotFoundError |
| 7 from future import Gettable, Future | 7 from future import Gettable, Future |
| 8 | 8 |
| 9 | 9 |
| 10 class ChainedCompiledFileSystem(object): | 10 class ChainedCompiledFileSystem(object): |
| 11 ''' A CompiledFileSystem implementation that fetches data from a chain of | 11 '''A CompiledFileSystem implementation that fetches data from a chain of |
| 12 CompiledFileSystems that have different file systems and separate cache | 12 possible FileSystems. The chain consists of some number of FileSystems which |
| 13 namespaces. | 13 may have cached data for their CompiledFileSystem instances (injected on |
| 14 Factory construction) + the main FileSystem (injected at Creation time). |
| 14 | 15 |
| 15 The rules for the compiled file system chain are: | 16 The expected configuration is that the main FileSystem is a PatchedFileSystem |
| 16 - Versions are fetched from the first compiled file system's underlying | 17 and the chain the FileSystem which it patches, but with file systems |
| 17 file system. | 18 constructed via the HostFileSystemIterator the main FileSystems could be |
| 18 - Each compiled file system is read in the reverse order (the last one is | 19 anything. |
| 19 read first). If the version matches, return the data. Otherwise, read | |
| 20 from the previous compiled file system until the first one is read. | |
| 21 | 20 |
| 22 It is used to chain compiled file systems whose underlying file systems are | 21 This slightly unusual configuration is primarily needed to avoid re-compiling |
| 23 slightly different. This makes it possible to reuse cached compiled data in | 22 data for PatchedFileSystems, which are very similar to the FileSystem which |
| 24 one of them without recompiling everything that is shared by them. | 23 they patch. Re-compiling data is expensive and a waste of memory resources. |
| 24 ChainedCompiledFileSystem shares the data. |
| 25 ''' | 25 ''' |
| 26 class Factory(CompiledFileSystem.Factory): | 26 class Factory(CompiledFileSystem.Factory): |
| 27 def __init__(self, | 27 def __init__(self, file_system_chain, object_store): |
| 28 factory_and_fs_chain): | 28 self._file_system_chain = file_system_chain |
| 29 self._factory_and_fs_chain = factory_and_fs_chain | 29 self._object_store = object_store |
| 30 | 30 |
| 31 def Create(self, populate_function, cls, category=None): | 31 def Create(self, file_system, populate_function, cls, category=None): |
| 32 return ChainedCompiledFileSystem( | 32 return ChainedCompiledFileSystem(tuple( |
| 33 [(factory.Create(populate_function, cls, category), fs) | 33 CompiledFileSystem.Factory(self._object_store).Create( |
| 34 for factory, fs in self._factory_and_fs_chain]) | 34 fs, populate_function, cls, category=category) |
| 35 for fs in [file_system] + self._file_system_chain)) |
| 35 | 36 |
| 36 def __init__(self, compiled_fs_chain): | 37 def __init__(self, compiled_fs_chain): |
| 37 '''|compiled_fs_chain| is a list of tuples (compiled_fs, file_system). | 38 '''|compiled_fs_chain| is a list of tuples (compiled_fs, file_system). |
| 38 ''' | 39 ''' |
| 39 assert len(compiled_fs_chain) > 0 | 40 assert len(compiled_fs_chain) > 0 |
| 40 self._compiled_fs_chain = compiled_fs_chain | 41 self._compiled_fs_chain = compiled_fs_chain |
| 41 | 42 |
| 42 def GetFromFile(self, path, binary=False): | 43 def GetFromFile(self, path, binary=False): |
| 43 return self._GetImpl( | 44 return self._GetImpl( |
| 44 path, | 45 path, |
| 45 lambda compiled_fs: compiled_fs.GetFromFile(path, binary=binary), | 46 lambda compiled_fs: compiled_fs.GetFromFile(path, binary=binary), |
| 46 lambda compiled_fs: compiled_fs.StatFile(path)) | 47 lambda compiled_fs: compiled_fs.GetFileVersion(path)) |
| 47 | 48 |
| 48 def GetFromFileListing(self, path): | 49 def GetFromFileListing(self, path): |
| 49 if not path.endswith('/'): | 50 if not path.endswith('/'): |
| 50 path += '/' | 51 path += '/' |
| 51 return self._GetImpl( | 52 return self._GetImpl( |
| 52 path, | 53 path, |
| 53 lambda compiled_fs: compiled_fs.GetFromFileListing(path), | 54 lambda compiled_fs: compiled_fs.GetFromFileListing(path), |
| 54 lambda compiled_fs: compiled_fs.StatFileListing(path)) | 55 lambda compiled_fs: compiled_fs.GetFileListingVersion(path)) |
| 55 | 56 |
| 56 def _GetImpl(self, path, reader, statter): | 57 def _GetImpl(self, path, reader, version_getter): |
| 57 # It's possible that a new file is added in the first compiled file system | 58 # Strategy: Get the current version of |path| in main FileSystem, then run |
| 58 # and it doesn't exist in other compiled file systems. | 59 # through |_compiled_fs_chain| in *reverse* to find the "oldest" FileSystem |
| 60 # with an up-to-date version of that file. |
| 61 # |
| 62 # Obviously, if files have been added in the main FileSystem then none of |
| 63 # the older FileSystems will be able to find it. |
| 59 read_futures = [(reader(compiled_fs), compiled_fs) | 64 read_futures = [(reader(compiled_fs), compiled_fs) |
| 60 for compiled_fs, _ in self._compiled_fs_chain] | 65 for compiled_fs in self._compiled_fs_chain] |
| 61 | 66 |
| 62 def resolve(): | 67 def resolve(): |
| 63 try: | 68 try: |
| 64 first_compiled_fs, first_file_system = self._compiled_fs_chain[0] | 69 first_compiled_fs = self._compiled_fs_chain[0] |
| 65 # The first file system contains both files of a newer version and | 70 # The first file system contains both files of a newer version and |
| 66 # files shared with other compiled file systems. We are going to try | 71 # files shared with other compiled file systems. We are going to try |
| 67 # each compiled file system in the reverse order and return the data | 72 # each compiled file system in the reverse order and return the data |
| 68 # when version matches. Data cached in other compiled file system will | 73 # when version matches. Data cached in other compiled file system will |
| 69 # be reused whenever possible so that we don't need to recompile things | 74 # be reused whenever possible so that we don't need to recompile things |
| 70 # that are not changed across these file systems. | 75 # that are not changed across these file systems. |
| 71 version = first_file_system.Stat(path).version | 76 first_version = version_getter(first_compiled_fs) |
| 72 for read_future, compiled_fs in reversed(read_futures): | 77 for read_future, compiled_fs in reversed(read_futures): |
| 73 if statter(compiled_fs) == version: | 78 if version_getter(compiled_fs) == first_version: |
| 74 return read_future.Get() | 79 return read_future.Get() |
| 75 except FileNotFoundError: | 80 except FileNotFoundError: |
| 76 pass | 81 pass |
| 77 # Try an arbitrary operation again to generate a realistic stack trace. | 82 # Try an arbitrary operation again to generate a realistic stack trace. |
| 78 return read_futures[0][0].Get() | 83 return read_futures[0][0].Get() |
| 79 | 84 |
| 80 return Future(delegate=Gettable(resolve)) | 85 return Future(delegate=Gettable(resolve)) |
| OLD | NEW |