Details
-
Type:
New Feature
-
Status:
Closed
-
Priority:
Minor
-
Resolution: Won't Fix
-
Affects Version/s: X10 2.0.4
-
Fix Version/s: X10 2.2
-
Component/s: Language Design
-
Labels:None
-
Number of attachments :
Description
Trying to debug or log multi-place X10 code can get very confusing as asynchrony means that different errors may happen on different runs with the same input parameters.
One approach I have used to simplify the behavior is to 'sequentialize' the program. That is, make the following replacements:
- foreach(p in array) S => for (p in array) S
- ateach(p in array) S => {{for(p in array)
{ at(array.dist(p)) S }
}}
- future S => S
- async(P) S => at(P) S
(I'm not sure whether the last one will remain valid after [XTENLANG-1198].)
This results in a program in which there is only one active thread of execution across all places at any given time, and in which events are more predictably ordered. There are some limitations, for example this approach wouldn't work for any program that relies on the existence of more than one thread of execution (e.g. through a wait statement), and obviously this approach is no help in finding synchronization errors! However it is useful for checking basic functional correctness in the early stages of developing a multi-place program.
It should be possible to provide compiler and IDE support, if others find this approach useful. A compile flag -SEQUENTIAL that desugars statements as above, plus an X10DT option "Build as X10 Sequential", would automate the process.
Issue Links
- depends upon
-
XTENLANG-1484
Compiler or runtime optimization for single-threaded places
-
I think it would actually be easier and more useful to do it in the runtime and not in the compiler front end for several reasons:
1) A transformation that maintains the same semantics is quite hard to do in the source language,
e.g., you need to handle exceptions correctly (run all the iterations, catch all exceptions and throw MultipleExceptions), you need to remove future objects and calls to force(), etc.
I also think it might be easier to do in the runtime: maintain a global stack of all asyncs created, and run the top of the stack until completion or until it spawns another async. That ordering of asyncs should work for most "future" usages. If the code waits for some async to proceed (e.g., uses "force()" or "wait"), then the runtime can throw an exception.
2) It is more useful if you compile once, and can run in multiple ways. It is especially important if you must debug a deployed application.