"Can I share this object between threads?" has a different answer for each kind of Bodu type, and getting it wrong rarely fails loudly: a race corrupts state quietly, or shows up once under load. This page gathers the answer for the types you are most likely to keep in a field, register as a singleton, or hand to parallel work, across the whole suite. Every row comes from the type's own API documentation, or, where the documentation says nothing, from its source, and the table says which.
For Bodu.Core, Bodu.Collections, and Bodu.Collections.Concurrent, the thread-safety contracts guide has a row for every public type, with its enumeration behavior; this page summarizes it.
Terms
Term
Meaning
Safe
Designed for concurrent use from any number of threads without your own locking.
Immutable
Cannot change after construction, so sharing is safe.
Read-only safe
Concurrent reads are safe while no thread writes; a write needs exclusive access.
Single-threaded
No internal synchronization: confine the instance to one thread at a time, or lock around every use.
Core Foundations
Types
Contract
Notes
The calendar value sets (DayOfWeekSet and its siblings), Option<T>, Result<T>, Either<TLeft,TRight>
Immutable
Value types; every operation returns a new value.
The Bodu.Threading primitives (AsyncLock, AsyncSemaphore, RateGate, and the rest), Memoizer
Safe
Concurrent use is their purpose.
The extension classes and ThrowHelper
Safe
Stateless; only as safe as the arguments you pass them.
PooledBufferBuilder<T>, XorShiftRandom
Single-threaded
One owner each.
Every mutable collection in Bodu.Collections
Single-threaded, or read-only safe
None takes a lock. Where a lookup rewrites state - EvictingDictionary<TKey,TValue> under a recency policy, SequencedDictionary<TKey,TValue> in access order, DefaultingDictionary<TKey,TValue>'s indexer, DisjointSet<T>.Find - even concurrent readers race.
Enumeration never throws for concurrent modification; the hash set's is weakly consistent and the other three enumerate a snapshot.
Hashing and cryptography
Types
Contract
Notes
Every Bodu.IO.Hashing algorithm (Crc, Fnv1a64, Fletcher32, ...) and check-digit algorithm
Single-threaded
Their remarks say instances are not thread-safe: construct one per thread, or lock. The process-wide CRC lookup-table cache behind Crc is shared and safe.
Every Bodu.Security.Cryptography algorithm instance: the HashAlgorithm, KeyedHashAlgorithm, SymmetricAlgorithm, and AsymmetricAlgorithm types, the mode and AEAD transforms, SecretBytes
Immutable and stateless between calls, so one instance can be a singleton - provided its factory returns a fresh HashAlgorithm on every call, as SHA256.Create does. A factory that hands back one shared algorithm corrupts concurrent computations.
Globalization and calendars
Types
Contract
Notes
NotableDateService
Safe
Immutable once constructed, with no per-query state; share one instance per resource.
NotableDateResource
Immutable
Imports and overrides are applied while it loads.
NotableDateAlgorithmRegistry
Safe
Lookups are lock-free and may run concurrently with registrations.
CachingNotableDateService
Safe
Built to be a shared decorator: concurrent misses for the same cold year coalesce onto one computation, and a reload swaps its state atomically (from its source; the type has no thread statement).
InMemoryNotableDateCache
Safe
Reads and writes are individually thread-safe; two concurrent stores for one territory resolve last-write-wins.
RecurrenceRule
Immutable
Build it once and query it from any thread.
CronExpression, RecurrenceSet, AnchoredInterval
Immutable
No thread statement, but every field is fixed when the value is parsed or built.
RecurrenceRuleBuilder
Single-threaded
A mutable builder; build, then share the rule it produces.
Text and serialization
Types
Contract
Notes
The Utf8…Reader and Utf8…Writer types of every format, and BiffReader / BiffWriter
Single-threaded
ref structs: they live on one thread's stack by construction.
…SerializerOptions
Safe once frozen
An options instance is mutable until its first use (or MakeReadOnly()), then read-only, with its resolved converters cached in concurrent dictionaries. Configure one, and share it. See options lifecycle.
The static serializers (TomlSerializer, YamlSerializer, DelimitedSerializer, and the rest)
Safe
Static entry points; thread safety is that of the options and values you pass.
The mutable node DOMs (TomlNode, IniObject, ...)
Single-threaded
Ordinary object graphs.
YamlDocument
Read-only safe
Element views stay valid until the document is disposed, and disposing it while another thread reads is not safe. The other formats' read-only documents carry no thread statement.
TextFilter
Safe
The compiled state is immutable, so matching and filtering are exact under concurrency; only the statistics counters are unsynchronized and may undercount.
BinaryEncodings instances
Safe
Each property returns a thread-safe singleton.
ConfigurationDocument
Single-threaded
Its section list is fixed, but sections and entries stay mutable through IniSection.
The web exchange-rate providers (every WebRateProvider, such as EcbRateProvider)
Safe
Lookups read an immutable snapshot and never block behind a fetch; concurrent loads for one window share a single request. See exchange-rate concepts.
InMemoryRateCache
Safe
Reads and writes are individually thread-safe; concurrent stores for one pair resolve last-write-wins. SqliteRateCache runs each write in a transaction, so concurrent writes for one pair lose neither.
RateSeriesBuilder, RateTableBuilder
Single-threaded
Their remarks say so.
Binary formats
Types
Contract
Notes
CompoundFile, opened read-only and buffered (the default)
Safe
The whole source is read at open, so access never touches it again. Opened unbuffered, reads are serialized against the shared source position: concurrent, but not parallel.
CompoundFile, opened for editing
Not stated
The thread statement covers read-only instances only. Edits are staged until Commit; make them from one thread.
ExcelBinaryWorkbook
Single-threaded
Its remarks say to call its members from one thread at a time; an ExcelWorksheetReader is a forward-only reader for one consumer.
PstFile, OutlookMailStore
Single-threaded
Their remarks say a session's members, and those of every view obtained from it, must not be called concurrently.
OutlookMessage
Single-threaded
Its remarks say a message's members, and those of every recipient, attachment, and nested message obtained from it, must not be called concurrently; its recipients, attachments, and named-property map are decoded on first access and cached without synchronization.
Rules of thumb
Share immutable values and frozen configuration; confine builders, sessions, and algorithm instances. The value types, resources, compiled filters, and frozen serializer options are made to be shared; a hash algorithm, a document session, or a builder is not.
Register singletons only for types marked Safe or Immutable. Every Bodu dependency-injection registration adds its services as singletons, so a type you plug into one - a currency lookup, a rate provider, a cache backend - must be safe for concurrent use too; see dependency injection and configuration.
Do not use a reader-writer lock for a type whose reads write. The collections listed above rewrite state on lookup.
Give each worker its own algorithm. For parallel hashing, share a factory (Func<HashAlgorithm> or IHashAlgorithmFactory<T>), never an instance.