|
|
| 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.
I think we need to see some examples (well, I do for one).