9
9
using System . IO ;
10
10
using System . Runtime . CompilerServices ;
11
11
using System . Text ;
12
+ using System . Threading ;
12
13
13
14
namespace System . Resources
14
15
#if RESOURCES_EXTENSIONS
@@ -60,10 +61,11 @@ public sealed partial class
60
61
// it make sense to use anything less than one page?
61
62
private const int DefaultFileStreamBufferSize = 4096 ;
62
63
63
- private BinaryReader _store ; // backing store we're reading from.
64
- // Used by RuntimeResourceSet and this class's enumerator. Maps
65
- // resource name to a value, a ResourceLocator, or a
66
- // LooselyLinkedManifestResource.
64
+ // Backing store we're reading from. Usages outside of constructor
65
+ // initialization must be protected by lock (this).
66
+ private BinaryReader _store ;
67
+ // Used by RuntimeResourceSet and this class's enumerator.
68
+ // Accesses must be protected by lock(_resCache).
67
69
internal Dictionary < string , ResourceLocator > ? _resCache ;
68
70
private long _nameSectionOffset ; // Offset to name section of file.
69
71
private long _dataSectionOffset ; // Offset to Data section of file.
@@ -88,7 +90,6 @@ public sealed partial class
88
90
// Version number of .resources file, for compatibility
89
91
private int _version ;
90
92
91
-
92
93
public
93
94
#if RESOURCES_EXTENSIONS
94
95
DeserializingResourceReader ( string fileName )
@@ -169,13 +170,16 @@ private unsafe void Dispose(bool disposing)
169
170
}
170
171
}
171
172
172
- internal static unsafe int ReadUnalignedI4 ( int * p )
173
+ private static unsafe int ReadUnalignedI4 ( int * p )
173
174
{
174
175
return BinaryPrimitives . ReadInt32LittleEndian ( new ReadOnlySpan < byte > ( p , sizeof ( int ) ) ) ;
175
176
}
176
177
177
178
private void SkipString ( )
178
179
{
180
+ // Note: this method assumes that it is called either during object
181
+ // construction or within another method that locks on this.
182
+
179
183
int stringLength = _store . Read7BitEncodedInt ( ) ;
180
184
if ( stringLength < 0 )
181
185
{
@@ -234,6 +238,7 @@ public IDictionaryEnumerator GetEnumerator()
234
238
return new ResourceEnumerator ( this ) ;
235
239
}
236
240
241
+ // Called from RuntimeResourceSet
237
242
internal ResourceEnumerator GetEnumeratorInternal ( )
238
243
{
239
244
return new ResourceEnumerator ( this ) ;
@@ -243,6 +248,7 @@ internal ResourceEnumerator GetEnumeratorInternal()
243
248
// To read the data, seek to _dataSectionOffset + dataPos, then
244
249
// read the resource type & data.
245
250
// This does a binary search through the names.
251
+ // Called from RuntimeResourceSet
246
252
internal int FindPosForResource ( string name )
247
253
{
248
254
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
@@ -327,6 +333,8 @@ internal int FindPosForResource(string name)
327
333
private unsafe bool CompareStringEqualsName ( string name )
328
334
{
329
335
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
336
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
337
+
330
338
int byteLen = _store . Read7BitEncodedInt ( ) ;
331
339
if ( byteLen < 0 )
332
340
{
@@ -459,68 +467,74 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
459
467
}
460
468
461
469
// This takes a virtual offset into the data section and reads a String
462
- // from that location.
463
- // Anyone who calls LoadObject should make sure they take a lock so
464
- // no one can cause us to do a seek in here.
470
+ // from that location. Called from RuntimeResourceSet
465
471
internal string ? LoadString ( int pos )
466
472
{
467
473
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
468
- _store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
469
- string ? s = null ;
470
- int typeIndex = _store . Read7BitEncodedInt ( ) ;
471
- if ( _version == 1 )
472
- {
473
- if ( typeIndex == - 1 )
474
- return null ;
475
- if ( FindType ( typeIndex ) != typeof ( string ) )
476
- throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , FindType ( typeIndex ) . FullName ) ) ;
477
- s = _store . ReadString ( ) ;
478
- }
479
- else
474
+
475
+ lock ( this )
480
476
{
481
- ResourceTypeCode typeCode = ( ResourceTypeCode ) typeIndex ;
482
- if ( typeCode != ResourceTypeCode . String && typeCode != ResourceTypeCode . Null )
477
+ _store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
478
+ string ? s = null ;
479
+ int typeIndex = _store . Read7BitEncodedInt ( ) ;
480
+ if ( _version == 1 )
483
481
{
484
- string ? typeString ;
485
- if ( typeCode < ResourceTypeCode . StartOfUserTypes )
486
- typeString = typeCode . ToString ( ) ;
487
- else
488
- typeString = FindType ( typeCode - ResourceTypeCode . StartOfUserTypes ) . FullName ;
489
- throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , typeString ) ) ;
490
- }
491
- if ( typeCode == ResourceTypeCode . String ) // ignore Null
482
+ if ( typeIndex == - 1 )
483
+ return null ;
484
+ if ( FindType ( typeIndex ) != typeof ( string ) )
485
+ throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , FindType ( typeIndex ) . FullName ) ) ;
492
486
s = _store . ReadString ( ) ;
487
+ }
488
+ else
489
+ {
490
+ ResourceTypeCode typeCode = ( ResourceTypeCode ) typeIndex ;
491
+ if ( typeCode != ResourceTypeCode . String && typeCode != ResourceTypeCode . Null )
492
+ {
493
+ string ? typeString ;
494
+ if ( typeCode < ResourceTypeCode . StartOfUserTypes )
495
+ typeString = typeCode . ToString ( ) ;
496
+ else
497
+ typeString = FindType ( typeCode - ResourceTypeCode . StartOfUserTypes ) . FullName ;
498
+ throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , typeString ) ) ;
499
+ }
500
+ if ( typeCode == ResourceTypeCode . String ) // ignore Null
501
+ s = _store . ReadString ( ) ;
502
+ }
503
+ return s ;
493
504
}
494
- return s ;
495
505
}
496
506
497
507
// Called from RuntimeResourceSet
498
508
internal object ? LoadObject ( int pos )
499
509
{
500
- if ( _version == 1 )
501
- return LoadObjectV1 ( pos ) ;
502
- return LoadObjectV2 ( pos , out _ ) ;
510
+ lock ( this )
511
+ {
512
+ return _version == 1 ? LoadObjectV1 ( pos ) : LoadObjectV2 ( pos , out _ ) ;
513
+ }
503
514
}
504
515
516
+ // Called from RuntimeResourceSet
505
517
internal object ? LoadObject ( int pos , out ResourceTypeCode typeCode )
506
518
{
507
- if ( _version == 1 )
519
+ lock ( this )
508
520
{
509
- object ? o = LoadObjectV1 ( pos ) ;
510
- typeCode = ( o is string ) ? ResourceTypeCode . String : ResourceTypeCode . StartOfUserTypes ;
511
- return o ;
521
+ if ( _version == 1 )
522
+ {
523
+ object ? o = LoadObjectV1 ( pos ) ;
524
+ typeCode = ( o is string ) ? ResourceTypeCode . String : ResourceTypeCode . StartOfUserTypes ;
525
+ return o ;
526
+ }
527
+ return LoadObjectV2 ( pos , out typeCode ) ;
512
528
}
513
- return LoadObjectV2 ( pos , out typeCode ) ;
514
529
}
515
530
516
531
// This takes a virtual offset into the data section and reads an Object
517
532
// from that location.
518
- // Anyone who calls LoadObject should make sure they take a lock so
519
- // no one can cause us to do a seek in here.
520
- internal object ? LoadObjectV1 ( int pos )
533
+ private object ? LoadObjectV1 ( int pos )
521
534
{
522
535
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
523
536
Debug . Assert ( _version == 1 , ".resources file was not a V1 .resources file!" ) ;
537
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
524
538
525
539
try
526
540
{
@@ -540,6 +554,8 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
540
554
541
555
private object ? _LoadObjectV1 ( int pos )
542
556
{
557
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
558
+
543
559
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
544
560
int typeIndex = _store . Read7BitEncodedInt ( ) ;
545
561
if ( typeIndex == - 1 )
@@ -596,10 +612,11 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
596
612
}
597
613
}
598
614
599
- internal object ? LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
615
+ private object ? LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
600
616
{
601
617
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
602
618
Debug . Assert ( _version >= 2 , ".resources file was not a V2 (or higher) .resources file!" ) ;
619
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
603
620
604
621
try
605
622
{
@@ -619,6 +636,8 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
619
636
620
637
private object ? _LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
621
638
{
639
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
640
+
622
641
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
623
642
typeCode = ( ResourceTypeCode ) _store . Read7BitEncodedInt ( ) ;
624
643
@@ -755,6 +774,7 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
755
774
[ MemberNotNull ( nameof ( _typeNamePositions ) ) ]
756
775
private void ReadResources ( )
757
776
{
777
+ Debug . Assert ( ! Monitor . IsEntered ( this ) ) ; // only called during init
758
778
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
759
779
760
780
try
@@ -777,6 +797,8 @@ private void ReadResources()
777
797
[ MemberNotNull ( nameof ( _typeNamePositions ) ) ]
778
798
private void _ReadResources ( )
779
799
{
800
+ Debug . Assert ( ! Monitor . IsEntered ( this ) ) ; // only called during init
801
+
780
802
// Read ResourceManager header
781
803
// Check for magic number
782
804
int magicNum = _store . ReadInt32 ( ) ;
@@ -962,6 +984,8 @@ private Type FindType(int typeIndex)
962
984
"Custom readers as well as custom objects on the resources file are not observable by the trimmer and so required assemblies, types and members may be removed." ) ]
963
985
private Type UseReflectionToGetType ( int typeIndex )
964
986
{
987
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
988
+
965
989
long oldPos = _store . BaseStream . Position ;
966
990
try
967
991
{
@@ -1000,6 +1024,8 @@ private Type UseReflectionToGetType(int typeIndex)
1000
1024
private string TypeNameFromTypeCode ( ResourceTypeCode typeCode )
1001
1025
{
1002
1026
Debug . Assert ( typeCode >= 0 , "can't be negative" ) ;
1027
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
1028
+
1003
1029
if ( typeCode < ResourceTypeCode . StartOfUserTypes )
1004
1030
{
1005
1031
Debug . Assert ( ! string . Equals ( typeCode . ToString ( ) , "LastPrimitive" ) , "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers." ) ;
@@ -1077,31 +1103,31 @@ public DictionaryEntry Entry
1077
1103
if ( ! _currentIsValid ) throw new InvalidOperationException ( SR . InvalidOperation_EnumNotStarted ) ;
1078
1104
if ( _reader . _resCache == null ) throw new InvalidOperationException ( SR . ResourceReaderIsClosed ) ;
1079
1105
1080
- string key ;
1106
+ string key = _reader . AllocateStringForNameIndex ( _currentName , out _dataPosition ) ; // AllocateStringForNameIndex could lock on _reader
1107
+
1081
1108
object ? value = null ;
1082
- lock ( _reader )
1083
- { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
1084
- lock ( _reader . _resCache )
1109
+ // Lock the cache first, then the reader (in this case, we don't actually need to lock the reader and cache at the same time).
1110
+ // Lock order MUST match RuntimeResourceSet.GetObject to avoid deadlock.
1111
+ Debug . Assert ( ! Monitor . IsEntered ( _reader ) ) ;
1112
+ lock ( _reader . _resCache )
1113
+ {
1114
+ if ( _reader . _resCache . TryGetValue ( key , out ResourceLocator locator ) )
1085
1115
{
1086
- key = _reader . AllocateStringForNameIndex ( _currentName , out _dataPosition ) ; // AllocateStringForNameIndex could lock on _reader
1087
- if ( _reader . _resCache . TryGetValue ( key , out ResourceLocator locator ) )
1088
- {
1089
- value = locator . Value ;
1090
- }
1091
- if ( value == null )
1092
- {
1093
- if ( _dataPosition == - 1 )
1094
- value = _reader . GetValueForNameIndex ( _currentName ) ;
1095
- else
1096
- value = _reader . LoadObject ( _dataPosition ) ;
1097
- // If enumeration and subsequent lookups happen very
1098
- // frequently in the same process, add a ResourceLocator
1099
- // to _resCache here. But WinForms enumerates and
1100
- // just about everyone else does lookups. So caching
1101
- // here may bloat working set.
1102
- }
1116
+ value = locator . Value ;
1103
1117
}
1104
1118
}
1119
+ if ( value is null )
1120
+ {
1121
+ if ( _dataPosition == - 1 )
1122
+ value = _reader . GetValueForNameIndex ( _currentName ) ;
1123
+ else
1124
+ value = _reader . LoadObject ( _dataPosition ) ;
1125
+ // If enumeration and subsequent lookups happen very
1126
+ // frequently in the same process, add a ResourceLocator
1127
+ // to _resCache here (we'll also need to extend the lock block!).
1128
+ // But WinForms enumerates and just about everyone else does lookups.
1129
+ // So caching here may bloat working set.
1130
+ }
1105
1131
return new DictionaryEntry ( key , value ) ;
1106
1132
}
1107
1133
}
0 commit comments