Skip to content
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

Append history in interactive sessions #99

Merged
merged 5 commits into from
Aug 21, 2018

Conversation

ylluminarious
Copy link
Contributor

As per the discussion here. Please let me know if you have any improvements in mind. Pinging @raimue since we spoke about this together on the aforementioned Trac ticket.

Copy link
Member

@raimue raimue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry I did not review your patch on Trac. My time for MacPorts is currently a bit scarce, but the pull request makes it easier. Thank you for that.

The changes look good to me, although I did not test them. I would appreciate if you could use the usual trailer with "Closes: ..." as reference to the ticket.


hist_file = fopen(s, "a");
fprintf(hist_file, "%s\n", current_history()->line);
fclose(hist_file);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a chance for a race condition here when multiple port instances are running at the same time. Although it is very unlikely that anyone would be able to type in both port instances at the same time, so we could just ignore it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raimue Hmm, okay, I will see if I can address this with lockfiles or the like. If things get too hairy, I may just ignore it as you say.

@ylluminarious
Copy link
Contributor Author

@raimue

I am sorry I did not review your patch on Trac. My time for MacPorts is currently a bit scarce, but the pull request makes it easier. Thank you for that.

No problem, I figured you were probably too busy to respond on Trac. I definitely understand not having time for things like Macports sometimes; real life can be a pain! But you're welcome, and thank you for the kindness.

The changes look good to me, although I did not test them. I would appreciate if you could use the usual trailer with "Closes: ..." as reference to the ticket.

Okay, I will try to do that in the future. Where am I supposed to put the "Closes: ..." remark? In the title of the PR?

@jmroot
Copy link
Member

jmroot commented Jul 4, 2018

Okay, I will try to do that in the future. Where am I supposed to put the "Closes: ..." remark? In the title of the PR?

In the commit message body, on a line by itself.
https://trac.macports.org/wiki/CommitMessages

@ylluminarious
Copy link
Contributor Author

@jmroot Oh, okay. I had read that article previously, but I missed the part which gives an example of that. Thanks for pointing me to it!

The only interesting thing here is that apparently we don't need the
special encoding that is currently used in the history file. More
details here: https://trac.macports.org/ticket/56381#comment:10
Hitherto, port.tcl has used bash-style history-writing at the end of
an interactive session. That is vulnerable to crashes and other abrupt
interruptions. This changes that.
As discussed here: macports#99 (comment)

The first hunk which changes _XOPEN_SOURCE to _BSD_SOURCE is due to
the fact that flock(2) doesn't work when _XOPEN_SOURCE is defined as
such. According to an answer on StackOverflow, using _BSD_SOURCE is
a valid alternative for strdup(3).
See https://stackoverflow.com/questions/5378778/what-does-d-xopen-source-do-mean#5724485
@ylluminarious
Copy link
Contributor Author

@raimue I have added some file-locking code as discussed above. Sorry for the delay; I've been quite busy but finally managed to make some time for this. Please let me know if anything else should be done. Also, should I squash my commits into a single one to have the "Closes: ..." trailer?

@pmetzger
Copy link
Member

You must indeed always squash your commits. (I have no opinion on the PR itself...)

@ylluminarious
Copy link
Contributor Author

@pmetzger Ok, thanks for the info.

@ylluminarious
Copy link
Contributor Author

@raimue Friendly ping... any thoughts on this? :)

Copy link
Sponsor Member

@neverpanic neverpanic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs an fflush(3) to work correctly, and while at it please sprinkle some error handling in, then it's good to go.

hist_file = fopen(s, "a");

flock(fileno(hist_file), LOCK_EX);
fprintf(hist_file, "%s\n", current_history()->line);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless hist_file is a terminal or stderr, this fprintf(3) call will be buffered, which means it will not actually trigger writing to disk, rendering your locking moot against modification across multiple instances of macports writing at the same time.

You must either switch hist_file to line buffering mode using setvbuf(3), or call fflush(3) after writing the line and before returning the lock. I'd probably just call fflush(3), it's simpler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@neverpanic Ok, I went ahead and took your advice for fflush. Strangely, what you're saying makes perfect sense, but when I tested this out, the locking mechanism actually worked. It was as though it didn't need a flush. I tested it by opening two separate terminals and entering commands into them via CloneKeys. I'm curious now; do you know why that may have happened?

}
s = Tcl_GetString(objv[2]);

hist_file = fopen(s, "a");
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add error handling to the fopen(3) call here? mkdtemp.c has an example on how to do this, e.g.

if (NULL == (hist_file = fopen(s, "a"))) {
    Tcl_AppendResult(interp, "fopen(", s, "): ", strerror(errno), NULL);
    return TCL_ERROR;
}

return TCL_ERROR;
}
s = Tcl_GetString(objv[2]);
hist_file = fopen(s, "a");
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you GitHub review for hiding my review comment against the version in one of the lower commits. Please add error handling here. See mkdtemp.c for good examples. E.g., use:

if (NULL == (hist_file = fopen(s, "a"))) {
    Tcl_AppendResult(interp, "fopen(", s, "): ", strerror(errno), NULL);
    return TCL_ERROR;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@neverpanic Thanks for the code, I went ahead and pasted it in verbatim :)

I also added some error-checking for the fprintf call; please let me know if it's okay. It's not as pretty as the code in mktemp.c, but it should do the trick.

@ylluminarious
Copy link
Contributor Author

@neverpanic I went ahead and implemented your changes as you requested. Please let me know if they're satisfactory. I also added some comments for visual and conceptual clarity since I stared at this code without comments and it felt like my eyes were getting poked with curly-braced needles :)

@neverpanic neverpanic merged commit 486565d into macports:master Aug 21, 2018
@neverpanic
Copy link
Sponsor Member

Thanks!

@ylluminarious
Copy link
Contributor Author

@neverpanic You're welcome! Thanks for merging it and for the help!

Did you see my comment from last night wherein I had a question about the flushing seeming unnecessary? I'm curious to know what may have been going on... :)

@neverpanic
Copy link
Sponsor Member

I think what you were seeing is that the fclose(3) at the end of the process will flush the buffers anyway, i.e. it will internally call fflush(3). However, it won't do that under the lock.

It may just be very hard to hit the race condition that would make the problem visible (and even if two writes were happening at the same time outside of the lock, they may still end up being a single write(2) syscall and not interfere with each other). To demonstrate the race condition in reality, you'd probably have to write huge commands (>= pagesize) at the exact same time in two terminals.

@ylluminarious
Copy link
Contributor Author

@neverpanic Thanks so much for the explanation! This makes more sense to me now. I suspected that fclose was behind this. I guess the lock gave just enough of a delay that the flushing done by fclose happens right before the acquisition of the lock for the competing process, or at least before that competing process was able to do its flush. Thus, there was a de facto order of operations, although unreliable.

Yes, such a situation would indeed make it hard to expose the problem. That is a very hard-to-hit race condition, although possible. And as you also said, if the two writes were mixed into the same syscall then there would be no conflict anyway. Maybe I'll try that test you mention someday, just for fun :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
5 participants