juusirantala opened a new issue, #3996:
URL: https://github.com/apache/fory/issues/3996

   ### Search before asking
   
   - [x] I had searched in the [issues](https://github.com/apache/fory/issues) 
and found no similar issues.
   
   
   ### Version
   
   fory-core 1.7.0
   
   ### Component(s)
   
   Rust
   
   ### Minimal reproduce step
   
   `TypeResolver` holds `Rc<TypeInfo>` and `Rc<MetaString>` in most of its 
fields, but:
   
   ```rust
   // rust/fory-core/src/resolver/type_resolver.rs
   unsafe impl Send for TypeResolver {}
   unsafe impl Sync for TypeResolver {}
   ```
   
   `get_type_info_by_id`, `get_type_info_by_name`, `get_provider_type_info` and 
`get_target_type_info` all take `&self` and clone one of those `Rc`s out. 
`WriteContext` and `ReadContext` own a resolver, hand it out through 
`get_type_resolver(&self)`, and are `Send + Sync` themselves, so a custom 
serializer that parallelises hits this.
   
   ## Repro
   
   `fory-core = "=1.7.0"`, otherwise an empty lib crate.
   
   ```rust
   use fory_core::{Error, Fory, ReadContext, Serializer, WriteContext};
   
   struct Blob(Vec<i32>);
   struct BlobSerializer;
   
   impl Serializer for BlobSerializer {
       type Target = Blob;
   
       fn write_data(_value: &Blob, context: &mut WriteContext) -> Result<(), 
Error> {
           let ctx: &WriteContext = context;
           std::thread::scope(|s| {
               s.spawn(|| {
                   ctx.get_type_resolver().get_type_info_by_id(1);
               });
               ctx.get_type_resolver().get_type_info_by_id(1);
           });
           Ok(())
       }
   
       fn read_data(_context: &mut ReadContext) -> Result<Blob, Error> {
           Ok(Blob(Vec::new()))
       }
   }
   
   #[test]
   fn realistic_parallel_custom_serializer() {
       let mut fory = Fory::default();
       fory.register_serializer::<BlobSerializer>(500).unwrap();
       fory.serialize_with::<BlobSerializer>(&Blob(vec![1, 2, 3])).unwrap();
   }
   ```
   
   `cargo +nightly miri test --lib`, nightly-2026-08-28:
   
   ```
   error: Undefined Behavior: Data race detected between (1) non-atomic write on
   thread `realistic_paral` and (2) non-atomic read on thread `unnamed-2`
      --> library/core/src/cell.rs:558:18
   
   help: and (1) occurred earlier here
      --> src/lib.rs:19:59
   
       = note: stack backtrace:
               2: <RcInner<fory_core::TypeInfo> as RcInnerPtr>::inc_strong
               3: <Rc<fory_core::TypeInfo> as Clone>::clone
               5: fory_core::TypeResolver::get_type_info_by_id
                  at fory-core-1.7.0/src/resolver/type_resolver.rs:795
               6: <BlobSerializer as 
Serializer>::write_data::{closure#0}::{closure#0}
   ```
   
   Shorter version of the same thing, no serializer needed:
   
   ```rust
   #[test]
   fn minimal_shared_type_resolver() {
       let r = std::sync::Arc::new(fory_core::TypeResolver::default());
       let r2 = std::sync::Arc::clone(&r);
       std::thread::scope(|s| {
           s.spawn(move || { r2.get_type_info_by_id(1); });
           r.get_type_info_by_id(1);
       });
   }
   ```
   
   ## Scope
   
   Sharing a `Fory` and serializing from several threads does not race on 
1.7.0. `with_write_context` deep-clones the resolver into a thread-local 
context (`Rc::new(rc.deep_clone())` in `TypeResolver::clone`), and the 
`register*` methods all take `&mut self`. So the documented "share one instance 
across threads" pattern is fine today. It's the deep clone doing that work, 
though, not the type system.
   
   
   ### What did you expect to see?
   
   Sharing a `&WriteContext` across threads shouldn't compile: `TypeResolver` 
holds `Rc`, so neither it nor the contexts that own it are actually Sync. 
Failing that, if it does compile, no data race.
   
   
   ### What did you see instead?
   
   It compiles, and the two threads race the Rc strong count. Miri:
   
   ```
   error: Undefined Behavior: Data race detected between (1) non-atomic write on
   thread `realistic_paral` and (2) non-atomic read on thread `unnamed-2`
      --> library/core/src/cell.rs:558:18
               3: <Rc<fory_core::TypeInfo> as Clone>::clone
               5: fory_core::TypeResolver::get_type_info_by_id
   ```
   
   
   ### Anything Else?
   
   Haven't tested these, just noticed them nearby:
   
   - `RefReader` is `Send + Sync` holding a `Vec<Box<dyn Any>>`, which is 
neither. Comment above it already says `// danger but useful for multi-thread`.
   - `ReadContext` / `WriteContext` have a safety comment saying concurrent 
access is UB, which sits oddly on a type declaring `Sync`.
   - `Reader` is back to `&'a [u8]` + `usize`, so its asserts look redundant 
rather than wrong.
   
   Looks like the set came in with #2737, when `Reader` still used raw 
pointers..
   
   
   ### Are you willing to submit a PR?
   
   - [ ] I'm willing to submit a PR!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to