Reputation: 2247
Generally I've always seen try-with-resources used to allocate a new object instance whose close()
method is invoked as it goes out-of-scope.
As far as I can tell, creating a new object is not a requirement and the try-with-resources syntax simply needs a local variable to call close() on when that goes out-of-scope. Therefore you can use it to control "paired operations" such as allocating something from a pool and making sure it is returned.
For example, MyHandle
below shows how to release a pooled instance when you no longer need it:
// init
class MyHandle implements AutoCloseable {
boolean inUse = false;
public MyHandle allocate() {
inUse = true;
return this;
}
public void close() {
inUse = false;
}
}
MyHandle[] pool = new MyHandle[POOL_SIZE];
for (int i = 0; i < pool.length; i++) {
pool[i] = new MyHandle(i);
}
// allocate
MyHandle allocateFromPool() {
for (int i = 0; i < pool.length; i++) {
if (!pool[i].inUse)
return pool[i].allocate();
}
throw new Exception("pool depleted");
}
// using resources from the pool
try (MyHandle handle = allocateFromPool()) {
// do something
}
// at this point, inUse==false for that handle...
Is this considered bad form?
EDIT: I guess I'm asking if there are better alternatives to building this sort of logic, or if there's some major drawback when going with the approach above. I find that using this in a library makes for a clean API.
EDIT 2: Please disregard problems in the code example, I wrote it inline in the SO text box to make my question clear with some sort of example. Obviously, it's not real code! :)
Upvotes: 13
Views: 1431
Reputation: 5568
Nothing wrong with try-with-resources like that. But in your case i would worry about accidential reusing closed handles that have been reopend from another thread.
A little indirection would solve that problem, return MyHandleWrapper
instead of the direct access to MyHandle
(allocateFromPool will return a new instance of MyHandleWrapper). It will not! solve all the other threading issues.
public class MyHandleWrapper extends MyHandle {
private MyHandle handle;
private boolean closed;
public void close() {
if(!closed){
handle.inUse = false;
}
closed = true;
}
public void read() {
if (closed) {
throw new IllegalStateException("Already closed");
}
handle.read();
}
}
Basically you keep the information if the handle was closed in MyHandleWrapper. You guard any state changing access to handle
with that flag, if necessary throw appropriate exceptions.
Upvotes: 2
Reputation: 591
Under the covers, most resources (e.g. File Descriptors) are effectively allocated from a pool by the Operating System, and then returned to the pool when closed.
Using try-with-resources in this way is perfectly valid.
N.B. your code example has significant threading issues though, but I assume the necessary thread safety has been removed for clarity in the question. I mention it only because people should NOT copy your code and use it in an implementation.
Upvotes: 4
Reputation: 311998
The try-with-resource syntax is intended as a syntactic sugaring to allow you to make sure you dispose an object, whatever the disposal logic would be. In your case, it's returning the object to the pool. There's absolutely nothing wrong with using try-with-resource like this. It may not be the most common usecase for it, but it's definitely a valid one.
Upvotes: 12