< prev index next >

src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java

Print this page
rev 54588 : 8222532: (zipfs) Performance regression when writing ZipFileSystem entries in parallel
Reviewed-by: TBD
rev 54589 : imported patch def2

@@ -33,14 +33,12 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
-import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
-import java.nio.channels.NonWritableChannelException;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.SeekableByteChannel;
 import java.nio.channels.WritableByteChannel;
 import java.nio.file.*;
 import java.nio.file.attribute.FileAttribute;

@@ -622,18 +620,19 @@
                 e.flag |= FLAG_USE_UTF8;
         }
 
         @Override
         public void close() throws IOException {
-            OutputStream os = getOutputStream(e);
+            // will update the entry
+            try (OutputStream os = getOutputStream(e)) {
             os.write(toByteArray());
-            os.close(); // will update the entry
+            }
             super.close();
         }
     }
 
-    private int getCompressMethod(FileAttribute<?>... attrs) {
+    private int getCompressMethod() {
          return defaultMethod;
     }
 
     // Returns a Writable/ReadByteChannel for now. Might consider to use
     // newFileChannel() instead, which dump the entry data into a regular

@@ -673,11 +672,11 @@
                 }
                 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
                     throw new NoSuchFileException(getString(path));
                 checkParents(path);
                 return new EntryOutputChannel(
-                    new Entry(path, Entry.NEW, false, getCompressMethod(attrs)));
+                    new Entry(path, Entry.NEW, false, getCompressMethod()));
             } finally {
                 endRead();
             }
         } else {
             beginRead();

@@ -740,11 +739,11 @@
                                            .provider()
                                            .newFileChannel(tmpfile, options, attrs);
             final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
             if (forWrite) {
                 u.flag = FLAG_DATADESCR;
-                u.method = getCompressMethod(attrs);
+                u.method = getCompressMethod();
             }
             // is there a better way to hook into the FileChannel's close method?
             return new FileChannel() {
                 public int write(ByteBuffer src) throws IOException {
                     return fch.write(src);

@@ -845,11 +844,11 @@
     private Set<InputStream> streams =
         Collections.synchronizedSet(new HashSet<>());
 
     // the ex-channel and ex-path that need to close when their outstanding
     // input streams are all closed by the obtainers.
-    private Set<ExChannelCloser> exChClosers = new HashSet<>();
+    private Set<ExistingChannelCloser> exChClosers = new HashSet<>();
 
     private Set<Path> tmppaths = Collections.synchronizedSet(new HashSet<Path>());
     private Path getTempPathForEntry(byte[] path) throws IOException {
         Path tmpPath = createTempFileInSameDirectoryAs(zfpath);
         if (path != null) {

@@ -1244,14 +1243,12 @@
 
     // sync the zip file system, if there is any udpate
     private void sync() throws IOException {
         // check ex-closer
         if (!exChClosers.isEmpty()) {
-            for (ExChannelCloser ecc : exChClosers) {
-                if (ecc.streams.isEmpty()) {
-                    ecc.ch.close();
-                    Files.delete(ecc.path);
+            for (ExistingChannelCloser ecc : exChClosers) {
+                if (ecc.closeAndDeleteIfDone()) {
                     exChClosers.remove(ecc);
                 }
             }
         }
         if (!hasUpdate)

@@ -1318,17 +1315,17 @@
         if (!streams.isEmpty()) {
             //
             // There are outstanding input streams open on existing "ch",
             // so, don't close the "cha" and delete the "file for now, let
             // the "ex-channel-closer" to handle them
-            ExChannelCloser ecc = new ExChannelCloser(
-                                      createTempFileInSameDirectoryAs(zfpath),
+            Path path = createTempFileInSameDirectoryAs(zfpath);
+            ExistingChannelCloser ecc = new ExistingChannelCloser(path,
                                       ch,
                                       streams);
-            Files.move(zfpath, ecc.path, REPLACE_EXISTING);
+            Files.move(zfpath, path, REPLACE_EXISTING);
             exChClosers.add(ecc);
-            streams = Collections.synchronizedSet(new HashSet<InputStream>());
+            streams = Collections.synchronizedSet(new HashSet<>());
         } else {
             ch.close();
             Files.delete(zfpath);
         }
 

@@ -2505,22 +2502,38 @@
             fm.close();
             return sb.toString();
         }
     }
 
-    private static class ExChannelCloser  {
-        Path path;
-        SeekableByteChannel ch;
-        Set<InputStream> streams;
-        ExChannelCloser(Path path,
+    private static class ExistingChannelCloser {
+        private final Path path;
+        private final SeekableByteChannel ch;
+        private final Set<InputStream> streams;
+        ExistingChannelCloser(Path path,
                         SeekableByteChannel ch,
-                        Set<InputStream> streams)
-        {
+                              Set<InputStream> streams) {
             this.path = path;
             this.ch = ch;
             this.streams = streams;
         }
+
+        /**
+         * If there are no more outstanding streams, close the channel and
+         * delete the backing file
+         *
+         * @return true if we're done and closed the backing file,
+         *         otherwise false
+         * @throws IOException
+         */
+        public boolean closeAndDeleteIfDone() throws IOException {
+            if (streams.isEmpty()) {
+                ch.close();
+                Files.delete(path);
+                return true;
+            }
+            return false;
+        }
     }
 
     // ZIP directory has two issues:
     // (1) ZIP spec does not require the ZIP file to include
     //     directory entry
< prev index next >