4848import java .util .Map ;
4949import java .util .Set ;
5050import java .util .WeakHashMap ;
51+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
5152import java .util .logging .Level ;
5253import java .util .logging .Logger ;
5354
@@ -155,8 +156,14 @@ private static class NativeStringTracking {
155156 //public static final int ALIGN_8 = 6;
156157
157158 protected static final int CALCULATE_SIZE = -1 ;
159+ static final ReentrantReadWriteLock layoutInfoLock = new ReentrantReadWriteLock ();
160+ static final ReentrantReadWriteLock fieldOrderLock = new ReentrantReadWriteLock ();
161+ static final ReentrantReadWriteLock fieldListLock = new ReentrantReadWriteLock ();
162+ static final ReentrantReadWriteLock validationLock = new ReentrantReadWriteLock ();
158163 static final Map <Class <?>, LayoutInfo > layoutInfo = new WeakHashMap <>();
159164 static final Map <Class <?>, List <String >> fieldOrder = new WeakHashMap <>();
165+ static final Map <Class <?>, List <Field >> fieldList = new WeakHashMap <>();
166+ static final Map <Class <?>, Boolean > validationMap = new WeakHashMap <>();
160167
161168 // This field is accessed by native code
162169 private Pointer memory ;
@@ -1015,36 +1022,68 @@ protected void sortFields(List<Field> fields, List<String> names) {
10151022 * this {@link Structure} class.
10161023 */
10171024 protected List <Field > getFieldList () {
1018- List <Field > flist = new ArrayList <>();
1019- for (Class <?> cls = getClass ();
1020- !cls .equals (Structure .class );
1021- cls = cls .getSuperclass ()) {
1022- List <Field > classFields = new ArrayList <>();
1023- Field [] fields = cls .getDeclaredFields ();
1024- for (int i =0 ;i < fields .length ;i ++) {
1025- int modifiers = fields [i ].getModifiers ();
1026- if (Modifier .isStatic (modifiers ) || !Modifier .isPublic (modifiers )) {
1027- continue ;
1028- }
1029- classFields .add (fields [i ]);
1025+ Class <?> clazz = getClass ();
1026+ // Try to read the value under the read lock
1027+ fieldListLock .readLock ().lock ();
1028+ try {
1029+ List <Field > fields = fieldList .get (clazz );
1030+ if (fields != null ) {
1031+ return fields ; // Return the cached result if found
10301032 }
1031- flist .addAll (0 , classFields );
1033+ } finally {
1034+ fieldListLock .readLock ().unlock ();
1035+ }
1036+
1037+ // If not found, compute the value under the write lock
1038+ fieldListLock .writeLock ().lock ();
1039+ try {
1040+ // Double-check if another thread has computed the value before we do
1041+ return fieldList .computeIfAbsent (clazz , (c ) -> {
1042+ List <Field > flist = new ArrayList <>();
1043+ List <Field > classFields = new ArrayList <>();
1044+ for (Class <?> cls = clazz ;
1045+ !cls .equals (Structure .class );
1046+ cls = cls .getSuperclass ()) {
1047+ for (Field field : cls .getDeclaredFields ()) {
1048+ int modifiers = field .getModifiers ();
1049+ if (Modifier .isStatic (modifiers ) || !Modifier .isPublic (modifiers )) {
1050+ continue ;
1051+ }
1052+ classFields .add (field );
1053+ }
1054+ flist .addAll (0 , classFields );
1055+ classFields .clear ();
1056+ }
1057+ return flist ;
1058+ });
1059+ } finally {
1060+ fieldListLock .writeLock ().unlock ();
10321061 }
1033- return flist ;
10341062 }
10351063
10361064 /** Cache field order per-class.
10371065 * @return (cached) ordered list of fields
10381066 */
10391067 private List <String > fieldOrder () {
10401068 Class <?> clazz = getClass ();
1041- synchronized (fieldOrder ) {
1042- List <String > list = fieldOrder .get (clazz );
1043- if (list == null ) {
1044- list = getFieldOrder ();
1045- fieldOrder .put (clazz , list );
1069+ // Try to read the value under the read lock
1070+ fieldOrderLock .readLock ().lock ();
1071+ try {
1072+ List <String > order = fieldOrder .get (clazz );
1073+ if (order != null ) {
1074+ return order ; // Return the cached result if found
10461075 }
1047- return list ;
1076+ } finally {
1077+ fieldOrderLock .readLock ().unlock ();
1078+ }
1079+
1080+ // If not found, compute the value under the write lock
1081+ fieldOrderLock .writeLock ().lock ();
1082+ try {
1083+ // Double-check if another thread has computed the value before we do (see JavaDoc)
1084+ return fieldOrder .computeIfAbsent (clazz , (c ) -> getFieldOrder ());
1085+ } finally {
1086+ fieldOrderLock .writeLock ().unlock ();
10481087 }
10491088 }
10501089
@@ -1159,8 +1198,11 @@ static int size(Class<? extends Structure> type) {
11591198 */
11601199 static <T extends Structure > int size (Class <T > type , T value ) {
11611200 LayoutInfo info ;
1162- synchronized (layoutInfo ) {
1201+ layoutInfoLock .readLock ().lock ();
1202+ try {
11631203 info = layoutInfo .get (type );
1204+ } finally {
1205+ layoutInfoLock .readLock ().unlock ();
11641206 }
11651207 int sz = (info != null && !info .variable ) ? info .size : CALCULATE_SIZE ;
11661208 if (sz == CALCULATE_SIZE ) {
@@ -1183,8 +1225,11 @@ int calculateSize(boolean force, boolean avoidFFIType) {
11831225 int size = CALCULATE_SIZE ;
11841226 Class <?> clazz = getClass ();
11851227 LayoutInfo info ;
1186- synchronized (layoutInfo ) {
1228+ layoutInfoLock .readLock ().lock ();
1229+ try {
11871230 info = layoutInfo .get (clazz );
1231+ } finally {
1232+ layoutInfoLock .readLock ().unlock ();
11881233 }
11891234 if (info == null
11901235 || this .alignType != info .alignType
@@ -1196,7 +1241,8 @@ int calculateSize(boolean force, boolean avoidFFIType) {
11961241 this .structFields = info .fields ;
11971242
11981243 if (!info .variable ) {
1199- synchronized (layoutInfo ) {
1244+ layoutInfoLock .readLock ().lock ();
1245+ try {
12001246 // If we've already cached it, only override layout if
12011247 // we're using non-default values for alignment and/or
12021248 // type mapper; this way we don't override the cache
@@ -1205,8 +1251,18 @@ int calculateSize(boolean force, boolean avoidFFIType) {
12051251 if (!layoutInfo .containsKey (clazz )
12061252 || this .alignType != ALIGN_DEFAULT
12071253 || this .typeMapper != null ) {
1254+ // Must release read lock before acquiring write lock (see JavaDoc lock escalation example)
1255+ layoutInfoLock .readLock ().unlock ();
1256+ layoutInfoLock .writeLock ().lock ();
1257+
12081258 layoutInfo .put (clazz , info );
1259+
1260+ // Downgrade by acquiring read lock before releasing write lock (again, see JavaDoc)
1261+ layoutInfoLock .readLock ().lock ();
1262+ layoutInfoLock .writeLock ().unlock ();;
12091263 }
1264+ } finally {
1265+ layoutInfoLock .readLock ().unlock ();
12101266 }
12111267 }
12121268 size = info .size ;
@@ -1250,9 +1306,28 @@ private void validateField(String name, Class<?> type) {
12501306
12511307 /** ensure all fields are of valid type. */
12521308 private void validateFields () {
1253- List <Field > fields = getFieldList ();
1254- for (Field f : fields ) {
1255- validateField (f .getName (), f .getType ());
1309+ // Try to read the value under the read lock
1310+ validationLock .readLock ().lock ();
1311+ try {
1312+ if (validationMap .containsKey (getClass ())) {
1313+ return ; // Return because this Structure has already been validated
1314+ }
1315+ } finally {
1316+ validationLock .readLock ().unlock ();
1317+ }
1318+
1319+ // If not found, perform validation and update the cache under the write lock
1320+ validationLock .writeLock ().lock ();
1321+ try {
1322+ // Double-check if another thread has computed the value before we do (see JavaDoc)
1323+ validationMap .computeIfAbsent (getClass (), (cls ) -> {
1324+ for (Field f : getFieldList ()) {
1325+ validateField (f .getName (), f .getType ());
1326+ }
1327+ return true ;
1328+ });
1329+ } finally {
1330+ validationLock .writeLock ().unlock ();
12561331 }
12571332 }
12581333
0 commit comments