[前][次][番号順一覧][スレッド一覧]

ruby-changes:65397

From: Jeremy <ko1@a...>
Date: Sat, 6 Mar 2021 00:18:46 +0900 (JST)
Subject: [ruby-changes:65397] 14e1739ff3 (master): [ruby/irb] Make save-history extension safe for concurrent use

https://git.ruby-lang.org/ruby.git/commit/?id=14e1739ff3

From 14e1739ff3ec81c9ea87a8aba03393f0bf0433a7 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Tue, 2 Mar 2021 12:17:23 -0800
Subject: [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
---
 lib/irb/ext/save-history.rb | 20 +++++++++++++++-----
 test/irb/test_history.rb    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb
index ac358c8..7acaebe 100644
--- a/lib/irb/ext/save-history.rb
+++ b/lib/irb/ext/save-history.rb
@@ -81,6 +81,8 @@ module IRB https://github.com/ruby/ruby/blob/trunk/lib/irb/ext/save-history.rb#L81
             end
           }
         end
+        @loaded_history_lines = history.size
+        @loaded_history_mtime = File.mtime(history_file)
       end
     end
 
@@ -105,12 +107,20 @@ module IRB https://github.com/ruby/ruby/blob/trunk/lib/irb/ext/save-history.rb#L107
           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
diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb
index 392a6af..aad8952 100644
--- a/test/irb/test_history.rb
+++ b/test/irb/test_history.rb
@@ -127,6 +127,37 @@ module TestIRB https://github.com/ruby/ruby/blob/trunk/test/irb/test_history.rb#L127
       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 https://github.com/ruby/ruby/blob/trunk/test/irb/test_history.rb#L174
         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
 
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]