読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

Procとセーフレベル

Ruby

Rubyリファレンスマニュアル セキュリティモデルより。「Procはその時点でのセーフレベルを記憶する。そのProcオブジェクトがcallされると、 記憶していたセーフレベルで実行される。」とのこと。

# セーフレベル0で作ったproc
p0 = proc { $SAFE }
# セーフレベル4で作ったproc
p4 = Thread.fork { 
  $SAFE = 4
  proc { $SAFE } 
}.value

# セーフレベル0で実行
puts "-- safelevel 0"
puts p0.call # => 0
puts p4.call # => 4

# セーフレベル4で実行
puts "-- safelevel 4"
puts Thread.fork {
  $SAFE = 4
  "#{p0.call}\n#{p4.call}" # => 0, 4
}.value

実行結果です。

-- safelevel 0
0
4
-- safelevel 4
0
4

ブロックはどうなるの?

ここでちょっとびくってなったのは、関数に渡されたブロックの扱いについて。ブロックとprocって同じような扱いになっているイメージがあったので、↓みたいな関数を作って使っていたのだけれど、これって期待通りの動作になってないんじゃね?と思った次第。

# ブロックの処理を指定のセーフレベルで実行する
def safe(level=4)
  Thread.fork {
    $SAFE = level
    yield if block_given?
  }.value
end

ということで確認。結論としては、関数に渡されたブロックは

  • yieldで実行すれば、カレントのセーフレベル
  • callで実行すれば、記録されたセーフレベル

で実行されます。なので上の関数は期待通り動作する。

# yieldでブロックを実行するsafe関数
def safe_yield
  Thread.fork {
    $SAFE = 4
    yield
  }.value
end
# callでブロックを実行するsafe関数
def safe_call(&block)
  Thread.fork {
    $SAFE = 4
    block.call
  }.value
end

# ブロック + yield
puts safe_yield { $SAFE } #=> 4

# ブロック + call
puts safe_call { $SAFE } #=> 0

# procをブロックとして渡す + yield
puts safe_yield( &proc { $SAFE } )  #=> 4

# procをブロックとして渡す + call
puts safe_call( &proc { $SAFE } )  #=> 0

実行結果です。

4
0
4
0

これ、いつかはまりそうだなー。