void around()

An Extension of AspectJ to
Weave Aspect into an Arbitrary
Code Region
赤井駿平
08M37025
千葉研究室
1
目的:同期の粒度を切り替えたい

ライブラリが2種類の同期を提供:
–
–

–
2
高並列性
低オーバーヘッド
ライブラリのユーザが最適な粒度を選ぶ
–

細かい粒度の同期
粗い粒度の同期
ライブラリ
環境に応じて
良い性能
粗い同期
同期のコードの付替が容易でなければな
らない
–
アスペクトを利用してはどうか?
細かい同
期
Motivation:
ライブラリにおける同期処理

2006年にJavassistに
対してバグレポート
–

Not thread-safe!
public class ProxyFactory {
public Class createClass() {
if (thisClass == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (useCache){createClass2(cl);}
else {createClass3(cl);}
}}
return thisClass;}
private void createClass2(ClassLoader cl) {
CacheKey key = new CacheKey(…);
HashMap cacheForTheLoader =…;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap();
proxyCache.put(cl, cacheForTheLoader);
cacheForTheLoader.put(key, key);
}else {...}
単純な修正は簡単
–
synchronized文を追加
Class c = isValidEntry(key);
if (c == null) {
createClass3(cl);
key.proxyClass = new WeakReference(…);
}else{thisClass = c;}
3
}}
修正によるパフォーマンスへの影響

synchronized文の付け方
が問題
–
細かい粒度


–
粗い粒度


4
高い並列性
オーバーヘッドが多い
並列性が低い
少コアのマシンでは早い
public class ProxyFactory {
public Class createClass() { Coarse-grained
if (thisClass == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (useCache){createClass2(cl);}
else {createClass3(cl);}
}}
return thisClass;}
Fine-grained
private void createClass2(ClassLoader
cl) {
CacheKey key = new CacheKey(…);
synchronized (proxyCache) {
HashMap cacheForTheLoader =…;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap();
proxyCache.put(cl, cacheForTheLoader);
cacheForTheLoader.put(key, key);
}else {...}}
synchronized (key) {
Class c = isValidEntry(key);
if (c == null) {
createClass3(cl);
key.proxyClass = new WeakReference(…);
}else{thisClass = c;}
}}}
アスペクト指向で同期を切り替えたい

横断的関心事をモジュール化
–
–
–
advice
join point: プログラム実行中の実行点(メソッド呼び出し,
フィールドアクセス…)
pointcut: join point を選ぶ手段
advice: 選択したjoin point で実行するコード
pointcut
void around (): call(* *.foo())
{bar(); proceed() ;}
5
public String toString(){
foo();
return …;
}
call join point
アスペクト指向で同期を切り替えたい

領域に対して
around adviceを
織り込みたい
void around ():
pointcut(coarse_grained_region)
{ synchronized(…) { proceed(); } }
6
void around ():
pointcut(fine_grained_regions)
{ synchronized(…) { proceed(); } }
public class ProxyFactory {
public Class createClass() {
if (thisClass == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (useCache){createClass2(cl);}
else {createClass3(cl);}
}}
return thisClass;}
private void createClass2(ClassLoader cl) {
CacheKey key = new CacheKey(…);
synchronized (proxyCache) {
HashMap cacheForTheLoader =…;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap();
proxyCache.put(cl, cacheForTheLoader);
cacheForTheLoader.put(key, key);
}else {...}}
synchronized (key) {
Class c = isValidEntry(key);
if (c == null) {
createClass3(cl);
key.proxyClass = new WeakReference(…);
}else{thisClass = c;}
}}}
Our Proposal

Regioncut
–
領域を選択する指定子
void around():
region[ call(* Map.get(..)), call(* Map.put(..)) ]
{ …; proceed(); …;}

Assertion for Advice
–
–
7
fragile性 の問題を解決するため
指定したアドバイスが織り込まれていないと警告
@AssertAdvised(“lock_map”)
void foo(){ … }
@SolveProblem(“Foo.lock_map”)
void around(): … { … }
AspectJでは実現不可


Pointcutは領域を選択できない
execution “point”のみ選択可能
–
–
–
Call: メソッド呼び出しを選択
Get, set: フィールドアクセスを選択
Execution: メソッドボディを選択



8
around advice を使えばメソッドボディ全体は同期可能
メソッドボディが最適な粒度とは限らない→リファクタリング
が必要
全ての粒度の同期の箇所をメソッドに抽出するのは大変
なぜAspectJは同期/領域を扱えないか

Fragile性を減らせない
–
コードが変更されると選択できなくなる可能性


–
同期は必須の関心事


9
領域にはpointcut より指定に多くの情報が必要
pointcut より変更に弱い
同期アスペクトを織り込み忘れると正しく動かない
AspectJでは織り込み忘れを発見できない
Our Proposal:
Regioncut

新しいポイントカット指定子のようなもの
–

領域を選択
領域を指定するには
–
領域内のexecution pointの中で重要なものを列挙


call, get, set, が使える
選択したい典型的な領域はそれで指定できる
region[
call(* Map.get(..)),
call(Object nextValue(Object)),
call(* Map.put(..))
]
10
void foo(Map map){
Object o=map.get(key);
println(o);
o=nextValue(o);
map.put(key,o);}
領域の拡大

領域と制御構造が衝突
–

void foo(Map map){
around advice を織り込
めない
…
Object o=map.get(key);
領域を拡大:
–
–
–
Conflict
println(o);
o=nextValue(o);
元の領域を含み
制御構造を含み
かつできるだけ小さく
if(…){
map.put(key,o);
…
}
…
11
}
Expanded
Our Proposal:
Assertion for Advice

Fragile性を
減らすため
織り込まれなくなったアドバイスを見つけて警告
–
@AssertAdvised(“<concern_name>”)

–
adviceが必要なメソッドに付加.関心事の名前を付ける
@SolveProblem(“<ClassName>.<concern_name>”)

対象の関心事を解決するadviceに付加
class Foo{
@AssertAdvised(“lock_map”)
void foo(Map map){
…
12
}
}
@SolveProblem(“Foo.lock_map”)
void around(): region[ … ]{
synchronize(…){
proceed();
}
}
Assertion for Advice チェッカー


@AssertAdviced の付
いたメソッドが対応する
adviceを持つかチェック
以下の場合警告なし:
–
–
13
adviceがメソッドを呼ん
でいる
メソッドがadviceを呼ん
でいる

間接的にでもOK
@SolveProblem(“Foo.lock_map”)
void around(): … { … }
Call the
method
@AssertAdvised(“lock_map”)
void foo(){ … }
Call the
advice
@SolveProblem(“Foo.lock_map”)
void around(): … { … }
2つのチェッカーを開発

静的なチェッカー
–
–
–
クラスファイルを解析
コールグラフを調べる
制限


動的なチェッカー
–
–
14
リフレクションに対応できない
実行時に(呼び出した|呼ばれた)かを調べる
実際に実行された部分のみチェック可能
Implementation

the AspectBench Compiler (abc)を拡張
–
Regioncut

–
Assertion for Advice


–
15
中間言語Jimpleを解析/変換
dynamic: ASTを変換してダイナミックにチェック
static: Sootを利用
10,000+ LOC
Implementation Issue(1/2):
Around advice

abc で around adviceを織り込む場合
–
–
join points/regions を抽出しメソッドに分ける
問題


ローカル変数を共有できない
領域の外へジャンプできない
public void toBeAdvised(int x){
a();
b(x);
advice(x);
c();
}
16
public static void advice(int x){
beforeJoinPoint();
shadow(x);
afterJoinPoint();
}
public static shadow(int x){
a();
b(x);
c();
}
Implementation Issue(2/2):
解決法

ローカル変数を保存するオブジェクト
–

引数経由で共有して対処
ジャンプ先を示す値を変数に入れて共有
–
領域を抜けてからジャンプするように変換
share
public void toBeAdvised(int x){
advice(x);
}
17
public static void advice(Obj o){
beforeJoinPoint();
shadow(x);
afterJoinPoint();
}
{
int x;
int jmpTgt;
}
public static shadow(Obj o){
a();
b(x);
c();
}
評価: regioncutの記述力

Hadoopのsynchronized文をアスペクトに分離
–
–
–
–
regioncutが領域を十分に選択できるかを評価
Hadoop 0.16.4
通常のpointcutで選択できなかったものは全て選択できた
12の内の8領域が拡大が必要
Synchronized 文
通常のpointcutで選択された
regioncutで選択された
18
21
9
12
評価: Assertion が有効に働くか

Hadoop を 0.16.4 から 0.18.3
に更新
–
–
dynamic版を使用
Hadoopの ユニットテストを動か
してチェック



19
6個が正しく織り込まれなかっ
た
2個は検知
残りは実行されていなかったの
で検知不可
旧バージョンでのregioncut
12
正しく織り込まれた
6
assertionで検出された
2
検出されなかった
4
Current Limitation

選択した2つ以上の領域が重なると
–
–
around adviceをweaveできない
何らかの優先順位を付ける必要がある
void withConflict(){
beginA();
beginB();
endA();
endB();
}
20
Related Work


領域へのpointcutは重要な問題
限定的な解
–
–

Loop join point: ループボディにマッチ
Synchronized join point: synchronized文にマッチ
同等の解
–
Transactional pointcut

21
同じ会議で同時にaccept
Conclusion

アスペクト指向言語で領域を選択を実現
–
–

Regioncut
Assertion for advice
これまでの成果
–
査読付き

–
査読なし/ワークショップ


22
GPCE'09, 2009-10, Denver
ソフトウェア科学会大会,2008-07,東京
ACP4IS’09, 2009-03, Charlottesville