Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
张重言
rails
提交
a010fc18
R
rails
项目概览
张重言
/
rails
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
rails
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
a010fc18
编写于
6月 21, 2012
作者:
Y
Yehuda Katz
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #6428 from pinetops/resolver_concurrency_fix
Make the Resolver template cache threadsafe
上级
6688cd2b
67bf728a
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
80 addition
and
23 deletion
+80
-23
actionpack/lib/action_view/template/resolver.rb
actionpack/lib/action_view/template/resolver.rb
+76
-19
actionpack/test/template/lookup_context_test.rb
actionpack/test/template/lookup_context_test.rb
+4
-4
未找到文件。
actionpack/lib/action_view/template/resolver.rb
浏览文件 @
a010fc18
...
...
@@ -2,6 +2,7 @@
require
"active_support/core_ext/class"
require
"active_support/core_ext/class/attribute_accessors"
require
"action_view/template"
require
"thread"
module
ActionView
# = Action View Resolver
...
...
@@ -31,6 +32,72 @@ def to_str
alias
:to_s
:to_str
end
# Threadsafe template cache
class
Cache
#:nodoc:
class
CacheEntry
attr_accessor
:templates
delegate
:synchronize
,
:to
=>
"@mutex"
def
initialize
@mutex
=
Mutex
.
new
end
end
def
initialize
@data
=
Hash
.
new
{
|
h1
,
k1
|
h1
[
k1
]
=
Hash
.
new
{
|
h2
,
k2
|
h2
[
k2
]
=
Hash
.
new
{
|
h3
,
k3
|
h3
[
k3
]
=
Hash
.
new
{
|
h4
,
k4
|
h4
[
k4
]
=
{}
}
}
}
}
@mutex
=
Mutex
.
new
end
# Cache the templates returned by the block
def
cache
(
key
,
name
,
prefix
,
partial
,
locals
)
cache_entry
=
nil
# first obtain a lock on the main data structure to create the cache entry
@mutex
.
synchronize
do
cache_entry
=
@data
[
key
][
name
][
prefix
][
partial
][
locals
]
||=
CacheEntry
.
new
end
# then to avoid a long lasting global lock, obtain a more granular lock
# on the CacheEntry itself
cache_entry
.
synchronize
do
if
Resolver
.
caching?
cache_entry
.
templates
||=
yield
else
fresh_templates
=
yield
if
templates_have_changed?
(
cache_entry
.
templates
,
fresh_templates
)
cache_entry
.
templates
=
fresh_templates
else
cache_entry
.
templates
||=
[]
end
end
end
end
def
clear
@mutex
.
synchronize
do
@data
.
clear
end
end
private
def
templates_have_changed?
(
cached_templates
,
fresh_templates
)
# if either the old or new template list is empty, we don't need to (and can't)
# compare modification times, and instead just check whether the lists are different
if
cached_templates
.
blank?
||
fresh_templates
.
blank?
return
fresh_templates
.
blank?
!=
cached_templates
.
blank?
end
cached_templates_max_updated_at
=
cached_templates
.
map
(
&
:updated_at
).
max
# if a template has changed, it will be now be newer than all the cached templates
fresh_templates
.
any?
{
|
t
|
t
.
updated_at
>
cached_templates_max_updated_at
}
end
end
cattr_accessor
:caching
self
.
caching
=
true
...
...
@@ -39,12 +106,11 @@ class << self
end
def
initialize
@cached
=
Hash
.
new
{
|
h1
,
k1
|
h1
[
k1
]
=
Hash
.
new
{
|
h2
,
k2
|
h2
[
k2
]
=
Hash
.
new
{
|
h3
,
k3
|
h3
[
k3
]
=
Hash
.
new
{
|
h4
,
k4
|
h4
[
k4
]
=
{}
}
}
}
}
@cache
=
Cache
.
new
end
def
clear_cache
@cache
d
.
clear
@cache
.
clear
end
# Normalizes the arguments and passes it on to find_template.
...
...
@@ -72,27 +138,18 @@ def build_path(name, prefix, partial)
# Handles templates caching. If a key is given and caching is on
# always check the cache before hitting the resolver. Otherwise,
# it always hits the resolver but
check if the resolver is fresher
# before returning it.
# it always hits the resolver but
if the key is present, check if the
#
resolver is fresher
before returning it.
def
cached
(
key
,
path_info
,
details
,
locals
)
#:nodoc:
name
,
prefix
,
partial
=
path_info
locals
=
locals
.
map
{
|
x
|
x
.
to_s
}.
sort!
if
key
&&
caching?
@cached
[
key
][
name
][
prefix
][
partial
][
locals
]
||=
decorate
(
yield
,
path_info
,
details
,
locals
)
else
fresh
=
decorate
(
yield
,
path_info
,
details
,
locals
)
return
fresh
unless
key
scope
=
@cached
[
key
][
name
][
prefix
][
partial
]
cache
=
scope
[
locals
]
mtime
=
cache
&&
cache
.
map
(
&
:updated_at
).
max
if
!
mtime
||
fresh
.
empty?
||
fresh
.
any?
{
|
t
|
t
.
updated_at
>
mtime
}
scope
[
locals
]
=
fresh
else
cache
if
key
@cache
.
cache
(
key
,
name
,
prefix
,
partial
,
locals
)
do
decorate
(
yield
,
path_info
,
details
,
locals
)
end
else
decorate
(
yield
,
path_info
,
details
,
locals
)
end
end
...
...
actionpack/test/template/lookup_context_test.rb
浏览文件 @
a010fc18
...
...
@@ -180,7 +180,7 @@ def teardown
class
LookupContextWithFalseCaching
<
ActiveSupport
::
TestCase
def
setup
@resolver
=
ActionView
::
FixtureResolver
.
new
(
"test/_foo.erb"
=>
[
"Foo"
,
Time
.
utc
(
2000
)])
@r
esolver
.
stubs
(
:caching?
).
returns
(
false
)
ActionView
::
R
esolver
.
stubs
(
:caching?
).
returns
(
false
)
@lookup_context
=
ActionView
::
LookupContext
.
new
(
@resolver
,
{})
end
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录