From 929f9fd3fe7d2b0fa698ea3d461da07ce7ebec11 Mon Sep 17 00:00:00 2001 From: Robert Fletcher Date: Tue, 16 Jan 2024 16:27:58 -0800 Subject: [PATCH] Fix threading issue with strict locals Fixes #50774 When the server boots up, 2 threads hit the same `UnboundTemplate` instance before it has set up `@templates`. Both threads get past the `unless template = @templates[locals]` check because `@templates[locals]` isn't set yet. However, with `@write_lock`, one thread waits while the other one proceeds, setting `@templates` to a frozen hash. The second thread then gets the write lock and tries to modify `@templates` but it has been frozen. --- actionview/lib/action_view/unbound_template.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/actionview/lib/action_view/unbound_template.rb b/actionview/lib/action_view/unbound_template.rb index 699b21f953..b9e28e2e7d 100644 --- a/actionview/lib/action_view/unbound_template.rb +++ b/actionview/lib/action_view/unbound_template.rb @@ -26,15 +26,15 @@ def bind_locals(locals) # while holding the lock. template = (@templates[normalized_locals] ||= build_template(normalized_locals)) - # This may have already been assigned, but we've already de-dup'd so - # reassignment is fine. - @templates[locals.dup] = template - if template.strict_locals? # Under strict locals, we only need one template. # This replaces the @templates Concurrent::Map with a hash which # returns this template for every key. @templates = Hash.new(template).freeze + else + # This may have already been assigned, but we've already de-dup'd so + # reassignment is fine. + @templates[locals.dup] = template end end end