2

I am currently looking into an issue with Logback where the Compressor class is unable to delete a file if I open it in the log file reader glogg. This situation only happens with that program, should I open the file in Notepad.exe no issues arise, so it is somehow related to locking the file or something like that (full reproduction steps exist in the linked repo).

Right now, I need to manually open the glogg program to reproduce the issue, but ideally I would like to just reproduce this using the Java demo program. So far, I have been quite unsuccessful. So I would like to know how to achieve the following:

  • Start thread A and thread B
  • thread A should write to file X
  • when Thread A is doing File#delete() on X it should return false (meaning unsuccessful)
  • thread B should be the reason for this as it holds on to the file somehow, preventing the delete from being successful

What I have tried out

public void run() {
    while (true) {
        logDumper.debug(bigString);
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] s) {

    final Runnable runnable = () -> {
        try {
            var fileInputStream = new FileInputStream("./test.log");
            var file = new File("./test.log");
            fileInputStream.readAllBytes();
            System.out.println("Size: " + file.length());
            final var fc = fileInputStream.getChannel();

            // this entire locking thing does not seem to have any effect
            final var shared = true; // required to avoid throwing when opening a file as read-only
            try (FileLock lock = fc.tryLock(0,file.length(), shared)) {
            } catch (NonReadableChannelException| NonWritableChannelException e) {
                logger.error("Something happened", e);
                fc.close();
            }
        } catch (IOException e) {
            logger.error("Crashed on locking", e);
            e.printStackTrace();
        }
    };

    new Thread(runnable).start();
    new Main().run();
}
oligofren
  • 20,744
  • 16
  • 93
  • 180
  • @Kayaman I have basically just tinkered around with the reproduction case code in the linked repository. I have just added one such attempt in the question above, where I try to make one thread lock the file, but it seems it can be deleted regardless. The deletion is implicit, as it is done by the `Compressor` class in Logback, after compressing the file. I have not tried opening a RandomAccessFile, as I wanted to work backwards from a working real-world reproduction towards a Java-only reproduction with no manual steps. – oligofren Jun 28 '21 at 12:45
  • 1
    "File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine". From : https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/FileLock.html. However, this seems to be an example how to work around it: https://gist.github.com/syazawa/d1f6bf4553dcc469709b – Oliver Jan Krylow Jun 28 '21 at 13:39
  • @OliverJanKrylow One workaround I could think of then is to simply spin up a second JVM process from the first one. For instance a single class that just opens a file and does not close it. Got some good pointers here: https://stackoverflow.com/questions/636367/executing-a-java-application-in-a-separate-process. That would not simulate, though, it would actually _be_ a second program. – oligofren Jun 28 '21 at 20:39
  • 1
    That is probably your best bet. Even if you achieve a file lock between threads in one JVM, like the example I posted, I am beginning to doubt that it would be useful in your case. Since your goal is a regression test, it should simulate the original bug condition as closely as possible. A second process is therefore probably better. Then you "only" have to deal with OS platform differences in regards to file locking. – Oliver Jan Krylow Jun 30 '21 at 12:19

0 Answers0