|
|
| Line 4: |
Line 4: |
|
| |
|
| I think we need to see some examples (well, I do for one). | | I think we need to see some examples (well, I do for one). |
|
| |
| == Examples ==
| |
| The main thing to remember is that the proposal states that all features calls are asynchronous.
| |
|
| |
| Transactions are assured with transactional memory, probably implemented as software transactional memory.
| |
| ----------------
| |
| Example of transaction:<br/>
| |
| feature<br/>
| |
| correct_removal(container: DISPENSER) is<br/>
| |
| transaction -- This is a transaction so it is assured to be atomic and isolated<br/>
| |
| if<br/>
| |
| not(container.is_empty)<br/>
| |
| then<br/>
| |
| container.remove<br/>
| |
| end<br/>
| |
| end<br/>
| |
|
| |
| This feature is assured to be correct because is is isolated and atomic with respect to the system. If the container has been modified between the call to is_empty and remove, the feature would be aborted and retried.
| |
| -----------------
| |
|
| |
| feature<br/>
| |
| incorrect_removal(container: DISPENSER) is<br/>
| |
| do<br/>
| |
| if<br/>
| |
| not(container.is_empty)<br/>
| |
| then<br/>
| |
| container.remove<br/>
| |
| end<br/>
| |
| end<br/>
| |
|
| |
| This is the standard example of concurrency issues with preconditions. The container can change between the call to is_empty and remove. Using transactions avoids this.
| |
| ----------------
| |
|
| |
| Example of concurrency:<br/>
| |
| feature<br/>
| |
| write_all_files is<br/>
| |
| do<br/>
| |
| write_file_1<br/>
| |
| write_file_2<br/>
| |
| write_file_3<br/>
| |
| write_file_4<br/>
| |
| end<br/>
| |
|
| |
| Since all feature calls would be asynchronous, the four write_file features could be executed in parallel if the runtime decides to do so. This is not a transaction so isolation across these calls is not assured.
| |
| ----------------
| |
|
| |
| feature<br/>
| |
| write_file is<br/>
| |
| external<br/>
| |
| "operating_system_write"<br/>
| |
| abort<br/>
| |
| "operating_system_abort"<br/>
| |
| commit<br/>
| |
| "operating_system_commit"<br/>
| |
|
| |
| This is how transactional features can be mapped on to external function calls that support transactional operations
| |
| ----------------
| |
|
| |
| feature<br/>
| |
| source: DISPENSER[ANY] --Where items are taken from<br/>
| |
| destination: DISPENSER[ANY] -- where items are put after processing<br/>
| |
| running: BOOLEAN -- Flag to stop processing<br/>
| |
|
| |
| consumer is<br/>
| |
| do<br/>
| |
| from<br/>
| |
| until<br/>
| |
| not (running)<br/>
| |
| loop --This loop will spawn multiple threads, microthreads, hyperthreads, or whatever the implementation is and runtime chooses, all processing a single item.<br/>
| |
| process_single_item<br/>
| |
| end<br/>
| |
| end<br/>
| |
| <br/>
| |
| process_single_item is<br/>
| |
| local<br/>
| |
| item: ANY<br/>
| |
| transaction --Since this is a transaction, accessing the container is safe even when run concurrently.<br/>
| |
| if<br/>
| |
| not source.is_empty<br/>
| |
| then<br/>
| |
| item := source.item<br/>
| |
| source.remove<br/>
| |
| io.put_string(item.to_string)<br/>
| |
| destination.put(item)<br/>
| |
| end<br/>
| |
| end<br/>
| |
| <br/>
| |
| ---------------
| |
| Legacy externals example
| |
|
| |
| feature<br/>
| |
| use_legacy_external(item: LEGACY_ITEM) is<br/>
| |
| do<br/>
| |
| print_item(item)<br/>
| |
| log_item(item)<br/>
| |
| --At this point the runtime will block until print_item and log_item have both committed, it will ensure nothing is running in the system and then run the external item serially. This is the non-concurrent way to ensure something executes as a transaction, isolated and atomic.<br/>
| |
| use_item(item)<br/>
| |
| end<br/>
| |
| <br/>
| |
| use_item(item: LEGACY_ITEM) is<br/>
| |
| external<br/>
| |
| "..."<br/>
| |
| end<br/>
| |
| <br/>
| |
| print_item(item: LEGACY_ITEM) is<br/>
| |
| do<br/>
| |
| ...<br/>
| |
| end<br/>
| |
| <br/>
| |
| log_item(item: LEGACY_ITEM) is<br/>
| |
| do<br/>
| |
| ...<br/>
| |
| end<br/>
| |
Discussion of the feasibility and desirability of implementing transactional concurrency in Eiffel.
Examples please
I think we need to see some examples (well, I do for one).