Skip to content

Commit

Permalink
* Add PointerScope to manage more easily the resources of a group …
Browse files Browse the repository at this point in the history
…of `Pointer` objects
  • Loading branch information
saudet committed May 10, 2018
1 parent 96045c9 commit d98d323
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Add `PointerScope` to manage more easily the resources of a group of `Pointer` objects
* Fix `Parser` failing on `const void*&` or similar function arguments, and on constructors of class templates
* Add `Info.skipDefaults` to have the `Parser` ignore default function arguments and prevent method overloading
* Accelerate copy and extraction of resources by using larger buffers for file operations
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/org/bytedeco/javacpp/Pointer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2017 Samuel Audet
* Copyright (C) 2011-2018 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -44,6 +44,9 @@
* and classes to let users benefit not only from from garbage collection, but also the
* try-with-resources statement, since it implements the {@link AutoCloseable} interface.
* <p>
* It is also possible to use a {@link PointerScope} to keep track of a group of Pointer objects,
* and have them deallocated in a transparent but deterministic manner.
* <p>
* For examples of subclasses, please refer to the following:
*
* @see BytePointer
Expand Down Expand Up @@ -120,6 +123,10 @@ void init(long allocatedAddress, long allocatedCapacity, long ownerAddress, long
if (ownerAddress != 0 && deallocatorAddress != 0) {
deallocator(new NativeDeallocator(this, ownerAddress, deallocatorAddress));
}
PointerScope s = PointerScope.scopeStack.get().peek();
if (s != null) {
s.attach(this);
}
}

/** The interface to implement to produce a Deallocator usable by Pointer. */
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/org/bytedeco/javacpp/PointerScope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (C) 2018 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
* the Free Software Foundation (subject to the "Classpath" exception),
* either version 2, or any later version (collectively, 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
* http://www.gnu.org/licenses/
* http://www.gnu.org/software/classpath/license.html
*
* or as provided in the LICENSE.txt file that accompanied this code.
* 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 KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.bytedeco.javacpp;

import java.util.ArrayDeque;
import java.util.Deque;
import org.bytedeco.javacpp.tools.Logger;

/**
* {@link Pointer} objects attach themselves automatically on {@link Pointer#init} to the first {@link PointerScope}
* found in {@link #scopeStack}. The user can then call {@link #deallocate()}, or rely on {@link #close()},
* to deallocate in a timely fashion all attached Pointer objects, instead of relying on the garbage collector.
*/
public class PointerScope implements AutoCloseable {
private static final Logger logger = Logger.create(PointerScope.class);

/** A thread-local stack of {@link PointerScope} objects. Pointer objects attach themselves
* automatically on {@link Pointer#init} to the first one on the stack. */
static final ThreadLocal<Deque<PointerScope>> scopeStack = new ThreadLocal<Deque<PointerScope>>() {
@Override protected Deque initialValue() {
return new ArrayDeque<PointerScope>();
}
};

/** The stack keeping references to attached {@link Pointer} objects. */
Deque<Pointer> pointerStack = new ArrayDeque<Pointer>();

/** When true, {@link #deallocate()} gets called on {@link #close()}. */
boolean deallocateOnClose = true;

/** Calls {@code this(true)}. */
public PointerScope() {
this(true);
}

/** Initializes {@link #deallocateOnClose} and pushes itself on the {@link #scopeStack}. */
public PointerScope(boolean deallocateOnClose) {
if (logger.isDebugEnabled()) {
logger.debug("Opening " + this);
}
this.deallocateOnClose = deallocateOnClose;
scopeStack.get().push(this);
}

/** Sets {@link #deallocateOnClose} and returns this Scope. */
public PointerScope deallocateOnClose(boolean deallocateOnClose) {
this.deallocateOnClose = deallocateOnClose;
return this;
}

/** Returns {@link #deallocateOnClose}. */
public boolean deallocateOnClose() {
return deallocateOnClose;
}

/** Pushes the Pointer onto the {@link #pointerStack} of this Scope. */
public PointerScope attach(Pointer p) {
if (logger.isDebugEnabled()) {
logger.debug("Attaching " + p + " to " + this);
}
pointerStack.push(p);
return this;
}

/** Removes the Pointer from the {@link #pointerStack} of this Scope. */
public PointerScope detach(Pointer p) {
if (logger.isDebugEnabled()) {
logger.debug("Detaching " + p + " from " + this);
}
pointerStack.remove(p);
return this;
}

/** Calls {@link #deallocate()} when {@link #deallocateOnClose} is true,
* and removes itself from {@link #scopeStack}. */
@Override public void close() {
if (logger.isDebugEnabled()) {
logger.debug("Closing " + this);
}
if (deallocateOnClose()) {
deallocate();
}
scopeStack.get().remove(this);
}

/** Calls {@link Pointer#deallocate()} on all attached pointers,
* as they are popped off the {@link #pointerStack}. */
public void deallocate() {
if (logger.isDebugEnabled()) {
logger.debug("Deallocating " + this);
}
while (pointerStack.size() > 0) {
pointerStack.pop().deallocate();
}
pointerStack = null;
}
}
36 changes: 36 additions & 0 deletions src/test/java/org/bytedeco/javacpp/PointerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -715,4 +715,40 @@ static class TestFunction extends FunctionPointer {
System.out.println("Took " + (System.nanoTime() - time) / 1000000 + " ms");
}

@Test public void testPointerScope() {
System.out.println("PointerScope");
IntPointer outside = new IntPointer(1);
IntPointer attached = new IntPointer(1), detached, inside, inside1, inside2, inside3, inside4, inside5;

try (PointerScope scope = new PointerScope()) {
scope.attach(attached);

detached = new IntPointer(1);
scope.detach(detached);

inside = new IntPointer(1);
try (PointerScope scope1 = new PointerScope()) {
inside1 = new IntPointer(1);
inside2 = new IntPointer(1);
}
try (PointerScope scope2 = new PointerScope(false)) {
inside3 = new IntPointer(1);
inside4 = new IntPointer(1);
}
inside5 = new IntPointer(1);
}

IntPointer outside2 = new IntPointer(1);

assertFalse(outside.isNull());
assertTrue(attached.isNull());
assertFalse(detached.isNull());
assertTrue(inside.isNull());
assertTrue(inside1.isNull());
assertTrue(inside2.isNull());
assertFalse(inside3.isNull());
assertFalse(inside4.isNull());
assertTrue(inside5.isNull());
assertFalse(outside2.isNull());
}
}

0 comments on commit d98d323

Please sign in to comment.