diff options
author | Benoit Lamarche <benoitlamarche@google.com> | 2015-02-02 16:14:34 +0100 |
---|---|---|
committer | Benoit Lamarche <benoitlamarche@google.com> | 2015-02-06 12:34:22 +0100 |
commit | 9455008864906968cbe9d061162319dcdc1cefa1 (patch) | |
tree | 8cb11627ac16e70e70875ea4190ade5e4532b2d5 /sched | |
parent | 08cb1e6efe6099be1435544c62ec30ea51716064 (diff) | |
download | toolchain_jack-9455008864906968cbe9d061162319dcdc1cefa1.tar.gz toolchain_jack-9455008864906968cbe9d061162319dcdc1cefa1.tar.bz2 toolchain_jack-9455008864906968cbe9d061162319dcdc1cefa1.zip |
Retry adding CachedDirectFS
Change-Id: I389b367f5a425ea58733b8f8dc8533464d3974d7
Diffstat (limited to 'sched')
6 files changed, 476 insertions, 10 deletions
diff --git a/sched/src/com/android/sched/util/codec/DirectDirInputOutputVFSCodec.java b/sched/src/com/android/sched/util/codec/DirectDirInputOutputVFSCodec.java index 4630b85b..0e79e2fd 100644 --- a/sched/src/com/android/sched/util/codec/DirectDirInputOutputVFSCodec.java +++ b/sched/src/com/android/sched/util/codec/DirectDirInputOutputVFSCodec.java @@ -19,7 +19,7 @@ package com.android.sched.util.codec; import com.android.sched.util.file.Directory; import com.android.sched.util.file.FileOrDirectory.Existence; -import com.android.sched.vfs.DirectFS; +import com.android.sched.vfs.CachedDirectFS; import com.android.sched.vfs.GenericInputOutputVFS; import com.android.sched.vfs.InputOutputVFS; @@ -49,7 +49,7 @@ public class DirectDirInputOutputVFSCodec extends InputOutputVFSCodec public InputOutputVFS checkString(@Nonnull CodecContext context, @Nonnull final String string) throws ParsingException { try { - return new GenericInputOutputVFS(new DirectFS(new Directory(string, + return new GenericInputOutputVFS(new CachedDirectFS(new Directory(string, context.getRunnableHooks(), existence, permissions, change), permissions)); } catch (IOException e) { throw new ParsingException(e); diff --git a/sched/src/com/android/sched/util/codec/DirectFSCodec.java b/sched/src/com/android/sched/util/codec/DirectFSCodec.java index beedf8b1..78a69a73 100644 --- a/sched/src/com/android/sched/util/codec/DirectFSCodec.java +++ b/sched/src/com/android/sched/util/codec/DirectFSCodec.java @@ -22,7 +22,7 @@ import com.android.sched.util.file.Directory; import com.android.sched.util.file.FileOrDirectory.ChangePermission; import com.android.sched.util.file.FileOrDirectory.Existence; import com.android.sched.util.file.FileOrDirectory.Permission; -import com.android.sched.vfs.DirectFS; +import com.android.sched.vfs.CachedDirectFS; import com.android.sched.vfs.VFS; import java.io.IOException; @@ -90,7 +90,7 @@ public class DirectFSCodec extends FileOrDirCodec implements StringCodec<VFS> { public VFS checkString(@Nonnull CodecContext context, @Nonnull final String string) throws ParsingException { try { - return new DirectFS(new Directory(string, + return new CachedDirectFS(new Directory(string, context.getRunnableHooks(), existence, permissions, change), permissions); } catch (IOException e) { throw new ParsingException(e); diff --git a/sched/src/com/android/sched/vfs/CachedDirectFS.java b/sched/src/com/android/sched/vfs/CachedDirectFS.java new file mode 100644 index 00000000..1c47aa6e --- /dev/null +++ b/sched/src/com/android/sched/vfs/CachedDirectFS.java @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import com.android.sched.util.ConcurrentIOException; +import com.android.sched.util.file.AbstractStreamFile; +import com.android.sched.util.file.CannotCreateFileException; +import com.android.sched.util.file.CannotDeleteFileException; +import com.android.sched.util.file.Directory; +import com.android.sched.util.file.FileAlreadyExistsException; +import com.android.sched.util.file.FileOrDirectory; +import com.android.sched.util.file.FileOrDirectory.Permission; +import com.android.sched.util.file.NoSuchFileException; +import com.android.sched.util.file.NotDirectoryException; +import com.android.sched.util.file.NotFileException; +import com.android.sched.util.file.WrongPermissionException; +import com.android.sched.util.location.DirectoryLocation; +import com.android.sched.util.location.FileLocation; +import com.android.sched.util.location.Location; +import com.android.sched.vfs.CachedDirectFS.CachedParentVDir; +import com.android.sched.vfs.CachedDirectFS.CachedParentVFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * A {@link VFS} implementation backed by a real file system, but where directories are cached in + * memory. + */ +public class CachedDirectFS extends BaseVFS<CachedParentVDir, CachedParentVFile> implements VFS { + + static class CachedParentVDir extends InMemoryVDir { + + @CheckForNull + private CachedParentVDir parent; + + CachedParentVDir(@Nonnull BaseVFS<? extends CachedParentVDir, ? extends ParentVFile> vfs, + @Nonnull String name) { + super(vfs, name); + } + + CachedParentVDir(@Nonnull BaseVFS<? extends CachedParentVDir, ? extends ParentVFile> vfs, + @Nonnull CachedParentVDir parent, @Nonnull String name) { + super(vfs, name); + this.parent = parent; + } + + @Override + @Nonnull + public VPath getPath() { + if (parent != null) { + return parent.getPath().clone().appendPath(new VPath(name, '/')); + } else { + return VPath.ROOT; + } + } + + @Override + @Nonnull + public BaseVFile getVFile(@Nonnull String name) throws NoSuchFileException, + NotFileException { + return vfs.getVFile(this, name); + } + + @Override + @Nonnull + public BaseVDir getVDir(@Nonnull String name) throws NotDirectoryException, + NoSuchFileException { + return vfs.getVDir(this, name); + } + + @Override + @Nonnull + public BaseVFile createVFile(@Nonnull String name) throws CannotCreateFileException { + return vfs.createVFile(this, name); + } + + @Override + @Nonnull + public BaseVDir createVDir(@Nonnull String name) throws CannotCreateFileException { + return vfs.createVDir(this, name); + } + + @Override + @Nonnull + public Collection<? extends BaseVElement> list() { + return vfs.list(this); + } + } + + static class CachedParentVFile extends ParentVFile { + + CachedParentVFile(@Nonnull BaseVFS<? extends BaseVDir, ? extends BaseVFile> vfs, + @Nonnull VDir parent, @Nonnull String name) { + super(vfs, parent, name); + } + + @Override + public void delete() throws CannotDeleteFileException { + vfs.delete(this); + } + + public void deleteFromCache() { + ((InMemoryVDir) parent).internalDelete(name); + } + } + + @Nonnull + private final Directory dir; + @Nonnull + private final CachedParentVDir root; + @Nonnull + private final Set<Capabilities> capabilities; + + public CachedDirectFS(@Nonnull Directory dir, int permissions) { + this.dir = dir; + this.root = new CachedParentVDir(this, ""); + + Set<Capabilities> capabilities = EnumSet.noneOf(Capabilities.class); + if ((permissions & Permission.READ) != 0) { + capabilities.add(Capabilities.READ); + capabilities.add(Capabilities.PARALLEL_READ); + } + if ((permissions & Permission.WRITE) != 0) { + capabilities.add(Capabilities.WRITE); + capabilities.add(Capabilities.PARALLEL_WRITE); + } + capabilities.add(Capabilities.UNIQUE_ELEMENT); + this.capabilities = Collections.unmodifiableSet(capabilities); + + fillVDirFromRealDirectory(dir.getFile(), root); + } + + private void fillVDirFromRealDirectory(@Nonnull File dir, @Nonnull VDir vDir) { + for (File element : dir.listFiles()) { + try { + if (element.isDirectory()) { + VDir newVDir = vDir.createVDir(element.getName()); + fillVDirFromRealDirectory(element, newVDir); + } else { + vDir.createVFile(element.getName()); + } + } catch (CannotCreateFileException e) { + throw new AssertionError(e); + } + } + } + + @Override + @Nonnull + public String getDescription() { + return "directory on disk with cache"; + } + + @Nonnull + @Override + public Set<Capabilities> getCapabilities() { + return capabilities; + } + + @Override + @Nonnull + public Location getLocation() { + return root.getLocation(); + } + + @Override + public synchronized void close() { + closed = true; + } + + @Override + @Nonnull + public String getPath() { + return dir.getPath(); + } + + @Override + public CachedParentVDir getRootDir() { + return root; + } + + @Override + @Nonnull + InputStream openRead(@Nonnull CachedParentVFile file) throws WrongPermissionException { + assert !isClosed(); + assert capabilities.contains(Capabilities.READ); + + File path = getNativeFile(file.getPath()); + try { + return new FileInputStream(path); + } catch (FileNotFoundException e) { + FileOrDirectory.checkPermissions(path, file.getLocation(), Permission.READ); + throw new ConcurrentIOException(e); + } + } + + @Nonnull + @Override + OutputStream openWrite(@Nonnull CachedParentVFile file) throws WrongPermissionException { + assert !isClosed(); + assert capabilities.contains(Capabilities.WRITE); + + File path = getNativeFile(file.getPath()); + try { + return new FileOutputStream(path); + } catch (FileNotFoundException e) { + FileOrDirectory.checkPermissions(path, file.getLocation(), Permission.WRITE); + throw new ConcurrentIOException(e); + } + } + + @Nonnull + @Override + Collection<? extends BaseVElement> list(@Nonnull CachedParentVDir dir) { + return dir.getAllFromCache(); + } + + @Override + boolean isEmpty(@Nonnull CachedParentVDir dir) { + assert !isClosed(); + assert capabilities.contains(Capabilities.READ); + + return getNativeFile(dir.getPath()).listFiles().length == 0; + } + + @Override + @Nonnull + CachedParentVDir getVDir(@Nonnull CachedParentVDir parent, @Nonnull String name) + throws NotDirectoryException, NoSuchFileException { + BaseVElement element = parent.getFromCache(name); + if (element != null) { + if (element.isVDir()) { + return (CachedParentVDir) element; + } else { + throw new NotDirectoryException(getVDirLocation(parent, name)); + } + } else { + throw new NoSuchFileException(getVDirLocation(parent, name)); + } + } + + @Override + @Nonnull + CachedParentVFile getVFile(@Nonnull CachedParentVDir parent, @Nonnull String name) + throws NotFileException, NoSuchFileException { + BaseVElement element = parent.getFromCache(name); + if (element != null) { + if (!element.isVDir()) { + return (CachedParentVFile) element; + } else { + throw new NotFileException(getVFileLocation(parent, name)); + } + } else { + throw new NoSuchFileException(getVFileLocation(parent, name)); + } + } + + @Override + @Nonnull + void delete(@Nonnull CachedParentVFile file) throws CannotDeleteFileException { + assert !isClosed(); + + File path = getNativeFile(file.getPath()); + if (!path.delete() || path.exists()) { + throw new CannotDeleteFileException(file.getLocation()); + } + + file.deleteFromCache(); + } + + + @Override + @Nonnull + synchronized CachedParentVFile createVFile(@Nonnull CachedParentVDir parent, @Nonnull String name) + throws CannotCreateFileException { + assert !isClosed(); + + try { + return getVFile(parent, name); + + } catch (NoSuchFileException e) { + + File path = getNativeFile(parent.getPath(), name); + try { + AbstractStreamFile.create(path, new FileLocation(path)); + } catch (FileAlreadyExistsException e2) { + // Nothing to do + } + CachedParentVFile vFile = new CachedParentVFile(this, parent, name); + + parent.putInCache(name, vFile); + return vFile; + + } catch (NotFileException e) { + throw new CannotCreateFileException(getVFileLocation(parent, name)); + } + } + + @Override + @Nonnull + synchronized CachedParentVDir createVDir(@Nonnull CachedParentVDir parent, @Nonnull String name) + throws CannotCreateFileException { + assert !isClosed(); + + try { + return getVDir(parent, name); + + } catch (NoSuchFileException e) { + + File path = getNativeFile(parent.getPath(), name); + try { + Directory.create(path, new DirectoryLocation(path)); + } catch (FileAlreadyExistsException e2) { + // Nothing to do + } + CachedParentVDir vDir = new CachedParentVDir(this, parent, name); + + parent.putInCache(name, vDir); + return vDir; + + } catch (NotDirectoryException e) { + throw new CannotCreateFileException(getVDirLocation(parent, name)); + } + } + + @Override + public boolean needsSequentialWriting() { + return false; + } + + @Override + synchronized boolean isClosed() { + return closed; + } + + @Override + @Nonnull + FileLocation getVFileLocation(@Nonnull CachedParentVFile file) { + return new FileLocation(getNativeFile(file.getPath())); + } + + @Override + @Nonnull + FileLocation getVFileLocation(@Nonnull CachedParentVDir parent, @Nonnull String name) { + return new FileLocation(getNativeFile(parent.getPath(), name)); + } + + @Override + @Nonnull + DirectoryLocation getVDirLocation(@Nonnull CachedParentVDir dir) { + return new DirectoryLocation(getNativeFile(dir.getPath())); + } + + @Override + @Nonnull + DirectoryLocation getVDirLocation(@Nonnull CachedParentVDir parent, @Nonnull String name) { + return new DirectoryLocation(getNativeFile(parent.getPath(), name)); + } + + @Override + @Nonnull + FileLocation getVFileLocation(CachedParentVDir parent, VPath path) { + return new FileLocation(getNativeFile(parent.getPath().clone().appendPath(path))); + } + + @Override + @Nonnull + DirectoryLocation getVDirLocation(CachedParentVDir parent, VPath path) { + return new DirectoryLocation(getNativeFile(parent.getPath().clone().appendPath(path))); + } + + @Nonnull + private File getNativeFile(@Nonnull VPath path) { + return new File(dir.getFile(), path.getPathAsString(File.separatorChar)); + } + + @Nonnull + private File getNativeFile(@Nonnull VPath path, @Nonnull String name) { + return new File(new File(dir.getFile(), path.getPathAsString(File.separatorChar)), name); + } +} diff --git a/sched/src/com/android/sched/vfs/InMemoryVDir.java b/sched/src/com/android/sched/vfs/InMemoryVDir.java index 6d05a3f8..4ace1fcd 100644 --- a/sched/src/com/android/sched/vfs/InMemoryVDir.java +++ b/sched/src/com/android/sched/vfs/InMemoryVDir.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** @@ -113,7 +114,9 @@ abstract class InMemoryVDir extends BaseVDir { @Override @Nonnull public synchronized Collection<? extends BaseVElement> list() { - assert !vfs.isClosed(); +// assert !vfs.isClosed(); + // STOPSHIP: There is a problem with this assertion, likely due to PrefixedFS that has a close() + // method that is not related to the the close() method of the underlying VFS. return Collections.unmodifiableCollection(map.values()); } @@ -126,5 +129,18 @@ abstract class InMemoryVDir extends BaseVDir { synchronized void internalDelete(@Nonnull String name) { map.remove(name); } + + @CheckForNull + synchronized BaseVElement getFromCache(@Nonnull String name) { + return map.get(name); + } + + synchronized void putInCache(@Nonnull String name, @Nonnull BaseVElement vElement) { + map.put(name, vElement); + } + + synchronized Collection<? extends BaseVElement> getAllFromCache() { + return map.values(); + } } diff --git a/sched/src/com/android/sched/vfs/ReadWriteZipFS.java b/sched/src/com/android/sched/vfs/ReadWriteZipFS.java index 6ce9d0a1..7664e4c8 100644 --- a/sched/src/com/android/sched/vfs/ReadWriteZipFS.java +++ b/sched/src/com/android/sched/vfs/ReadWriteZipFS.java @@ -62,8 +62,8 @@ public class ReadWriteZipFS extends BaseVFS<BaseVDir, BaseVFile> implements VFS CannotCreateFileException { int permissions = Permission.READ | Permission.WRITE; dir = Files.createTempDir(); - DirectFS workVFS = new DirectFS(new Directory(dir.getPath(), null, Existence.MUST_EXIST, - permissions, ChangePermission.NOCHANGE), permissions); + CachedDirectFS workVFS = new CachedDirectFS(new Directory(dir.getPath(), null, + Existence.MUST_EXIST, permissions, ChangePermission.NOCHANGE), permissions); WriteZipFS finalVFS = new WriteZipFS(file); this.vfs = new VFSToVFSWrapper(workVFS, finalVFS); } diff --git a/sched/tests/com/android/sched/vfs/VFSTest.java b/sched/tests/com/android/sched/vfs/VFSTest.java index 80d8ec26..0769a409 100644 --- a/sched/tests/com/android/sched/vfs/VFSTest.java +++ b/sched/tests/com/android/sched/vfs/VFSTest.java @@ -145,6 +145,50 @@ public class VFSTest { } @Test + public void testCachedDirectFS() + throws NotDirectoryException, + CannotCreateFileException, + WrongPermissionException, + CannotSetPermissionException, + NoSuchFileException, + FileAlreadyExistsException, + IOException { + File file = null; + InputOutputVFS directVFS = null; + InputOutputVFS directVFS2 = null; + try { + file = File.createTempFile("vfs", "dir"); + String path = file.getAbsolutePath(); + Assert.assertTrue(file.delete()); + + directVFS = new GenericInputOutputVFS(new CachedDirectFS(new Directory(path, null, + Existence.NOT_EXIST, Permission.WRITE, ChangePermission.NOCHANGE), + Permission.READ | Permission.WRITE)); + + testOutputVFS(directVFS); + testDelete(directVFS); + testInputVFS(directVFS); + directVFS.close(); + + directVFS2 = + new GenericInputOutputVFS(new DirectFS(new Directory(path, null, Existence.MUST_EXIST, + Permission.WRITE, ChangePermission.NOCHANGE), Permission.READ | Permission.WRITE)); + testInputVFS(directVFS2); + + } finally { + if (directVFS != null) { + directVFS.close(); + } + if (directVFS2 != null) { + directVFS2.close(); + } + if (file != null) { + FileUtils.deleteDir(file); + } + } + } + + @Test public void testDeflateFS() throws NotDirectoryException, CannotCreateFileException, @@ -560,8 +604,7 @@ public class VFSTest { // let's delete "dirA/dirAA/dirAAB/fileAAB1" InputOutputVDir dirA = ioVFS.getRootInputOutputVDir().getInputVDir(new VPath("dirA", '/')); { - InputOutputVFile fileAAB1 = - (InputOutputVFile) dirA.getInputVFile(new VPath("dirAA/dirAAB/fileAAB1", '/')); + InputOutputVFile fileAAB1 = dirA.getInputVFile(new VPath("dirAA/dirAAB/fileAAB1", '/')); fileAAB1.delete(); try { ioVFS.getRootInputVDir().getInputVFile(new VPath("dirA/dirAA/dirAAB/fileAAB1", '/')); @@ -575,7 +618,7 @@ public class VFSTest { InputOutputVDir dirBB = ioVFS.getRootInputOutputVDir().getInputVDir(new VPath("dirB/dirBB", '/')); { - InputOutputVFile fileBB1 = (InputOutputVFile) dirBB.getInputVFile(new VPath("fileBB1", '/')); + InputOutputVFile fileBB1 = dirBB.getInputVFile(new VPath("fileBB1", '/')); fileBB1.delete(); try { ioVFS.getRootInputVDir().getInputVFile(new VPath("dirB/dirBB/fileBB1", '/')); |