Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'config/initializers/enumerator_next_patch.rb')
-rw-r--r--config/initializers/enumerator_next_patch.rb44
1 files changed, 44 insertions, 0 deletions
diff --git a/config/initializers/enumerator_next_patch.rb b/config/initializers/enumerator_next_patch.rb
new file mode 100644
index 00000000000..e1fc04731ae
--- /dev/null
+++ b/config/initializers/enumerator_next_patch.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+# Monkey patch of Enumerator#next to get better stack traces
+# when an error is raised from within a Fiber.
+# https://bugs.ruby-lang.org/issues/16829
+module EnumeratorNextPatch
+ %w(next next_values peek peek_values).each do |name|
+ define_method(name) do |*args|
+ gitlab_patch_backtrace_marker { super(*args) }
+ rescue Exception => err # rubocop: disable Lint/RescueException
+ err.set_backtrace(err.backtrace + caller) unless
+ has_gitlab_patch_backtrace_marker?(err.backtrace) && backtrace_matches_caller?(err.backtrace)
+
+ raise
+ end
+ end
+
+ private
+
+ def gitlab_patch_backtrace_marker
+ yield
+ end
+
+ # This function tells us whether the exception was generated by #next itself or by something in
+ # the Fiber that it invokes. If it's generated by #next, then the backtrace will have
+ # #gitlab_patch_backtrace_marker as the third item down the trace (since
+ # #gitlab_patch_backtrace_marker calls a block, which in turn calls #next.) If it's generated
+ # by the Fiber that #next invokes, then it won't contain this marker.
+ def has_gitlab_patch_backtrace_marker?(backtrace)
+ match = %r(^(.*):[0-9]+:in `gitlab_patch_backtrace_marker'$).match(backtrace[2])
+
+ !!match && match[1] == __FILE__
+ end
+
+ # This function makes sure that the rest of the stack trace matches in order to avoid missing
+ # an exception that was generated by calling #next on another Enumerator inside the Fiber.
+ # This might miss some *very* contrived scenarios involving recursion, but exceptions don't
+ # provide Fiber information, so it's the best we can do.
+ def backtrace_matches_caller?(backtrace)
+ backtrace[3..] == caller[1..]
+ end
+end
+
+Enumerator.prepend(EnumeratorNextPatch)