-
Notifications
You must be signed in to change notification settings - Fork 418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
I/O module: express channel mark/revert/commit via context manager #19611
Comments
In theory I think this might be possible but the context managers don't have the possibility for two different exit conditions, so I'm not sure how you would indicate revert vs commit. Conceivably, that could be communicated by a variable (say that the context manager gives a ref to) or by throwing (and having the context manager think that throwing means it should revert). However, it seems to me that revert and commit methods are clearer than either of those two strategies. |
I think the main drawback of the method is that one might forget to call But, come to think of it, I think the same approach can work with a context manager You can call a method ( |
Summary for ad-hoc sub-team meeting next week: 1. What is the API being presented? How is it intended to be used?proc fileReader.mark() throws
proc fileWriter.mark() throws
proc fileReader.revert()
proc fileWriter.revert()
proc fileReader.commit()
proc fileWriter.commit() These methods enable attempting minor changes to the fileReader/fileWriter and either undoing those changes or saving them once the experimentation is done. A location in the file is marked with I made a short mock up of a record that performs the offset recording behavior to see what using them in a context manager setting could look like. In this version, the manage statement determines whether to use List;
record ch {
var x: list(int);
var filePos = 0;
proc enterThis() ref: int {
writeln("currentPos is: ", filePos);
return mark();
}
proc mark() ref{
x.pushBack(filePos);
return x.last();
}
proc leaveThis(in err: owned Error?) {
if err then revert(); // We could do something here to rethrow the error so it's obvious that the changes were reverted
else commit();
}
proc revert() {
// Drop the buffer
x.popBack();
}
proc commit() {
// Flush the buffer
filePos = x.popBack();
}
}
proc main() {
var c = new ch();
writeln(c.filePos);
manage c as offset {
writeln(offset);
c.filePos = 3;
writeln(c.filePos);
c.filePos = 7;
writeln(c.filePos);
c.filePos = 2;
writeln(c.filePos);
//throw new Error ("oops!"); // When this is uncommented, `revert` will be called instead of commit upon exiting the manage statement
}
writeln(c.filePos);
} It seems to behave as one would expect. I think if we provide this support, we'd probably expect the user to not directly call How is it being used in Arkouda and CHAMPS?These functions are not used in Arkouda or CHAMPS 2. What's the history of the feature, if it already exists?These methods were added 12 years ago and have mostly seen minor refactoring since then. The main change is that 3. What's the precedent in other languages, if they support it?a. Python does not seem to support this 4. Are there known Github issues with the feature?There are no currently open issues with these functions. The issue open about mark has been resolved by returning the offset (mentioned here) 5. Are there features it is related to? What impact would changing or adding this feature have on those other features?
6. How do you propose to solve the problem?A. Leave things as is
B. Use manage statements, either as outlined above or in another way I'm not thinking of
|
Discussion summary: In addition to what was present above, I also included the following code to have a real-life example of what such an adjustment would look like (at Jeremiah's suggestion): // Adjusted version of https://github.com/chapel-lang/chapel/blob/809fed0f3e5bf1b36145728171513e9ace51f806/modules/standard/IO.chpl#L6830
proc fileReader.readThrough(separator: string, ref s: string, maxSize=-1,
stripSeparator=false): bool throws {
on this._home {
try this.lock(); defer { this.unlock(); }
const (searchErr, found, bytesOffset) =
_findSeparator(separator, 4*maxSize, this._channel_internal);
if searchErr != 0 && searchErr != EEOF then
try this._ch_ioerror(searchErr, "in readThrough(string)");
const bytesToRead = if found then
bytesOffset + separator.numBytes else bytesOffset;
// Note: this is the statements that were changed. In the meeting, Ben suggested that
// the contents of the manage statement (and the `if maxSize >=0` else branch) could
// be transformed into a helper function, which would reduce the code duplication.
// Michael noted in the meeting that this code was already relying on throwing, so is
// not negatively impacted by the switch to using manage statements in this way
if maxSize >= 0 {
manage this as offset {
const err = readStringBytesData(s, this._channel_internal, bytesToRead,
-1);
if err {
try this._ch_ioerror(err, "in readThrough(string)");
} else {
if maxSize >= 0 && s.numCodepoints > maxSize {
try this._ch_ioerror(EFORMAT:errorCode, "in readThrough(string)");
}
}
}
} else {
const err = readStringBytesData(s, this._channel_internal, bytesToRead,
-1);
if err {
try this._ch_ioerror(err, "in readThrough(string)");
} else {
if maxSize >= 0 && s.numCodepoints > maxSize {
try this._ch_ioerror(EFORMAT:errorCode, "in readThrough(string)");
}
}
}
if found && stripSeparator then
s = s[0..<s.numCodepoints-separator.numCodepoints];
}
return s.size > 0;
} We also looked at its helper function, chapel/modules/standard/IO.chpl Line 7050 in 809fed0
We decided to keep the current strategy of mark/commit/revert but leave notes for enabling context manager support in the future - it seemed there were scenarios where both were useful, though we would strongly discourage intermingling the two strategies. We would also like more design on the context manager strategy before stabilizing it. We also discussed whether we should considering renaming these functions. We decided that the current names were good. Discussion notes:
|
The context manager path seems super cool. If you ran into difficulties or complications I'd be curious to know if there are opportunities to improve the language feature. I think it's perfectly reasonable to only support the lower level calls for now and then introduce the context manager in the future. Re: not wanting to mingle the two paths, producing the manager could set a bit on the channel which we could use to emit runtime warnings, e.g., |
Improves the docs for the `mark`/`commit`/`revert` methods on fileReader/fileWriter: - Minor wording/clarity improvements - The meaning of `revert` and `commit` were swapped. This is corrected - Some method links are shortened with `~` to make docs more readable - Clarifies that a fileReader/fileWriter must be manually locked before calling `mark` even if it has `locking=true` Per the discussion [here](#19611), also adds a comment about our desire to support mark/commit/revert via a context manager in the future. - [x] inspected built docs - [x] paratest [ reviewed by @lydia-duncan ] - Thanks!
Currently, lightweight transactions on channels are implemented via
channel.mark
,channel.revert
, andchannel.commit
. Could lightweight transactions be expressed via a context manager?The text was updated successfully, but these errors were encountered: