Skip to content

Thread safety Functions#329

Merged
naitoh merged 1 commit into
ruby:masterfrom
tompng:threadsafe_xpath
Jun 10, 2026
Merged

Thread safety Functions#329
naitoh merged 1 commit into
ruby:masterfrom
tompng:threadsafe_xpath

Conversation

@tompng

@tompng tompng commented Jun 7, 2026

Copy link
Copy Markdown
Member

REXML::Functions have a state and is not thread-safe.
Change it to a class, always create a new instance in XPath#match.
REXML::Functions = REXML::FunctionsClass.new is kept for untested REXML::Quickpath and for test code.

Bug reproduction code

['a', 'b'].map do |name|
  Thread.new do
    doc = REXML::Document.new '<div>' + "<#{name}/>"*100+'</div>'
    # Path that never matches.
    xpath = "//#{name}[name()!='#{name}']"
    loop do
      res = REXML::XPath.match(doc, xpath)
      raise "Shouldn't happen" unless res.empty?
    end
  end
end
sleep 100

After about 10 seconds, it raises "Shouldn't happen"

About deleted include Functions

Functions doesn't have instance methods, only have one constant for internal use. include Functions had no effect.

irb(main):001> REXML::VERSION
=> "3.4.4"
irb(main):002> REXML::Functions.instance_methods
=> []
irb(main):003> REXML::Functions.constants
=> [:INTERNAL_METHODS]

Copilot AI review requested due to automatic review settings June 7, 2026 16:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors REXML XPath function handling to use instance-scoped state (context/namespaces/variables) instead of module-level shared state, improving isolation and enabling safer concurrent use.

Changes:

  • Introduces FunctionsClass with instance variables for context, namespace_context, and variables, plus a new default singleton Functions.
  • Updates XPathParser to use an instance of FunctionsClass for boolean/number/string conversions and function dispatch.
  • Removes include Functions from REXML::XPath and REXML::QuickPath.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
lib/rexml/xpath_parser.rb Switches function evaluation and coercions to an instance (@functions) rather than global Functions state.
lib/rexml/xpath.rb Removes include Functions, altering how XPath functions are accessed through REXML::XPath.
lib/rexml/quickpath.rb Removes include Functions, altering QuickPath’s access to XPath functions.
lib/rexml/functions.rb Replaces module Functions with FunctionsClass and introduces a default singleton instance Functions.
Comments suppressed due to low confidence (1)

lib/rexml/functions.rb:1

  • @@available_functions is automatically populated from all non-internal instance methods via method_added, and send will then invoke them via super (bypassing visibility checks). This makes it easy to accidentally expose non-XPath helper methods as callable XPath functions when new instance methods are added in the future. A safer approach is to explicitly whitelist exported XPath functions (or mark helper methods private and exclude private methods from registration), rather than implicitly exporting everything not listed in INTERNAL_METHODS.
# frozen_string_literal: false

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/rexml/functions.rb
Comment thread lib/rexml/functions.rb
Comment thread lib/rexml/xpath.rb
Comment thread lib/rexml/functions.rb
Comment thread lib/rexml/functions.rb
@tompng tompng force-pushed the threadsafe_xpath branch from 9a862a6 to 8d3faa4 Compare June 8, 2026 12:40
Copilot AI review requested due to automatic review settings June 9, 2026 13:13
@tompng tompng force-pushed the threadsafe_xpath branch from 8d3faa4 to cde809e Compare June 9, 2026 13:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comment thread lib/rexml/xpath_parser.rb Outdated
Comment thread lib/rexml/xpath_parser.rb Outdated
Comment thread lib/rexml/xpath.rb
Comment thread lib/rexml/quickpath.rb
Comment thread lib/rexml/functions.rb
Comment thread lib/rexml/functions.rb
]
class << self
def singleton_method_added(name)
def method_added(name)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As copilot also mentions:

This makes it easy to accidentally expose non-XPath helper methods as callable XPath functions when new instance methods are added in the future.

Relying on this is not good. As a result, compare-language is exposed as a function.

Just an idea, perhaps introducing register method something like this may improve maintainability. (Similar problem also exist in Reline's key binding method)

register_function def position()
  ...
end

@naitoh

naitoh commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

@tompng
Could you please resolve the conflicts?

@tompng tompng force-pushed the threadsafe_xpath branch from cde809e to f82cff4 Compare June 9, 2026 16:08
REXML::Functions have a state and is not thread-safe.
Change it to a class, always create a new instance in XPath#match.
`REXML::Functions = REXML::FunctionsClass.new` is kept for untested REXML::Quickpath and for test code.
Copilot AI review requested due to automatic review settings June 10, 2026 01:41
@tompng tompng force-pushed the threadsafe_xpath branch from f82cff4 to ae0f7a1 Compare June 10, 2026 01:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comment thread lib/rexml/functions.rb
Comment thread lib/rexml/functions.rb
Comment thread lib/rexml/xpath.rb
Comment thread lib/rexml/quickpath.rb
Comment thread lib/rexml/xpath_parser.rb
Comment on lines +388 to +389
@functions.context = target_context
return @functions.send(func_name, *args)

@naitoh naitoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!

@naitoh naitoh merged commit 7d9e7c2 into ruby:master Jun 10, 2026
72 of 73 checks passed
@tompng tompng deleted the threadsafe_xpath branch June 10, 2026 04:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants