Currently when the UI sets an Item value of an Attribute, it simply calls the appropriate method on the instance. Prior to asynchronous operations this was the appropriate thing to do. However, since operations can be asynchronous, there is a possibility of a race condition between the UI and an operation setting/getting the Item’s value.
For example assume we have an Attribute a with a double Item d whose current value is 1.0. Let’s also assume there exists an operation that can set the value of d to double it current value. When the operation runs, it will attempt to lock a’s Attribute Resource for writing which would prevent any other operation that wants to modify the resource from running. So with d currently set to 1.0, running the operation will cause d to be set to 2.0 and the UI will update to display the new value.
Now lets assume that the user is in the process of changing d at the same time the operation is running (this can happen since operations can run in a thread that is not the main thread that the UI is running.
The act of setting a value via the UI does not lock the resource and here is where the race condition can occur. If the user attempt to change d to 10.0, the result could be any of the following:
- 2.0 - here the user’s value was set after the operation accessed d current value but before the operation completed.
- 10.0 - here the user’s value was set after the operation completed.
- 20.0 - here the user’s value was set before the operation accessed d current value .
In order to better handle this situation, I propose that we change the way the UI interacts with Attribute Resources by forcing all changes to go through Operations which will force the Attribute Resource to be locked for writing. Though having the UI may not prevent the race conditions from occurring (that is the price you pay for running operations asynchronously), it will provide a way to identifying when certain race conditions occur.
General Operation Structure for Modifying Items
All Item modifying operations would consist of the following:
- String Item representing the Item’s path w/r to the Attribute being modified
- Reference Item (or Association) to the Attribute being modified
The derived operations would include:
- Changing an optional Item’s state
- Reseting the Item to it’s default
- Adding new values (or subgroups) to an extensible Item
- Removing values (or subgroups) from an extensible Item
- Changing the value of an Item
In the case changing an Item’s value, there would need to be one operation for each of the following type of Items:
- Value Items (due to the ability of setting the value as a string we can handle Int, Double, and String Items with one operation
- Date Time Items
- File Items
- Reference Items
- User-Define Items - one per item type
Operation Structure for Value Items
For value items, the operation would consist of the following additional parameters:
- String Item storing the new value of the Item
- String Item storing the current value of the Item
- Int Item indicating the component being changed
The reason for storing both the original and new values would be to handle the case where another operation is attempting to change the Item’s value before this operation runs - in this case the operation could fail and leave the previous operation’s value in place. The reason behind this is that user’s decision to change the Item was based on the value they saw and not the value that another operation was setting. So in the above example it would not be possible for d’s final value to be 10.0 since it would mean that the other operation ran first while the UI’s operation would have been given the current value of 1.0 and new value 10.0. By the time the UI’s operation ran - it would have noticed the Item’s value had been changed to 2.0 and thus would have failed).