Custom inheritable data/function attributes (for example, for multi-threaded or real-time code)

49 views
Skip to first unread message

emuzy...@gmail.com

unread,
Mar 16, 2018, 6:03:01 AM3/16/18
to ISO C++ Standard - Future Proposals
C++ supports very useful const attribute. It can be assigned to both data and code (function). It is inheritable ('const' member of a base class is still constant in derived classes, 'const' member function gets a 'const' object). There is less useful volatile attribute, working the same way.

I think it is a good idea to have an ability to declare custom (user-defined) compile-time-only attributes that can be assigned either (or both) to data and/or code, make them inheritable, and define some usage restrictions, like const/volatile do.

Example 1: in a parallel (multi-threaded) code with concurrent data access, an object can be shared between several threads. To access the object safely, a thread must ensure mutual exclusion with other threads, acquiring a lock (a mutex, semaphore, critical section etc.) associated with the object. There are no problems accessing a single shared object at a time. But if a thread needs to access several shared objects, each of them protected with its own lock, a deadlock can occur. In spite of there is an explicit association between the object and the protection lock, there is no language feature to declare this association at compile time. So many errors that may cause deadlocks are found at run-time only. There are complex software instruments to analyze run-time code behavior while the same work can easily be performed by a compiler at compile time.

Example 2: real-time programming (including embedded system programming). There is an important code/data property like "can cause a long delay". It may be a code executed too long, or accesses resources not available "momentarily" (disk or even network files), or acquiring a protective lock for shared access; a data that can be protected by a lock, or paged out, or need to be built. To avoid undesired execution delays, or blocking, a real-time process must not call such code or access such data. But there are no language features to declare/check such code/data properties so many errors are very hard to find.

Example 3: kernel-mode programming. OS kernels have some data objects that can be accessed (or functions can be called) only with specified conditions (for example, cannot be accessed/called from an interrupt handler). Errors related to violation of such rules can be found only at run time.

Example 4: there can be "reliable" (checking all their arguments on every call) and "unreliable" (relying on the caller) functions. Of course, a reliable function can call either reliable or unreliable function but unreliable function should not call another unreliable one.

All these code/data properties are inheritable by their nature:
  • If a class requires mutual exclusion to access its objects, all classes derived from (or including) this class may require it too (a 'data-to-data' inheritance).
  • If a function can block thread execution, all functions calling this function can block too (a 'code-to-code' inheritance).
  • If a function accesses an object protected by a lock, this function can block thread execution (a 'data-to-code' inheritance).
  • If a class has member functions than can block, access to class objects can block too (a 'code-to-data' inheritance).
The proposal is to add language features allowing to declare and check various code/data attributes.

For such idea, there should be three features:
  • Attribute declaration, separate for code and data
  • Attribute inheritance rules
  • Attribute checking rules
Attributes used in a program can be (but not obligatory) declared in advance:

attributes {
  data:needs_lock_a, // Accessing code must hold mutex (lock) A
  data:needs_lock_b, // Accessing code must hold mutex (lock) B
  code:needs_lock_a, // Needs mutex (lock) A to be held by a caller
  code:needs_lock_b, // Needs mutex (lock) B to be held by a caller
  code:holds_lock_a, // Holds mutex (lock) A
  code:holds_lock_b, // Holds mutex (lock) B
  code:can_block, // Can block execution flow
  code:realtime, // Real-time code that cannot be blocked
  data:pageable, // Occupies non-locked memory that can be paged out
}

class attribute (cannot_be_accessed_from_interrupt_handler) ResourceDescriptor {...};
int attribute (needs_lock_a) Count1;
int attribute (needs_lock_b) Count2;
void attribute (holds_lock_a) IncreaseCounter1 ();
void attribute (holds_lock_b) IncreaseCounter2 ();

Therefore, attribute type (data/code) can be derived implicitly from the declaration/definition.

Inheritance rules should define the following:
  • If an object is wrapped with another object, all data attributes are inherited.
  • If a function calls (explicitly) another function, the caller function inherits all callee code attributes.
  • If a function accesses an object, how code attributes are derived from data attributes.
  • If a class has a member function, object data attributes are derived from function code attributes.
attribute_inheritance (<source>, <destination>)

attribute_inheritance (data:needs_lock_a, code:needs_lock_a) // Code accessing the object must host lock A
attribute_inheritance (data:needs_lock_b, code:needs_lock_b) // Code accessing the object must host lock B
attribute_inheritance (code:holds_lock_a || holds_lock_b, code:can_block) // Code holding a lock can block the execution
attribute_inheritance (data:pageable, code:can_block) // Code accessing pageable data can block the execution

Checking rules (assume that attribute names return boolean values):

attribute_check (code:realtime != code:can_block) // Realtime code is not compatible with blocking code
attribute_check (!(code:holds_lock_b && code_needs_lock_a)) // If a code holds lock B, it cannot hold lock A (prevent deadlocks)

Or there could be an operator like "get_attribute ()", returning attributes of a class, object or function, to be used in a statement like assume().

Keywords and syntax used are for example only, actual implementation could be completely different.

Such access control model can be used to implement various access rules (for example, in cases where "mutable" cannot be used safely, or to mark test/unfinished objects/functions that must not be accessed by production code).
Reply all
Reply to author
Forward
Message has been deleted
0 new messages