-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Avoid threading issues in sortFields (#1686) #1687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
I tried adding tests for this, but I couldn't figure out a good way to reliable trigger the issue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the solution in sortFields
. That method does exactly what it tells us. It also does not access global state, so there is no need for locking inside sortFields
. The problem is that the list returned by getFieldList
in line 1128 is modified via the call to sortFields
in line 1161 and that list is shared.
jna/src/com/sun/jna/Structure.java
Lines 1127 to 1163 in 1d3ceb7
protected List<Field> getFields(boolean force) { | |
List<Field> flist = getFieldList(); | |
Set<String> names = new HashSet<>(); | |
for (Field f : flist) { | |
names.add(f.getName()); | |
} | |
List<String> fieldOrder = fieldOrder(); | |
if (fieldOrder.size() != flist.size() && flist.size() > 1) { | |
if (force) { | |
throw new Error("Structure.getFieldOrder() on " + getClass() | |
+ (fieldOrder.size() < flist.size() | |
? " does not provide enough" | |
: " provides too many") | |
+ " names [" + fieldOrder.size() | |
+ "] (" | |
+ sort(fieldOrder) | |
+ ") to match declared fields [" + flist.size() | |
+ "] (" | |
+ sort(names) | |
+ ")"); | |
} | |
return null; | |
} | |
Set<String> orderedNames = new HashSet<>(fieldOrder); | |
if (!orderedNames.equals(names)) { | |
throw new Error("Structure.getFieldOrder() on " + getClass() | |
+ " returns names (" | |
+ sort(fieldOrder) | |
+ ") which do not match declared field names (" | |
+ sort(names) + ")"); | |
} | |
sortFields(flist, fieldOrder); | |
return flist; | |
} |
sortFields
is only called by getFields
and that is only called by deriveLayout
and that is called by calculateSize
. For "normal" structures caching kicks in, only structures containing arrays will not be cached. So from my POV the most obvious fix would be to only change the original line 1128 to:
protected List<Field> getFields(boolean force) {
// line 1128:
List<Field> flist = new ArrayList<>(getFieldList());
Set<String> names = new HashSet<>();
That way there is no global state and we need no extra layers.
For anything more complex I want to see numbers.
And the important thing i forgot: thanks for taking care 👍 |
I think I agree with @matthiasblaesing. Ultimately, because the The only arguable reason to do this would be to speed-up calculating the size of "variable" Structures ( protected List<Field> getFields(boolean force) {
// line 1128:
List<Field> flist = new ArrayList<>(getFieldList());
Set<String> names = new HashSet<>(); ...is miniscule in comparison to the work that At least that's my understanding at present. |
@matthiasblaesing thanks for the suggestion, I'm very happy to go with the simplified version. I didn't understand the caching strategy/requirements when I added that extra code. I'll push a new version up tomorrow. |
Copy the fields array from `getFieldList`, this avoids the possibility of 2 threads trying to mutate it at once in the call `sortFields` at the bottom of the function.
c4f7260
to
b665e9e
Compare
What do you think of making a 5.18.1 release for this? |
I used essentially the same pattern than
validateFields
uses.