Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting up a blocking file read in Java

I'd like to set up a blocking file read in Java. That is, have a file such that when wrapped by FileInputStream and any read() method is call, the call blocks.

I can't think of an easy OS-independent way - on Unix-like OSes I could try to create a FIFO using mkfifo and read from that file. A possible work around would be to just create a very large file and read from that - the read is unlikely to complete before I capture the stack, but it's ugly and slow (and indeed reads can still be incredibly fast when cached).

The corresponding socket read() case is trivial to set up - create a socket yourself and read from it, and you can have deterministic blocking.

The purpose is to examine stack of the method to determine what the top frames are in such a case. Imagine I have a component which periodically samples the stacks traces of all running threads and then tries to categorize what that thread is doing at the moment. One thing it could be doing is file IO. So I need to know what the "top of stack" looks like during file IO. I have already determined that by experimentation (simply read a file in a variety of ways and sample the stack), but I want to write a test that will fail if this ever changes.

The natural way to write such a test is to kick off a thread which does a file read, then examine the top frame(s). To do this reliably, I want a blocking read (or else the thread may finish its read before the stack trace is taken, etc).

like image 925
BeeOnRope Avatar asked Jan 20 '15 23:01

BeeOnRope


2 Answers

To get a guaranteed blocked I/O, read from a console, e.g. /dev/console on Linux or CON on Windows.

To make this platform-independent, you may hack the FileDescriptor of FileInputStream:

    // Open a dummy FileInputStream
    File f = File.createTempFile("dummy", ".tmp");
    f.deleteOnExit();
    FileInputStream fis = new FileInputStream(f);

    // Replace FileInputStream's descriptor with stdin
    Field fd = FileInputStream.class.getDeclaredField("fd");
    fd.setAccessible(true);
    fd.set(fis, FileDescriptor.in);

    System.out.println("Reading...");
    fis.read();
    System.out.println("Complete");

UPDATE

I've realized you don't even need a method to block. In order just to get a proper stacktrace you may invoke read() on an invalid FileInputStream:

    FileInputStream fis = new FileInputStream(new FileDescriptor());
    fis.read(); // This will throw IOException exactly with the right stacktrace

If you still need a blocking read(), named pipes is the way to go: run mkfifo using Runtime.exec on POSIX systems or create \\.\PIPE\MyPipeName on Windows.

like image 71
apangin Avatar answered Oct 03 '22 12:10

apangin


I don't know of anyway to make a File in a OS-Independent way that will always block when read.

If I were trying to find the stack trace when a specific function were called, I would run the program under a debugger and set a break point on that function. Although, method breakpoints will slow down your program and give you different results than you would normally get if timing is important.

If you have access to the source code of the progream, you could make a fake FileInputStream that extends the real one but always blocks on a read. All you need to do is to switch out the import statements throughout the code. However, this won't capture places where you are not able to switch out import statements and it could be a pain if there is a lot of code.

If you want to use your own FileInputStream without changing the program source code or compiling, you can make a custom class loader that loads your custom FileInputStream class instead of the real one. You can specify which class loader to use on the command line by:

java -Djava.system.class.loader=com.test.MyClassLoader xxx

Now that I think about it, I have an even better idea, instead of making a custom FileInputStream that blocks on read(), make a custom FileInputStream that prints out the stack traces on read(). The custom class can then call the real version of read(). This way you will get all of the stack traces for all calls.

like image 35
OfNothing Avatar answered Oct 03 '22 12:10

OfNothing