non_sync Tag

Most of the types in Ü should obey nested mutability rule. This means, that with an immutable reference to some value it’s not allowed to mutate nested values - fields, containers contents, etc. Such property is useful in a multithreaded environment. Immutable references of such types are possible to pass into a different thread without any synchronization problems.

But there are cases where interior mutability is required. For shared pointers, for example, it’s necessary to modify usage counters. Such types are not safe to use in a multithreaded environment. In order to prevent usage of such types in a multithreaded environment Ü has non_sync tag.

Such tag may be defined in struct or class definition. There are two ways to do this - unconditionally or with condition in () after non_sync tag.

It’s possible to check for non_sync tag existence via non_sync expression. non_sync tag is recursively propagated. If at least one field in a struct or class is of non_sync type, all struct or class is also non_sync. non_sync are also arrays and tuples which have non_sync elements.

class A non_sync {} // Unconditional non_sync
struct B non_sync( true ) {} // non_sync by the condition
class C non_sync( false ) {} // is not non_sync by the condition
class D{ A a; } // non_sync because it contains non_sync elements
class E polymorph non_sync {}
class F : E { } // non_sync because it has a non_sync parent

// Fundamental types are not non_sync
static_assert( !non_sync</i32/> );
static_assert( !non_sync</bool/> );
static_assert( !non_sync</[f32, 4]/> );

static_assert( non_sync</A/> );
static_assert( non_sync</[A, 8]/> ); // An array of non_sync elements is non_sync
static_assert( non_sync</B/> );
static_assert( non_sync</tup[bool, B, char8]/> ); // A tuple with at least one non_sync element is non_sync
static_assert( !non_sync</C/> );
static_assert( !non_sync</[C, 7]/> );
static_assert( non_sync</D/> );
static_assert( non_sync</E/> );
static_assert( non_sync</F/> );

It’s possible to create dependency loop via non_sync tag definition. But it’s not an error until an expression inside a non_sync tag is non_sync expression.

class A non_sync( non_sync</A/> ) {} // self-dependency - result is not non_sync
static_assert( !non_sync</A/> );

class B non_sync( non_sync</C/> ) {} // loop dependency - result is not non_sync, because there is no initial non_sync tag source
class C non_sync( non_sync</B/> ) {}
static_assert( !non_sync</B/> );
static_assert( !non_sync</C/> );

class D non_sync( non_sync</E/> ) {} // loop dependency via a field - result is non_sync, because a field is an initial non_sync tag source
class E non_sync { D d; }
static_assert( non_sync</D/> );
static_assert( non_sync</E/> );

It’s not allowed to change non_sync property in inheritance. If a non_sync class has non-non_sync parent, the compiler will produce an error. It’s important in order to avoid missing non_sync property when storing a value of derived class via a reference or inside a container for base class.

non_sync tag usage

There is almost no need to use non_sync tag in regular code, because it’s not possible to make something non_sync without usage of unsafe code.

It’s necessary to use non_sync tag only in containers which via unsafe code implement some thread-unsafe interior mutability. Also containers with indirect values storage (box, vector, variant, etc) should use conditional non_sync tag - depending on the contained type in order to propagate non_sync tag through these containers.

In a code that somehow creates threads or in a code where an object is transferred into another thread it’s necessary to add static_assert( !non_sync</T/> ) in order to prevent usage of unsafe for multithreaded environment types.

Safe multithreaded mutability

As it was mentioned above, values of non_sync types can’t be used in a multithreaded environment. But what if some sort of interior mutability is needed in combination with multithreading? Solution - use types, that implement thread-safe interior mutability. Such types are not marked as non_sync. Internally they use some synchronization primitives in order to prevent race conditions. Such primitives are mutex, rw_lock, atomics, etc.

Container types with interior mutability synchronization should check contained type for absence of thread-unsafe interior mutability - via static_assert( !non_sync</T/> ).