Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shouldn't TypeDictionary be thread-safe? #12

Closed
AArnott opened this issue Oct 23, 2024 · 3 comments · Fixed by #68
Closed

Shouldn't TypeDictionary be thread-safe? #12

AArnott opened this issue Oct 23, 2024 · 3 comments · Fixed by #68

Comments

@AArnott
Copy link
Contributor

AArnott commented Oct 23, 2024

I know it's just in the Samples area, but as sample tend to be copied for production code, shouldn't TypeDictionary be thread-safe?

It looks like maybe it doesn't need to be for present purposes because the existing uses create a new instance of Builder classes so TypeDictionary is never shared across threads. But that means multiple type graphs that overlap will produce redundant serializers, if I understand correctly.

AArnott added a commit to AArnott/PolyType that referenced this issue Oct 24, 2024
It's very rough and limited. And it assumes eiriktsarpalis#12 is addressed.
AArnott added a commit to AArnott/PolyType that referenced this issue Oct 24, 2024
It's very rough and limited. And it assumes eiriktsarpalis#12 is addressed.
@eiriktsarpalis
Copy link
Owner

It isn't intended to be thread-safe, no. The only differentiator for this type over a regular Dictionary<Type, object> is that it facilitates delayed serializer construction in cases where you have recursive types. As such it can contain incomplete values that will throw an exception if you try to exercise them while their type graph is still begin traversed.

But that means multiple type graphs that overlap will produce redundant serializers, if I understand correctly.

That's correct. There's no real solution I know of other than taking a lock on the dictionary while a particular type graph is being built. That's probably acceptable given it's a one-time cost per type (System.Text.Json also does it this way). Another alternative is keeping a global ConcurrentDictionary<Type, object> that you use to optimistically commit all values from a local TypeDictionary.

@eiriktsarpalis
Copy link
Owner

Another alternative is keeping a global ConcurrentDictionary<Type, object> that you use to optimistically commit all values from a local TypeDictionary.

FWIW the F# variant of the library defines a thread-safe TypeCache type that can be used to create local "generation contexts" that commit their results back to the global cache once disposed:

https://github.com/eiriktsarpalis/TypeShape/blob/ddc64e8d8f8254a172e320b46794b5740c404780/samples/TypeShape.Samples/equality-comparer.fs#L14-L15

Maybe it's useful to port this pattern to C# as well.

@AArnott
Copy link
Contributor Author

AArnott commented Oct 24, 2024

I suppose making that collection thread-safe isn't a good solution because if multiple graphs were being constructed concurrently, there's no guarantee that one type isn't requested and activated before it's "ready".
I'm thinking about a large graph that includes a recursive type, so it sticks the recursive type into the graph provisionally, and keeps working. In the meantime another graph is built that needs that type, finds it in the collection, and quickly exits claiming it's done, yet the type isn't fully assembled, and an exception is thrown.

@AArnott AArnott closed this as not planned Won't fix, can't repro, duplicate, stale Oct 26, 2024
eiriktsarpalis added a commit that referenced this issue Nov 1, 2024
* Add MsgPackSerializer sample

It's very rough and limited. And it assumes #12 is addressed.

* Add msgpack benchmarks

* Perf improvements

* More perf improvements

* Use list instead of dictionary for writing properties

* Remove unnecessary code

* Fix thread-safety

* Generate `IMessagePackFormatter<T>` implementations

* Remove extra level of indirection

* Fix failing unit tests

* Handle more primitive types and defer missing construction complaint till deserialization

* Fix DelayedFormatter pattern

* Store primitive formatters in a static dictionary

* Add support for enums

* Got non-default constructors working

* Remove workaround that is no longer required

* Serialize enum as underlying type rather than string

* Add msgpack Theory

* Replace msgpack sample with a reference

---------

Co-authored-by: Eirik Tsarpalis <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants