Skip to content

Commit 5a4d392

Browse files
committed
Merge pull request #420 from sjappig/master
Issue #413: Structure leaves always one element in ThreadLocal set
2 parents 0b47d1e + b8443b3 commit 5a4d392

File tree

3 files changed

+86
-58
lines changed

3 files changed

+86
-58
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Bug Fixes
5656
* [#388](https://github.com/twall/jna/issues/388): Ensure native library always opened with provided flags - [@zolyfarkas](https://github.com/zolyfarkas).
5757
* [#403](https://github.com/twall/jna/pull/403): Fix `com.sun.jna.platform.win32.COM.COMUtils.SUCCEEDED` and `FAILED` - [@lwahonen](https://github.com/lwahonen).
5858
* [#404](https://github.com/twall/jna/pull/404): Fix `VARIANT` constructors for `int`, `short`, and `long` - [@lwahonen](https://github.com/lwahonen).
59+
* [#420](https://github.com/twall/jna/pull/420): Fix structure leaving always one element in ThreadLocal set - [@sjappig](https://github.com/sjappig).
5960

6061
Release 4.1
6162
===========

src/com/sun/jna/Structure.java

+63-58
Original file line numberDiff line numberDiff line change
@@ -427,72 +427,77 @@ protected synchronized Object initialValue() {
427427
// Keep track of what is currently being read/written to avoid redundant
428428
// reads (avoids problems with circular references).
429429
private static final ThreadLocal busy = new ThreadLocal() {
430-
/** Avoid using a hash-based implementation since the hash code
430+
protected synchronized Object initialValue() {
431+
return new StructureSet();
432+
}
433+
};
434+
435+
/** Avoid using a hash-based implementation since the hash code
431436
for a Structure is not immutable.
432-
*/
433-
class StructureSet extends AbstractCollection implements Set {
434-
private Structure[] elements;
435-
private int count;
436-
private void ensureCapacity(int size) {
437-
if (elements == null) {
438-
elements = new Structure[size*3/2];
439-
}
440-
else if (elements.length < size) {
441-
Structure[] e = new Structure[size*3/2];
442-
System.arraycopy(elements, 0, e, 0, elements.length);
443-
elements = e;
444-
}
445-
}
446-
public int size() { return count; }
447-
public boolean contains(Object o) {
448-
return indexOf(o) != -1;
437+
*/
438+
static class StructureSet extends AbstractCollection implements Set {
439+
Structure[] elements;
440+
private int count;
441+
private void ensureCapacity(int size) {
442+
if (elements == null) {
443+
elements = new Structure[size*3/2];
444+
}
445+
else if (elements.length < size) {
446+
Structure[] e = new Structure[size*3/2];
447+
System.arraycopy(elements, 0, e, 0, elements.length);
448+
elements = e;
449+
}
450+
}
451+
public Structure[] getElements() {
452+
return elements;
453+
}
454+
public int size() { return count; }
455+
public boolean contains(Object o) {
456+
return indexOf(o) != -1;
457+
}
458+
public boolean add(Object o) {
459+
if (!contains(o)) {
460+
ensureCapacity(count+1);
461+
elements[count++] = (Structure)o;
449462
}
450-
public boolean add(Object o) {
451-
if (!contains(o)) {
452-
ensureCapacity(count+1);
453-
elements[count++] = (Structure)o;
463+
return true;
464+
}
465+
private int indexOf(Object o) {
466+
Structure s1 = (Structure)o;
467+
for (int i=0;i < count;i++) {
468+
Structure s2 = elements[i];
469+
if (s1 == s2
470+
|| (s1.getClass() == s2.getClass()
471+
&& s1.size() == s2.size()
472+
&& s1.getPointer().equals(s2.getPointer()))) {
473+
return i;
454474
}
455-
return true;
456475
}
457-
private int indexOf(Object o) {
458-
Structure s1 = (Structure)o;
459-
for (int i=0;i < count;i++) {
460-
Structure s2 = elements[i];
461-
if (s1 == s2
462-
|| (s1.getClass() == s2.getClass()
463-
&& s1.size() == s2.size()
464-
&& s1.getPointer().equals(s2.getPointer()))) {
465-
return i;
466-
}
467-
}
468-
return -1;
469-
}
470-
public boolean remove(Object o) {
471-
int idx = indexOf(o);
472-
if (idx != -1) {
473-
if (--count > 0) {
474-
elements[idx] = elements[count];
475-
elements[count] = null;
476-
}
477-
return true;
478-
}
479-
return false;
480-
}
481-
/** Simple implementation so that toString() doesn't break.
482-
Provides an iterator over a snapshot of this Set.
483-
*/
484-
public Iterator iterator() {
485-
Structure[] e = new Structure[count];
486-
if (count > 0) {
487-
System.arraycopy(elements, 0, e, 0, count);
476+
return -1;
477+
}
478+
public boolean remove(Object o) {
479+
int idx = indexOf(o);
480+
if (idx != -1) {
481+
if (--count >= 0) {
482+
elements[idx] = elements[count];
483+
elements[count] = null;
488484
}
489-
return Arrays.asList(e).iterator();
485+
return true;
490486
}
487+
return false;
491488
}
492-
protected synchronized Object initialValue() {
493-
return new StructureSet();
489+
/** Simple implementation so that toString() doesn't break.
490+
Provides an iterator over a snapshot of this Set.
491+
*/
492+
public Iterator iterator() {
493+
Structure[] e = new Structure[count];
494+
if (count > 0) {
495+
System.arraycopy(elements, 0, e, 0, count);
496+
}
497+
return Arrays.asList(e).iterator();
494498
}
495-
};
499+
}
500+
496501
static Set busy() {
497502
return (Set)busy.get();
498503
}

test/com/sun/jna/StructureTest.java

+22
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import java.util.Arrays;
1717
import java.util.List;
1818
import java.util.Map;
19+
import java.util.Set;
1920

2021
import junit.framework.TestCase;
2122

23+
import com.sun.jna.Structure.StructureSet;
2224
import com.sun.jna.ptr.ByteByReference;
2325
import com.sun.jna.ptr.IntByReference;
2426
import com.sun.jna.ptr.LongByReference;
@@ -1968,4 +1970,24 @@ protected List getFieldOrder() {
19681970
s.read();
19691971
assertEquals("String not decoded properly on read", VALUE, s.field);
19701972
}
1973+
1974+
public void testThreadLocalSetReleasesReferences() {
1975+
class TestStructure extends Structure {
1976+
public String field;
1977+
protected List getFieldOrder() {
1978+
return Arrays.asList(new String[] { "field" });
1979+
}
1980+
}
1981+
1982+
TestStructure ts1 = new TestStructure();
1983+
TestStructure ts2 = new TestStructure();
1984+
1985+
StructureSet structureSet = (StructureSet) Structure.busy();
1986+
structureSet.add(ts1);
1987+
structureSet.add(ts2);
1988+
structureSet.remove(ts1);
1989+
assertNotNull(structureSet.elements[0]);
1990+
structureSet.remove(ts2);
1991+
assertNull(structureSet.elements[0]);
1992+
}
19711993
}

0 commit comments

Comments
 (0)