Skip to content

Commit b85458e

Browse files
committed
fix: remove noncausal registry policies
1 parent 3744b40 commit b85458e

47 files changed

Lines changed: 230 additions & 676 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/languages/java.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,6 @@ Load this file when changing anything under `java/` or when Java drives a cross-
101101
- `ThreadSafeFory.execute` exposes one borrowed child only for the callback. Do not retain that
102102
child or register through it; use the facade registration methods so every current and future
103103
child receives the same setup.
104-
- A serializer instance registered on a thread-safe facade must implement `Shareable`. Resolver-
105-
local serializers use the class, resolver-factory, or module path so every child runtime owns its
106-
instance; never replay one resolver-bound serializer across children.
107104
- Serializer completion used by lazy, JIT, and generated serializers is an internal resolver-owned
108105
operation. It remains valid after registration freezes and must not be treated as explicit
109106
registration.

.agents/languages/python.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,12 @@ Load this file when changing `python/`, Cython serialization, or Python xlang be
2525
collisions before publishing resolver maps. Lazy TypeDef completion preserves a configured
2626
serializer, does not retain partial state, and restores the prior serializer and TypeDef after
2727
failed completion without adding a lifecycle state.
28-
- `ThreadSafeFory` accepts serializer classes or factories and constructs a serializer for each
29-
child resolver. It must reject resolver-bound serializer instances instead of replaying one
30-
instance across pooled children.
3128
- Registry freeze prohibits explicit type and serializer registration after the first root; it
3229
does not prohibit native runtime type resolution. Non-strict native writes may discover runtime
3330
classes or callables, and reads may resolve those authorized by the deserialization policy. Both
3431
paths may materialize resolver-owned type information or serializer cache entries without
3532
creating or changing an explicit type, serializer, ID, name, or policy registration. Do not
3633
describe these operations as late registration.
37-
- Function serialization writes captured globals as a data-only exact `dict`. Keep the reader's
38-
exact-type check before sizing or merging the namespace; a dict subclass or other mapping must not
39-
introduce runtime behavior into function reconstruction.
4034
- Use explicit Cython fields and methods for fixed hot-path shapes. Avoid `__getattr__`, generic `object` fields, public bridge internals, or `Fory` backreferences where ownership can stay explicit.
4135
- Keep Python and Cython context/ref-tracking branch conditions and stack mutations semantically aligned unless a documented intentional difference exists.
4236
- Root deserialization graph memory budget state belongs to pure-Python and Cython `ReadContext`.

cpp/fory/serialization/collection_serializer.h

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,6 @@ inline bool read_declared_same_type_collection(Container &result,
562562
auto elem = Serializer<T>::read(ctx, RefMode::None, false);
563563
collection_insert(result, std::move(elem));
564564
}
565-
if (FORY_PREDICT_FALSE(ctx.has_error())) {
566-
return false;
567-
}
568565
return true;
569566
}
570567

@@ -586,15 +583,9 @@ inline bool read_declared_same_type_collection(Container &result,
586583
checkpoint_byte = ctx.buffer().logical_reader_index();
587584
}
588585
}
589-
if (FORY_PREDICT_FALSE(ctx.has_error())) {
590-
return false;
591-
}
592-
if (checkpoint_item != length &&
593-
FORY_PREDICT_FALSE(!detail::settle_unbacked_container_items(
594-
ctx, length - checkpoint_item, checkpoint_byte))) {
595-
return false;
596-
}
597-
return true;
586+
return checkpoint_item == length ||
587+
detail::settle_unbacked_container_items(ctx, length - checkpoint_item,
588+
checkpoint_byte);
598589
}
599590

600591
template <typename T, typename Alloc>
@@ -625,15 +616,10 @@ read_declared_same_type_collection(std::forward_list<T, Alloc> &result,
625616
}
626617
}
627618
}
628-
if (FORY_PREDICT_FALSE(ctx.has_error())) {
629-
return false;
630-
}
631619
if constexpr (!read_data_always_advances_v<T>) {
632-
if (checkpoint_item != length &&
633-
FORY_PREDICT_FALSE(!detail::settle_unbacked_container_items(
634-
ctx, length - checkpoint_item, checkpoint_byte))) {
635-
return false;
636-
}
620+
return checkpoint_item == length ||
621+
detail::settle_unbacked_container_items(
622+
ctx, length - checkpoint_item, checkpoint_byte);
637623
}
638624
return true;
639625
}
@@ -679,15 +665,10 @@ read_same_type_info_collection_body(Container &result, ReadContext &ctx,
679665
}
680666
}
681667
}
682-
if (FORY_PREDICT_FALSE(ctx.has_error())) {
683-
return false;
684-
}
685668
if constexpr (MeasureProgress) {
686-
if (checkpoint_item != length &&
687-
FORY_PREDICT_FALSE(!detail::settle_unbacked_container_items(
688-
ctx, length - checkpoint_item, checkpoint_byte))) {
689-
return false;
690-
}
669+
return checkpoint_item == length ||
670+
detail::settle_unbacked_container_items(
671+
ctx, length - checkpoint_item, checkpoint_byte);
691672
}
692673
return true;
693674
}
@@ -720,15 +701,10 @@ inline bool read_same_type_info_collection_body(
720701
}
721702
}
722703
}
723-
if (FORY_PREDICT_FALSE(ctx.has_error())) {
724-
return false;
725-
}
726704
if constexpr (MeasureProgress) {
727-
if (checkpoint_item != length &&
728-
FORY_PREDICT_FALSE(!detail::settle_unbacked_container_items(
729-
ctx, length - checkpoint_item, checkpoint_byte))) {
730-
return false;
731-
}
705+
return checkpoint_item == length ||
706+
detail::settle_unbacked_container_items(
707+
ctx, length - checkpoint_item, checkpoint_byte);
732708
}
733709
return true;
734710
}

cpp/fory/serialization/serialization_test.cc

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include <chrono>
3030
#include <cstdint>
3131
#include <cstring>
32-
#include <forward_list>
3332
#include <limits>
3433
#include <map>
3534
#include <memory>
@@ -984,49 +983,6 @@ TEST(SerializationTest, SkipNoneListConsumesBudget) {
984983
ASSERT_TRUE(ctx.has_error());
985984
}
986985

987-
TEST(SerializationTest, LastElementErrorSafepoints) {
988-
Config config;
989-
990-
std::vector<uint8_t> declared_bytes{2};
991-
Buffer declared_buffer(declared_bytes);
992-
ReadContext declared_ctx(config, std::make_unique<TypeResolver>());
993-
declared_ctx.attach(declared_buffer);
994-
std::vector<int32_t> declared_values;
995-
EXPECT_FALSE(read_declared_same_type_collection<int32_t>(declared_values,
996-
declared_ctx, 2));
997-
EXPECT_TRUE(declared_ctx.has_error());
998-
999-
std::vector<uint8_t> forward_bytes{2};
1000-
Buffer forward_buffer(forward_bytes);
1001-
ReadContext forward_ctx(config, std::make_unique<TypeResolver>());
1002-
forward_ctx.attach(forward_buffer);
1003-
std::forward_list<int32_t> forward_values;
1004-
EXPECT_FALSE(read_declared_same_type_collection<int32_t>(forward_values,
1005-
forward_ctx, 2));
1006-
EXPECT_TRUE(forward_ctx.has_error());
1007-
1008-
std::vector<uint8_t> type_info_bytes{2};
1009-
Buffer type_info_buffer(type_info_bytes);
1010-
ReadContext type_info_ctx(config, std::make_unique<TypeResolver>());
1011-
type_info_ctx.attach(type_info_buffer);
1012-
TypeInfo type_info;
1013-
type_info.harness.read_data_always_advances = true;
1014-
std::vector<int32_t> type_info_values;
1015-
EXPECT_FALSE(read_same_type_info_collection<int32_t>(
1016-
type_info_values, type_info_ctx, 2, type_info));
1017-
EXPECT_TRUE(type_info_ctx.has_error());
1018-
1019-
std::vector<uint8_t> measured_bytes{2};
1020-
Buffer measured_buffer(measured_bytes);
1021-
ReadContext measured_ctx(config, std::make_unique<TypeResolver>());
1022-
measured_ctx.attach(measured_buffer);
1023-
type_info.harness.read_data_always_advances = false;
1024-
std::vector<int32_t> measured_values;
1025-
EXPECT_FALSE(read_same_type_info_collection<int32_t>(
1026-
measured_values, measured_ctx, 2, type_info));
1027-
EXPECT_TRUE(measured_ctx.has_error());
1028-
}
1029-
1030986
// ============================================================================
1031987
// Character Type Tests (C++ native only)
1032988
// ============================================================================

cpp/fory/serialization/struct_serializer.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,9 +4615,6 @@ struct Serializer<T, std::enable_if_t<is_fory_serializable_v<T>>> {
46154615
}
46164616
const TypeInfo *type_info = type_info_res.value();
46174617
ctx.write_struct_type_info(type_info);
4618-
if (FORY_PREDICT_FALSE(ctx.has_error())) {
4619-
return;
4620-
}
46214618
}
46224619

46234620
/// Read and validate type info.

csharp/src/Fory/Fory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ public byte[] Serialize<T>(in T value)
177177
_registryFrozen = true;
178178
ByteWriter writer = _writeContext.Writer;
179179
writer.Reset();
180-
// A previous failed root may leave references behind. Reset before serializer lookup,
181-
// because generated or application serializer factories can fail during that lookup.
180+
// Serializer lookup is part of the root and may fail before codec entry, so establish the
181+
// root's clean context before invoking generated or application serializer factories.
182182
_writeContext.ResetFor(writer);
183183
Serializer<T> serializer = _typeResolver.GetSerializer<T>();
184184
WriteHead(writer);

csharp/tests/Fory.Tests/ExternalTypeSerializationTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ public void CustomSerializerReplacesGenerated()
401401
ExternalFields value = new() { Count = 19, Name = "custom" };
402402
ForyRuntime generated = ForyRuntime.Builder().Build();
403403
generated.Register<ExternalFields>(6106);
404+
Assert.Throws<InvalidDataException>(
405+
() => generated.Register<ExternalFields, ExternalFieldsCustomSerializer>(6107));
404406
byte[] generatedBytes = generated.Serialize(value);
405407

406408
ForyRuntime custom = ForyRuntime.Builder().Build();
@@ -411,8 +413,6 @@ public void CustomSerializerReplacesGenerated()
411413
Assert.NotEqual(generatedBytes, customBytes);
412414
Assert.Equal(value.Count, decoded.Count);
413415
Assert.Equal(value.Name, decoded.Name);
414-
Assert.Throws<InvalidOperationException>(
415-
() => generated.Register<ExternalFields, ExternalFieldsCustomSerializer>(6107));
416416
}
417417

418418
[Fact]

csharp/tests/Fory.Tests/RuntimeEdgeCaseTests.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ public override LookupFailureValue ReadData(ReadContext context)
137137
}
138138
}
139139

140-
[ForyStruct]
141140
public sealed class FailingWritePayload
142141
{
143142
public int Value { get; set; }
@@ -834,15 +833,6 @@ public void ThreadSafeDottedNameRoundTrip()
834833
Assert.Equal("custom", decoded.Marker);
835834
}
836835

837-
[Fact]
838-
public void RegistryFreezesAfterSuccessfulRoot()
839-
{
840-
ForyRuntime fory = ForyRuntime.Builder().Build();
841-
Assert.Equal(1, fory.Deserialize<int>(fory.Serialize(1)));
842-
843-
Assert.Throws<InvalidOperationException>(() => fory.Register<FrozenPayload>(710));
844-
}
845-
846836
[Fact]
847837
public void FrozenRegistryRejectsBeforeMutation()
848838
{
@@ -1041,11 +1031,11 @@ public void TrailingFailureKeepsTypeMetaCache(bool useSpan)
10411031

10421032
if (useSpan)
10431033
{
1044-
Assert.ThrowsAny<Exception>(() => DeserializeIntSpan(fory, invalidPayload));
1034+
Assert.Throws<InvalidDataException>(() => DeserializeIntSpan(fory, invalidPayload));
10451035
}
10461036
else
10471037
{
1048-
Assert.ThrowsAny<Exception>(() => fory.Deserialize<int>(invalidPayload));
1038+
Assert.Throws<InvalidDataException>(() => fory.Deserialize<int>(invalidPayload));
10491039
}
10501040

10511041
Assert.True(context.TryGetTypeMetaByHash(firstHash, out _));

docs/object-serialization/java/custom-serializers.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,6 @@ fory.registerSerializer(
212212
CustomMap.class, resolver -> new CustomMapSerializer<>(resolver, CustomMap.class));
213213
```
214214

215-
For `ThreadSafeFory`, pass a serializer class or resolver factory when the serializer is
216-
runtime-local. The facade constructs one instance for each underlying runtime. An instance may be
217-
registered directly only when it implements `Shareable`. Construct a runtime-local union serializer
218-
inside a `ForyModule`, which is installed separately into every underlying runtime.
219-
220215
## Shareability
221216

222217
Implement the `Shareable` marker interface when the serializer can be safely reused across

docs/object-serialization/python/configuration.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ class ThreadSafeFory:
5757
def __init__(self, fory_factory=None, **kwargs)
5858
```
5959

60-
Pass either a no-argument `fory_factory` that returns a configured `Fory` instance, or pass normal
61-
`Fory` construction options through `**kwargs`.
60+
When supplied, `fory_factory` creates each pooled `Fory` instance. Otherwise, `**kwargs` are passed
61+
to the normal `Fory` constructor.
6262

6363
## Parameters
6464

@@ -84,7 +84,6 @@ Pass either a no-argument `fory_factory` that returns a configured `Fory` instan
8484

8585
```python
8686
fory = pyfory.Fory(xlang=True)
87-
thread_safe_fory = pyfory.ThreadSafeFory(xlang=True)
8887

8988
# Serialization (serialize/deserialize are identical to dumps/loads)
9089
data: bytes = fory.serialize(obj)
@@ -94,14 +93,11 @@ obj = fory.deserialize(data)
9493
data: bytes = fory.dumps(obj)
9594
obj = fory.loads(data)
9695

97-
# Direct Fory registration by id; serializer instances belong to that Fory.
96+
# Type registration by id
9897
fory.register(MyClass, type_id=123)
9998
fory.register(MyClass, type_id=123, serializer=custom_serializer)
10099

101-
# ThreadSafeFory constructs one serializer per pooled child from a class or factory.
102-
thread_safe_fory.register(MyClass, type_id=123, serializer=CustomSerializer)
103-
104-
# Direct Fory registration by name
100+
# Type registration by name
105101
fory.register(MyClass, name="my.package.MyClass")
106102
fory.register(MyClass, name="my.package.MyClass", serializer=custom_serializer)
107103
```

0 commit comments

Comments
 (0)