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 )
@@ -173,13 +174,16 @@ private unsafe void Dispose(bool disposing)
173
174
}
174
175
}
175
176
176
- internal static unsafe int ReadUnalignedI4 ( int * p )
177
+ private static unsafe int ReadUnalignedI4 ( int * p )
177
178
{
178
179
return BinaryPrimitives . ReadInt32LittleEndian ( new ReadOnlySpan < byte > ( p , sizeof ( int ) ) ) ;
179
180
}
180
181
181
182
private void SkipString ( )
182
183
{
184
+ // Note: this method assumes that it is called either during object
185
+ // construction or within another method that locks on this.
186
+
183
187
int stringLength = _store . Read7BitEncodedInt ( ) ;
184
188
if ( stringLength < 0 )
185
189
{
@@ -238,6 +242,7 @@ public IDictionaryEnumerator GetEnumerator()
238
242
return new ResourceEnumerator ( this ) ;
239
243
}
240
244
245
+ // Called from RuntimeResourceSet
241
246
internal ResourceEnumerator GetEnumeratorInternal ( )
242
247
{
243
248
return new ResourceEnumerator ( this ) ;
@@ -247,6 +252,7 @@ internal ResourceEnumerator GetEnumeratorInternal()
247
252
// To read the data, seek to _dataSectionOffset + dataPos, then
248
253
// read the resource type & data.
249
254
// This does a binary search through the names.
255
+ // Called from RuntimeResourceSet
250
256
internal int FindPosForResource ( string name )
251
257
{
252
258
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
@@ -331,6 +337,8 @@ internal int FindPosForResource(string name)
331
337
private unsafe bool CompareStringEqualsName ( string name )
332
338
{
333
339
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
340
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
341
+
334
342
int byteLen = _store . Read7BitEncodedInt ( ) ;
335
343
if ( byteLen < 0 )
336
344
{
@@ -463,68 +471,74 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
463
471
}
464
472
465
473
// This takes a virtual offset into the data section and reads a String
466
- // from that location.
467
- // Anyone who calls LoadObject should make sure they take a lock so
468
- // no one can cause us to do a seek in here.
474
+ // from that location. Called from RuntimeResourceSet
469
475
internal string ? LoadString ( int pos )
470
476
{
471
477
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
472
- _store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
473
- string ? s = null ;
474
- int typeIndex = _store . Read7BitEncodedInt ( ) ;
475
- if ( _version == 1 )
476
- {
477
- if ( typeIndex == - 1 )
478
- return null ;
479
- if ( FindType ( typeIndex ) != typeof ( string ) )
480
- throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , FindType ( typeIndex ) . FullName ) ) ;
481
- s = _store . ReadString ( ) ;
482
- }
483
- else
478
+
479
+ lock ( this )
484
480
{
485
- ResourceTypeCode typeCode = ( ResourceTypeCode ) typeIndex ;
486
- if ( typeCode != ResourceTypeCode . String && typeCode != ResourceTypeCode . Null )
481
+ _store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
482
+ string ? s = null ;
483
+ int typeIndex = _store . Read7BitEncodedInt ( ) ;
484
+ if ( _version == 1 )
487
485
{
488
- string ? typeString ;
489
- if ( typeCode < ResourceTypeCode . StartOfUserTypes )
490
- typeString = typeCode . ToString ( ) ;
491
- else
492
- typeString = FindType ( typeCode - ResourceTypeCode . StartOfUserTypes ) . FullName ;
493
- throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , typeString ) ) ;
494
- }
495
- if ( typeCode == ResourceTypeCode . String ) // ignore Null
486
+ if ( typeIndex == - 1 )
487
+ return null ;
488
+ if ( FindType ( typeIndex ) != typeof ( string ) )
489
+ throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , FindType ( typeIndex ) . FullName ) ) ;
496
490
s = _store . ReadString ( ) ;
491
+ }
492
+ else
493
+ {
494
+ ResourceTypeCode typeCode = ( ResourceTypeCode ) typeIndex ;
495
+ if ( typeCode != ResourceTypeCode . String && typeCode != ResourceTypeCode . Null )
496
+ {
497
+ string ? typeString ;
498
+ if ( typeCode < ResourceTypeCode . StartOfUserTypes )
499
+ typeString = typeCode . ToString ( ) ;
500
+ else
501
+ typeString = FindType ( typeCode - ResourceTypeCode . StartOfUserTypes ) . FullName ;
502
+ throw new InvalidOperationException ( SR . Format ( SR . InvalidOperation_ResourceNotString_Type , typeString ) ) ;
503
+ }
504
+ if ( typeCode == ResourceTypeCode . String ) // ignore Null
505
+ s = _store . ReadString ( ) ;
506
+ }
507
+ return s ;
497
508
}
498
- return s ;
499
509
}
500
510
501
511
// Called from RuntimeResourceSet
502
512
internal object ? LoadObject ( int pos )
503
513
{
504
- if ( _version == 1 )
505
- return LoadObjectV1 ( pos ) ;
506
- return LoadObjectV2 ( pos , out _ ) ;
514
+ lock ( this )
515
+ {
516
+ return _version == 1 ? LoadObjectV1 ( pos ) : LoadObjectV2 ( pos , out _ ) ;
517
+ }
507
518
}
508
519
520
+ // Called from RuntimeResourceSet
509
521
internal object ? LoadObject ( int pos , out ResourceTypeCode typeCode )
510
522
{
511
- if ( _version == 1 )
523
+ lock ( this )
512
524
{
513
- object ? o = LoadObjectV1 ( pos ) ;
514
- typeCode = ( o is string ) ? ResourceTypeCode . String : ResourceTypeCode . StartOfUserTypes ;
515
- return o ;
525
+ if ( _version == 1 )
526
+ {
527
+ object ? o = LoadObjectV1 ( pos ) ;
528
+ typeCode = ( o is string ) ? ResourceTypeCode . String : ResourceTypeCode . StartOfUserTypes ;
529
+ return o ;
530
+ }
531
+ return LoadObjectV2 ( pos , out typeCode ) ;
516
532
}
517
- return LoadObjectV2 ( pos , out typeCode ) ;
518
533
}
519
534
520
535
// This takes a virtual offset into the data section and reads an Object
521
536
// from that location.
522
- // Anyone who calls LoadObject should make sure they take a lock so
523
- // no one can cause us to do a seek in here.
524
- internal object ? LoadObjectV1 ( int pos )
537
+ private object ? LoadObjectV1 ( int pos )
525
538
{
526
539
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
527
540
Debug . Assert ( _version == 1 , ".resources file was not a V1 .resources file!" ) ;
541
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
528
542
529
543
try
530
544
{
@@ -544,6 +558,8 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
544
558
545
559
private object ? _LoadObjectV1 ( int pos )
546
560
{
561
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
562
+
547
563
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
548
564
int typeIndex = _store . Read7BitEncodedInt ( ) ;
549
565
if ( typeIndex == - 1 )
@@ -600,10 +616,11 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
600
616
}
601
617
}
602
618
603
- internal object ? LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
619
+ private object ? LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
604
620
{
605
621
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
606
622
Debug . Assert ( _version >= 2 , ".resources file was not a V2 (or higher) .resources file!" ) ;
623
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
607
624
608
625
try
609
626
{
@@ -623,6 +640,8 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
623
640
624
641
private object ? _LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
625
642
{
643
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
644
+
626
645
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
627
646
typeCode = ( ResourceTypeCode ) _store . Read7BitEncodedInt ( ) ;
628
647
@@ -759,6 +778,7 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset)
759
778
[ MemberNotNull ( nameof ( _typeNamePositions ) ) ]
760
779
private void ReadResources ( )
761
780
{
781
+ Debug . Assert ( ! Monitor . IsEntered ( this ) ) ; // only called during init
762
782
Debug . Assert ( _store != null , "ResourceReader is closed!" ) ;
763
783
764
784
try
@@ -781,6 +801,8 @@ private void ReadResources()
781
801
[ MemberNotNull ( nameof ( _typeNamePositions ) ) ]
782
802
private void _ReadResources ( )
783
803
{
804
+ Debug . Assert ( ! Monitor . IsEntered ( this ) ) ; // only called during init
805
+
784
806
// Read ResourceManager header
785
807
// Check for magic number
786
808
int magicNum = _store . ReadInt32 ( ) ;
@@ -968,6 +990,8 @@ private Type FindType(int typeIndex)
968
990
"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." ) ]
969
991
private Type UseReflectionToGetType ( int typeIndex )
970
992
{
993
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
994
+
971
995
long oldPos = _store . BaseStream . Position ;
972
996
try
973
997
{
@@ -1006,6 +1030,8 @@ private Type UseReflectionToGetType(int typeIndex)
1006
1030
private string TypeNameFromTypeCode ( ResourceTypeCode typeCode )
1007
1031
{
1008
1032
Debug . Assert ( typeCode >= 0 , "can't be negative" ) ;
1033
+ Debug . Assert ( Monitor . IsEntered ( this ) ) ; // uses _store
1034
+
1009
1035
if ( typeCode < ResourceTypeCode . StartOfUserTypes )
1010
1036
{
1011
1037
Debug . Assert ( ! string . Equals ( typeCode . ToString ( ) , "LastPrimitive" ) , "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers." ) ;
@@ -1083,31 +1109,31 @@ public DictionaryEntry Entry
1083
1109
if ( ! _currentIsValid ) throw new InvalidOperationException ( SR . InvalidOperation_EnumNotStarted ) ;
1084
1110
if ( _reader . _resCache == null ) throw new InvalidOperationException ( SR . ResourceReaderIsClosed ) ;
1085
1111
1086
- string key ;
1112
+ string key = _reader . AllocateStringForNameIndex ( _currentName , out _dataPosition ) ; // AllocateStringForNameIndex could lock on _reader
1113
+
1087
1114
object ? value = null ;
1088
- lock ( _reader )
1089
- { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
1090
- lock ( _reader . _resCache )
1115
+ // 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).
1116
+ // Lock order MUST match RuntimeResourceSet.GetObject to avoid deadlock.
1117
+ Debug . Assert ( ! Monitor . IsEntered ( _reader ) ) ;
1118
+ lock ( _reader . _resCache )
1119
+ {
1120
+ if ( _reader . _resCache . TryGetValue ( key , out ResourceLocator locator ) )
1091
1121
{
1092
- key = _reader . AllocateStringForNameIndex ( _currentName , out _dataPosition ) ; // AllocateStringForNameIndex could lock on _reader
1093
- if ( _reader . _resCache . TryGetValue ( key , out ResourceLocator locator ) )
1094
- {
1095
- value = locator . Value ;
1096
- }
1097
- if ( value == null )
1098
- {
1099
- if ( _dataPosition == - 1 )
1100
- value = _reader . GetValueForNameIndex ( _currentName ) ;
1101
- else
1102
- value = _reader . LoadObject ( _dataPosition ) ;
1103
- // If enumeration and subsequent lookups happen very
1104
- // frequently in the same process, add a ResourceLocator
1105
- // to _resCache here. But WinForms enumerates and
1106
- // just about everyone else does lookups. So caching
1107
- // here may bloat working set.
1108
- }
1122
+ value = locator . Value ;
1109
1123
}
1110
1124
}
1125
+ if ( value is null )
1126
+ {
1127
+ if ( _dataPosition == - 1 )
1128
+ value = _reader . GetValueForNameIndex ( _currentName ) ;
1129
+ else
1130
+ value = _reader . LoadObject ( _dataPosition ) ;
1131
+ // If enumeration and subsequent lookups happen very
1132
+ // frequently in the same process, add a ResourceLocator
1133
+ // to _resCache here (we'll also need to extend the lock block!).
1134
+ // But WinForms enumerates and just about everyone else does lookups.
1135
+ // So caching here may bloat working set.
1136
+ }
1111
1137
return new DictionaryEntry ( key , value ) ;
1112
1138
}
1113
1139
}
0 commit comments