mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-06 17:10:13 +00:00
Now this is a nice bug that was found accidentally by ArchiBoT...
ReaderWriterLockSlim() is very decent solution, but it's thread-based, and we're using our ConcurrentHashSet in mixed async/sync context. This means that if we use something like:
foreach (var item in concHashSet) {
await AnythingAsync().ConfigureAwait(false);
}
It's totally possible that we'll request read lock as thread 1, and release the read lock as thread 2, which will lead to RWLock exception => System.Threading.SynchronizationLockException: The read lock is being released without being held.
Fortunately it looks like we didn't have any scenario like this in ASF, as this was possible only when we async/await while enumerating over ConcurrentHashSet, so that specific bug didn't affect ASF codebase (yet). Still, I must fix this as current implementation is not thread-safe, so our HashSet is in fact not concurrent in the first place.
I analyzed possible solutions and there are basically 3: either using ConcurrentDictionary and wrapping around it, replacing lock with SemaphoreSlim, or using third-party AsyncReaderWriterLock from StephenCleary. SemaphoreSlim entirely kills the concept of multiple readers one writer, and could affect performance negatively, moreover - it doesn't support upgreadable lock scenario we have with ReplaceIfNeededWith(). Concurrent dictionary would be nice if I didn't have that awful memory hit from storing mandatory pointless value, plus I don't really like concept of wrapping around conc dictionary if I can simply use it right away and drop my conc hashset entirely. AsyncReaderWriterLock seem to be really well written, and works on Mono + should be compatible with .NET core in the future, so we should go for it as it's the best bet both performance-wise and memory-wise.
This brings another package dependency and changes a bit backend of ConcurrentHashSet
52 lines
2.4 KiB
XML
52 lines
2.4 KiB
XML
<?xml version="1.0"?>
|
|
<doc>
|
|
<assembly>
|
|
<name>System.IO</name>
|
|
</assembly>
|
|
<members>
|
|
<member name="T:System.Strings">
|
|
<summary>
|
|
A strongly-typed resource class, for looking up localized strings, etc.
|
|
</summary>
|
|
</member>
|
|
<member name="P:System.Strings.ResourceManager">
|
|
<summary>
|
|
Returns the cached ResourceManager instance used by this class.
|
|
</summary>
|
|
</member>
|
|
<member name="P:System.Strings.Culture">
|
|
<summary>
|
|
Overrides the current thread's CurrentUICulture property for all
|
|
resource lookups using this strongly typed resource class.
|
|
</summary>
|
|
</member>
|
|
<member name="P:System.Strings.GenericInvalidData">
|
|
<summary>
|
|
Looks up a localized string similar to Found invalid data while decoding..
|
|
</summary>
|
|
</member>
|
|
<member name="T:System.IO.InvalidDataException">
|
|
<summary>
|
|
The exception that is thrown when a data stream is in an invalid format.
|
|
</summary>
|
|
</member>
|
|
<member name="M:System.IO.InvalidDataException.#ctor">
|
|
<summary>
|
|
Initializes a new instance of the <see cref="T:System.IO.InvalidDataException" /> class.
|
|
</summary>
|
|
</member>
|
|
<member name="M:System.IO.InvalidDataException.#ctor(System.String)">
|
|
<summary>
|
|
Initializes a new instance of the <see cref="T:System.IO.InvalidDataException" /> class with a specified error message.
|
|
</summary>
|
|
<param name="message">The error message that explains the reason for the exception.</param>
|
|
</member>
|
|
<member name="M:System.IO.InvalidDataException.#ctor(System.String,System.Exception)">
|
|
<summary>
|
|
Initializes a new instance of the <see cref="T:System.IO.InvalidDataException" /> class with a reference to the inner exception that is the cause of this exception.</summary>
|
|
<param name="message">The error message that explains the reason for the exception.</param>
|
|
<param name="innerException">The exception that is the cause of the current exception. If the <paramref name="innerException" /> parameter is not null, the current exception is raised in a catch block that handles the inner exception.</param>
|
|
</member>
|
|
</members>
|
|
</doc>
|