mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/irb] Make save-history extension safe for concurrent use
This makes the save-history extension check for modifications to the history file before saving it. If the history file was modified after the history was loaded and before it was saved, append only the new history lines to the history file. This can result in more lines in the history file than SAVE_HISTORY allows. However, that will be fixed the next time irb is run and the history is saved. Fixes [Bug #13654] https://github.com/ruby/irb/commit/041ef53845
This commit is contained in:
parent
182cde8dfb
commit
14e1739ff3
2 changed files with 51 additions and 5 deletions
|
@ -81,6 +81,8 @@ module IRB
|
|||
end
|
||||
}
|
||||
end
|
||||
@loaded_history_lines = history.size
|
||||
@loaded_history_mtime = File.mtime(history_file)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -105,12 +107,20 @@ module IRB
|
|||
raise
|
||||
end
|
||||
|
||||
open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
|
||||
if File.exist?(history_file) && @loaded_history_mtime &&
|
||||
File.mtime(history_file) != @loaded_history_mtime
|
||||
history = history[@loaded_history_lines..-1]
|
||||
append_history = true
|
||||
end
|
||||
|
||||
open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
|
||||
hist = history.map{ |l| l.split("\n").join("\\\n") }
|
||||
begin
|
||||
hist = hist.last(num) if hist.size > num and num > 0
|
||||
rescue RangeError # bignum too big to convert into `long'
|
||||
# Do nothing because the bignum should be treated as inifinity
|
||||
unless append_history
|
||||
begin
|
||||
hist = hist.last(num) if hist.size > num and num > 0
|
||||
rescue RangeError # bignum too big to convert into `long'
|
||||
# Do nothing because the bignum should be treated as inifinity
|
||||
end
|
||||
end
|
||||
f.puts(hist)
|
||||
end
|
||||
|
|
|
@ -127,6 +127,37 @@ module TestIRB
|
|||
INPUT
|
||||
end
|
||||
|
||||
def test_history_concurrent_use
|
||||
omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
|
||||
IRB.conf[:SAVE_HISTORY] = 1
|
||||
assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT) do |history_file|
|
||||
exit
|
||||
5
|
||||
exit
|
||||
EXPECTED_HISTORY
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
INITIAL_HISTORY
|
||||
5
|
||||
exit
|
||||
INPUT
|
||||
assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2)
|
||||
exit
|
||||
EXPECTED_HISTORY2
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
INITIAL_HISTORY2
|
||||
5
|
||||
exit
|
||||
INPUT2
|
||||
File.utime(File.atime(history_file), File.mtime(history_file) + 2, history_file)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_history(expected_history, initial_irb_history, input)
|
||||
|
@ -143,6 +174,11 @@ module TestIRB
|
|||
io = TestInputMethod.new
|
||||
io.class::HISTORY.clear
|
||||
io.load_history
|
||||
if block_given?
|
||||
history = io.class::HISTORY.dup
|
||||
yield IRB.rc_file("_history")
|
||||
io.class::HISTORY.replace(history)
|
||||
end
|
||||
io.class::HISTORY.concat(input.split)
|
||||
io.save_history
|
||||
|
||||
|
|
Loading…
Reference in a new issue