Due to its complexity, an AGI system must inevitably use multithreading in a multicore environment. This requires a set of tools that ensure the reliable processing of shared data.
In many cases, shared data is formed/updated by an object of one "generator" class and used/consumed by objects of several other classes. Most coding guidelines for such systems describe a technique for synchronizing access to shared data, which ensures correct access to the data but does not restrict the ability of an object of any class to modify the shared data. Complex systems, where different developers code/alters various components of the system, are vulnerable to unintentional disruption of the logic of operation due to shared data being modified by objects that should not do so.
Below is a simple technique for restricting the ability of objects of one or more classes to modify shared data, ensuring that unintended modifications are detected at compile time.
The technique is demonstrated using the atomic boolean flag example but is applicable to any class.
The flags available for modification by arbitrary objects look like this:
std::atomic< bool > flagA;
std::atomic< bool > flagB;
std::atomic< bool > flagC;
If the logic of the system implies that flagA should be modified exclusively by objects of class WriterA, flagB - by objects of class WriterB, and so on, then to regulate "write" access to flags, a base class is created, similar to atomic<bool>, in which the method providing data modification is protected:
struct SharedBool{
inline bool load() const { return _.load(); }
protected:
inline void store( const bool& x ){ _.store( x ); }
std::atomic< bool >_{ false };
};
Then, the access-controlled flags are declared as singleton objects of an unnamed class inheriting from SharedBool:
struct: public SharedBool{ friend class WriterA; } flagA;
struct: public SharedBool{ friend class WriterB; } flagB;
struct: public SharedBool{ friend class WriterС; } flagC;
Naturally, assigning several friend classes that are allowed "write" access is possible. The presence of an unplanned modification of shared data is detected at the compilation stage.
The described technique does not affect the efficiency of the code and does not significantly increase the volume of the code.