48
48
import java .util .Map ;
49
49
import java .util .Set ;
50
50
import java .util .WeakHashMap ;
51
+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
51
52
import java .util .logging .Level ;
52
53
import java .util .logging .Logger ;
53
54
@@ -155,8 +156,14 @@ private static class NativeStringTracking {
155
156
//public static final int ALIGN_8 = 6;
156
157
157
158
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 ();
158
163
static final Map <Class <?>, LayoutInfo > layoutInfo = new WeakHashMap <>();
159
164
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 <>();
160
167
161
168
// This field is accessed by native code
162
169
private Pointer memory ;
@@ -1015,36 +1022,68 @@ protected void sortFields(List<Field> fields, List<String> names) {
1015
1022
* this {@link Structure} class.
1016
1023
*/
1017
1024
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
1030
1032
}
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 ();
1032
1061
}
1033
- return flist ;
1034
1062
}
1035
1063
1036
1064
/** Cache field order per-class.
1037
1065
* @return (cached) ordered list of fields
1038
1066
*/
1039
1067
private List <String > fieldOrder () {
1040
1068
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
1046
1075
}
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 ();
1048
1087
}
1049
1088
}
1050
1089
@@ -1159,8 +1198,11 @@ static int size(Class<? extends Structure> type) {
1159
1198
*/
1160
1199
static <T extends Structure > int size (Class <T > type , T value ) {
1161
1200
LayoutInfo info ;
1162
- synchronized (layoutInfo ) {
1201
+ layoutInfoLock .readLock ().lock ();
1202
+ try {
1163
1203
info = layoutInfo .get (type );
1204
+ } finally {
1205
+ layoutInfoLock .readLock ().unlock ();
1164
1206
}
1165
1207
int sz = (info != null && !info .variable ) ? info .size : CALCULATE_SIZE ;
1166
1208
if (sz == CALCULATE_SIZE ) {
@@ -1183,8 +1225,11 @@ int calculateSize(boolean force, boolean avoidFFIType) {
1183
1225
int size = CALCULATE_SIZE ;
1184
1226
Class <?> clazz = getClass ();
1185
1227
LayoutInfo info ;
1186
- synchronized (layoutInfo ) {
1228
+ layoutInfoLock .readLock ().lock ();
1229
+ try {
1187
1230
info = layoutInfo .get (clazz );
1231
+ } finally {
1232
+ layoutInfoLock .readLock ().unlock ();
1188
1233
}
1189
1234
if (info == null
1190
1235
|| this .alignType != info .alignType
@@ -1196,7 +1241,8 @@ int calculateSize(boolean force, boolean avoidFFIType) {
1196
1241
this .structFields = info .fields ;
1197
1242
1198
1243
if (!info .variable ) {
1199
- synchronized (layoutInfo ) {
1244
+ layoutInfoLock .readLock ().lock ();
1245
+ try {
1200
1246
// If we've already cached it, only override layout if
1201
1247
// we're using non-default values for alignment and/or
1202
1248
// type mapper; this way we don't override the cache
@@ -1205,8 +1251,18 @@ int calculateSize(boolean force, boolean avoidFFIType) {
1205
1251
if (!layoutInfo .containsKey (clazz )
1206
1252
|| this .alignType != ALIGN_DEFAULT
1207
1253
|| 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
+
1208
1258
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 ();;
1209
1263
}
1264
+ } finally {
1265
+ layoutInfoLock .readLock ().unlock ();
1210
1266
}
1211
1267
}
1212
1268
size = info .size ;
@@ -1250,9 +1306,28 @@ private void validateField(String name, Class<?> type) {
1250
1306
1251
1307
/** ensure all fields are of valid type. */
1252
1308
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 ();
1256
1331
}
1257
1332
}
1258
1333
0 commit comments