Resource Cleanup Order (try-with-resources)
Problem
A program opens n resources one after the other (number 1, then 2, ...) and then does some work. Opening resource f fails (f = 0 means that nothing fails; f can also be bigger than n, then nothing fails). Print what happens, one event per line: open i when resource i opens, work when the work runs, and close i when resource i closes. Resources must close in the opposite order of opening, also when something fails. If an opening fails, print caught after the closes. Always print done as the last line.
Constraints
- 1 <= n <= 1000
- 0 <= f <= 2000
Examples
Input
3 0
Output
open 1 open 2 open 3 work close 3 close 2 close 1 done
Input
3 2
Output
open 1 close 1 caught done
Solutions
Resources close in reverse order, like a stack. A resource whose constructor throws is never open, so it is not closed. try-with-resources (Java), with (Python) and try/finally (JavaScript) all guarantee the cleanup, also when an exception travels up.
/*
* Problem : Resource trace.
* Input : Two integers: n and f.
* Output : The events.
* Approach: try-with-resources.
* Time : O(n)
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: A Resource writes "open" in its constructor and "close" in close(). AutoCloseable lets try-with-resources close it.
static class Resource implements AutoCloseable {
private final int number;
private final java.util.List<String> trace;
Resource(int number, long failAt, java.util.List<String> trace) {
// Step 2: If this resource must fail, the constructor throws before it is open. Then there is nothing to close.
if (number == failAt) {
throw new IllegalStateException("cannot open " + number);
}
this.number = number;
this.trace = trace;
trace.add("open " + number);
}
@Override
public void close() {
trace.add("close " + number);
}
}
// Step 3: Open resource "index", then open the rest inside it. When the block ends, Java closes this resource,
// so the inner resources always close first.
static void run(int index, long total, long failAt, java.util.List<String> trace) {
try (Resource resource = new Resource(index, failAt, trace)) {
if (index < total) {
run(index + 1, total, failAt, trace);
} else {
trace.add("work");
}
}
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(long a, long b) {
java.util.List<String> trace = new java.util.ArrayList<>();
// Step 4: A failure inside run() travels up. Every resource that is already open is closed on the way.
try {
run(1, a, b, trace);
} catch (IllegalStateException error) {
trace.add("caught");
} finally {
// Step 5: finally runs in both cases.
trace.add("done");
}
return trace.toArray(new String[0]);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long a = scanner.nextLong();
long b = scanner.nextLong();
System.out.println(String.join("\n", solve(a, b)));
}
}
Hints (1)
- In which order do resources close?