This is an automated email from the ASF dual-hosted git repository. michaelo pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/master by this push: new fcb6be5 [MRESOLVER-132] Remove synchronization in TrackingFileManager fcb6be5 is described below commit fcb6be59c5a5855573886b09c70dab80074a1d27 Author: Michael Osipov <micha...@apache.org> AuthorDate: Mon Aug 17 09:20:36 2020 +0200 [MRESOLVER-132] Remove synchronization in TrackingFileManager This closes #67 --- .../aether/internal/impl/TrackingFileManager.java | 202 ++++++--------------- .../internal/impl/TrackingFileManagerTest.java | 57 +----- 2 files changed, 59 insertions(+), 200 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java index 8e67e8c..e31f096 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java @@ -29,14 +29,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; import java.util.Map; import java.util.Properties; /** - * Manages potentially concurrent accesses to a properties file. + * Manages access to a properties file. */ class TrackingFileManager { @@ -45,35 +42,28 @@ class TrackingFileManager public Properties read( File file ) { - synchronized ( getLock( file ) ) + FileInputStream stream = null; + try { - FileLock lock = null; - FileInputStream stream = null; - try + if ( !file.exists() ) { - if ( !file.exists() ) - { - return null; - } + return null; + } - stream = new FileInputStream( file ); + stream = new FileInputStream( file ); - lock = lock( stream.getChannel(), Math.max( 1, file.length() ), true ); + Properties props = new Properties(); + props.load( stream ); - Properties props = new Properties(); - props.load( stream ); - - return props; - } - catch ( IOException e ) - { - LOGGER.warn( "Failed to read tracking file {}", file, e ); - } - finally - { - release( lock, file ); - close( stream, file ); - } + return props; + } + catch ( IOException e ) + { + LOGGER.warn( "Failed to read tracking file {}", file, e ); + } + finally + { + close( stream, file ); } return null; @@ -83,82 +73,61 @@ class TrackingFileManager { Properties props = new Properties(); - synchronized ( getLock( file ) ) + File directory = file.getParentFile(); + if ( !directory.mkdirs() && !directory.exists() ) { - File directory = file.getParentFile(); - if ( !directory.mkdirs() && !directory.exists() ) - { - LOGGER.warn( "Failed to create parent directories for tracking file {}", file ); - return props; - } + LOGGER.warn( "Failed to create parent directories for tracking file {}", file ); + return props; + } - RandomAccessFile raf = null; - FileLock lock = null; - try + RandomAccessFile raf = null; + try + { + raf = new RandomAccessFile( file, "rw" ); + + if ( file.canRead() ) { - raf = new RandomAccessFile( file, "rw" ); - lock = lock( raf.getChannel(), Math.max( 1, raf.length() ), false ); + byte[] buffer = new byte[(int) raf.length()]; - if ( file.canRead() ) - { - byte[] buffer = new byte[(int) raf.length()]; + raf.readFully( buffer ); - raf.readFully( buffer ); + ByteArrayInputStream stream = new ByteArrayInputStream( buffer ); - ByteArrayInputStream stream = new ByteArrayInputStream( buffer ); + props.load( stream ); + } - props.load( stream ); + for ( Map.Entry<String, String> update : updates.entrySet() ) + { + if ( update.getValue() == null ) + { + props.remove( update.getKey() ); } - - for ( Map.Entry<String, String> update : updates.entrySet() ) + else { - if ( update.getValue() == null ) - { - props.remove( update.getKey() ); - } - else - { - props.setProperty( update.getKey(), update.getValue() ); - } + props.setProperty( update.getKey(), update.getValue() ); } + } - ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 ); + ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 ); - LOGGER.debug( "Writing tracking file {}", file ); - props.store( stream, "NOTE: This is a Maven Resolver internal implementation file" - + ", its format can be changed without prior notice." ); + LOGGER.debug( "Writing tracking file {}", file ); + props.store( stream, "NOTE: This is a Maven Resolver internal implementation file" + + ", its format can be changed without prior notice." ); - raf.seek( 0 ); - raf.write( stream.toByteArray() ); - raf.setLength( raf.getFilePointer() ); - } - catch ( IOException e ) - { - LOGGER.warn( "Failed to write tracking file {}", file, e ); - } - finally - { - release( lock, file ); - close( raf, file ); - } + raf.seek( 0 ); + raf.write( stream.toByteArray() ); + raf.setLength( raf.getFilePointer() ); } - - return props; - } - - private void release( FileLock lock, File file ) - { - if ( lock != null ) + catch ( IOException e ) { - try - { - lock.release(); - } - catch ( IOException e ) - { - LOGGER.warn( "Error releasing lock for tracking file {}", file, e ); - } + LOGGER.warn( "Failed to write tracking file {}", file, e ); } + finally + { + close( raf, file ); + } + + return props; } private void close( Closeable closeable, File file ) @@ -176,61 +145,4 @@ class TrackingFileManager } } - private Object getLock( File file ) - { - /* - * NOTE: Locks held by one JVM must not overlap and using the canonical path is our best bet, still another - * piece of code might have locked the same file (unlikely though) or the canonical path fails to capture file - * identity sufficiently as is the case with Java 1.6 and symlinks on Windows. - */ - try - { - return file.getCanonicalPath().intern(); - } - catch ( IOException e ) - { - LOGGER.warn( "Failed to canonicalize path {}", file, e ); - // TODO This is code smell and deprecated - return file.getAbsolutePath().intern(); - } - } - - @SuppressWarnings( { "checkstyle:magicnumber" } ) - private FileLock lock( FileChannel channel, long size, boolean shared ) - throws IOException - { - FileLock lock = null; - - for ( int attempts = 8; attempts >= 0; attempts-- ) - { - try - { - lock = channel.lock( 0, size, shared ); - break; - } - catch ( OverlappingFileLockException e ) - { - if ( attempts <= 0 ) - { - throw new IOException( e ); - } - try - { - Thread.sleep( 50L ); - } - catch ( InterruptedException e1 ) - { - Thread.currentThread().interrupt(); - } - } - } - - if ( lock == null ) - { - throw new IOException( "Could not lock file" ); - } - - return lock; - } - } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/TrackingFileManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/TrackingFileManagerTest.java index 6dbd21b..1593fa4 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/TrackingFileManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/TrackingFileManagerTest.java @@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl; * to you 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 @@ -113,57 +113,4 @@ public class TrackingFileManagerTest } } - @Test - public void testLockingOnCanonicalPath() - throws Exception - { - final TrackingFileManager tfm = new TrackingFileManager(); - - final File propFile = TestFileUtils.createTempFile( "#COMMENT\nkey1=value1\nkey2 : value2" ); - - final List<Throwable> errors = Collections.synchronizedList( new ArrayList<Throwable>() ); - - Thread[] threads = new Thread[4]; - for ( int i = 0; i < threads.length; i++ ) - { - String path = propFile.getParent(); - for ( int j = 0; j < i; j++ ) - { - path += "/."; - } - path += "/" + propFile.getName(); - final File file = new File( path ); - - threads[i] = new Thread() - { - public void run() - { - try - { - for ( int i = 0; i < 1000; i++ ) - { - assertNotNull( tfm.read( file ) ); - } - } - catch ( Throwable e ) - { - errors.add( e ); - } - } - }; - } - - for ( Thread thread1 : threads ) - { - thread1.start(); - } - - for ( Thread thread : threads ) - { - thread.join(); - } - - assertEquals( Collections.emptyList(), errors ); - } - }