Hi Vass: In my view sync and single variables should be treated like any other class instance. From that aspect, the behavior of sync and single variables with respect to writeln and I/O reads is a library issue, not a language issue.
The default behavior provided by the compiler is to write out the components of the class. This behavior can be overridden in the ChapelSyncvar module. That all sounds fine to me. * What should write() do on a sync/single? - As long as it remains within the framework described above, I don't have strong opinions here. I tend to think of I/O writes as "inspection" rather than "consumption". So writing out the contained value without consuming it, or the literal "<empty>" if it is empty (Option 4, Example 1) is a behavior I would find useful. * What should write() do on a class/record containing a sync/single field, without user-defined writeThis or readWriteThis? - This follows naturally from whatever is chosen as the answer to the previous question. It does not need to be considered as a separate issue. * What should read() do on a sync/single? - "Something useful." Again, I don't have strong opinions here. The default should be something unsurprising. Perhaps inheritance and polymorphism can be used to dodge this question: For example, if the default behavior is to overwrite the contents of the sync/single (i.e. using writeXF()), one could inherit "myBlockingSync" from _syncvar and explicitly change the behavior of read() to update the syncvar using writeEF() instead. FWIW, THH ________________________________________ From: Vassily Litvinov [[email protected]] Sent: Thursday, September 18, 2014 5:48 PM To: [email protected] Subject: Re: [Chapel-developers] Change to sync/single write*() interpretation (and min/max) The key point of this follow-up email is: we are looking for user input on how sync and single variables should work when passing them as arguments to functions with generic formals. For 1.10, write() et al. on sync/single will generate a compiler error. We made this choice to raise community awareness and to avoid users getting surprising behavior from whatever other option we might have implemented. min()/max() on sync/single/atomic will generate a compiler error, too. We will stick with this unless we hear strong arguments/proposals to the contrary. In both the above cases, to perform the desired operation on a sync/single, you will need to invoke readFE()/readFF()/readXX() etc. explicitly, then pass the result to write()/min() etc. For example: var mySync$: sync int; var mySingle$: single real; ... start computing the above ... //Currently the following would generate compiler errors like: // "IO.chpl:1691: error: sync/single variables cannot currently be written" //Run chpl --print-callstack-on-error to determine the source location. // // writeln("mySync$ = ", mySync$); // writeln("mySingle$ = ", mySingle$); //Specify the desired access explicitly. // E.g. write a pair of (is it full?, the value), non-atomically: writeln("mySync$ = ", (mySync$.isFull, mySync$.readXX())); // or wait until the variable is full: writeln("mySingle$ = ", mySingle$.readFF()); The above solution is not perfect because: * If you have a record/class with a sync/single field, you will need to provide your own writeThis() method to enable write() on that record/class to compile. Without it the compiler provides a default implementation that invokes writeThis on each field. The latter currently generates a compiler error for sync/single fields. For example: record RecordWithSync { var regularField: int; var syncField$: sync real; proc writeThis(x: Writer) { x.write("(", regularField, ", ", syncField$.readXX(), ")"); } } var r: RecordWithSync; // This would generate a compiler error without writeThis() above. // Since we used readXX() there, this will never block. writeln(r); * It is not symmetric with reading sync/single variables, for which it seems natural to wait until such a variable is empty and leave it full. Disclaimer: currently read() does not work on sync/single; this will not be fixed for 1.10. Some of the questions we'd like input on are: * What should write() do on a sync/single? Examples: - Not allowed (it's the current implementation). - "Consume" the full/empty state, i.e. implicitly do sync.readFE() or single.readFF(). - Wait for, but not consume, the full state, i.e. implicitly do readFF() for sync and single. - Do not wait for the full state - print whether it's full. Example 1: "5.5" (when full), "<empty>" (when empty). Example 2: "f:5.5" (when full), "e:5.5" (when empty). - Others? * What should write() do on a class/record containing a sync/single field, without user-defined writeThis or readWriteThis? Examples: - Do write() on the sync/single field, behaving according to the choice in the previous bullet. - Do something special, e.g. never "consume" the full/empty state of each field, like the last option in the previuos bullet. - Others? * What should read() do on a sync/single? E.g. should it wait for the variable to become empty before storing a value into it? Or should it read in and set the variable's full/empty bit, besides reading the value? There is more discussion in the commit message for the #420 merge, see: https://github.com/chapel-lang/chapel/commit/6c2e3a6 For example, it shows a new deadlock that our earlier implementation could result in, when a thread locked the channel before locking the sync variable. Vass On 09/16/14 09:02, Brad Chamberlain wrote: > Hi all -- > > As you may have seen/heard on github, Vass is working on a bug fix for the > long-term problem of passing sync/single to generic arguments: Previously, > such cases were unwrapped and treated as reads of the actual argument, > passing in the underlying value with no full/empty state. Thus, in: > > proc foo(x) { > ... > } > > var mySyncVar$: sync int; > > foo(mySyncVar$); > > foo() would be interpreted as taking an int and the foo(mySyncVar$) would be > interpreted as foo(mySyncVar$.readFE()). This has seemed inconsistent to > most of us for some time now, the original rationale for it didn't seem to > hold up as the language has matured, and it is something I attempted to fix a > year ago without success. Vass has been more successful this month > (motivated by his work to get is*Type()-style queries working correctly for > all types). > > One place where this has impact is in the implementation of write/writeln for > sync/single, which were previously treated as a blocking read of the value > and passing the value to the corresponding write/writeln for its base type. > It has always seemed unfortunate/surprising to me (and to users, I believe) > to have such cases consume the full/empty state, and has led to buggy > programs, so what I've proposed we do for this release is to reinterpret > write/writeln as an inspection of the sync/single variable's full state, > writing: > > "<empty>", if the variable is empty > its value, if the variable is full > > The rationale for this is that when you're writing something, I believe > you're typically wanting to inspect its logical state, not treat it as a > consumption of the value. And that if someone wants the previous behavior, > they would call their own readFE/readFF at the callsite, passing the base > value on to the write/writeln call. I think that this is a nice change and > improvement, but it's clearly a significant change so I wanted other > developers to be able to protest if they thought it was clearly wrong -- > particularly if they had a better proposal. Or if it seems like too > big/scary a change to make so close to the release. > > Similarly, and less significantly, min()/max() are defined generically and as > a result, used to perform a readFE/readFF of a sync/single argument at the > callsite and then just operate on the base value. In the new "pass the > sync/single by ref" interpretation, the generic implementations don't work > because they trip over the full/empty state. Our proposal here is to not > support min()/max() on synchronized types, requiring users to do the > readFE/readFF at the callsite if they want to perform min()/max() on the > value being wrapped by the sync/single. Thus, in Vass's patch, passing > min()/max() a sync or single (or atomic) results in a compiler error > explaining that those routines are not supported for said types. > > I think this semantic fix is long overdue and that the ensuing changes are > nice steps forward as well. Moreover, I think that the fix makes it worth > taking the changes even at this late date in the 1.10 release cycle. But if > there's a strong feeling that we should hold off until after the 1.10 branch > is cut, I'd like to hear that. > > Thanks, > -Brad ------------------------------------------------------------------------------ Slashdot TV. Video for Nerds. Stuff that Matters. http://pubads.g.doubleclick.net/gampad/clk?id=160591471&iu=/4140/ostg.clktrk _______________________________________________ Chapel-developers mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/chapel-developers ------------------------------------------------------------------------------ Slashdot TV. Video for Nerds. Stuff that Matters. http://pubads.g.doubleclick.net/gampad/clk?id=160591471&iu=/4140/ostg.clktrk _______________________________________________ Chapel-developers mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/chapel-developers
