Journal InTime


2014-03-15 (Sat) [長年日記]

_ 松江Ruby会議05

松江Ruby会議05に参加してきた。参加されたみなさん、おつかれさまでした。

ライブコーディングがテーマだったので色んな人のコードを見られて楽しかった。まつもとさんも珍しく書き下ろしのコードを紹介してたし。

とくに圧巻だったのはDXRuby作者のmirichiさんのライブコーディングで、15分程度(?)でその場で一からブロック崩しを作られていた。 スプライトの衝突判定にSprite#===を使われていたのが印象的だったけど、たぶんcaseやgrepで使えるようにという設計なんだろう。

自分がライブコーディングで書いたコードは以下のURLに置いておいた。

このコードだと単純なpointcutしか表現できないけど、例えば、

class Foo
  def bar
    Bar.new.baz
  end
end

のようなコードがあった時に、AspectJみたいにwithin(Foo) & call(class: Bar, method: :baz)みたいなpointcutの指定(Fooの中のBar#bazの呼び出しだけにアスペクトを織り込む)ができると使いやすそう。

しかし、Module#prependだとメソッドの呼び出し側でなく呼び出されるメソッドの側を置き換えるので、Fooから呼び出されたかどうかの判定に呼び出し元のクラスの情報が必要になるが、Rubyレベルではその情報を取るAPIがない。 気が向いたらrb_debug_inspector_open()とかを濫用して作るかもしれないけど、呼び出し元のクラスくらいは普通にRubyレベルで取れてもいいように思う。

Tags: Ruby
本日のツッコミ(全3件) [ツッコミを入れる]

_ 宮国 [私は宮崎で木工作家をしている宮国と申します。 現在は仕事の傍ら、以下のウェブサイトを運営しております。 四畳..]

_ shugo [コメントありがとうございます。 レポートを興味深く拝見しましたが、問題意識に共感いただけたことを嬉しく思います..]

_ 宮国 [見ていただいてありがとうございました。コメントもありがとうございます。 卒業論文であのようなレベルのものが書けるの..]


2014-03-04 (Tue) [長年日記]

_ 松江Ruby会議05予告

松江Ruby会議05でライブコーディングをすることになったが、「Module#prependとRefinementsで遊ぶ」というタイトルだけ出して、実は内容を考えてなかった。

以下のようなAspectJ風のコードを書くためのaspect.rbを実装しようと思う。

require "aspect"

class Job
  def run
    raise "subclass must override Command#execute"
  end
end

class PrintJob < Job
  def run
    puts "running PrintJob..."
  end
end

class BackupJob < Job
  def run
    puts "running BackupJob..."
  end
end

module Logging
  extend Aspect

  JOBS = execution(class: Job, method: :run)

  around JOBS do |join_point|
    STDERR.puts("enter #{join_point.signature}")
    begin
      join_point.proceed
    ensure
      STDERR.puts("exit #{join_point.signature}")
    end
  end
end

jobs = [PrintJob.new, BackupJob.new]
jobs.each(&:run)

実質20分くらいの持ち時間で上手く書けるかどうか。お楽しみに。

Tags: Ruby

2014-02-20 (Thu) [長年日記]

_ A memory of Jim Weirich

Today, I heard that Jim Weirich had passed away, but I can't believe it yet.

I met him at the first time at RubyConf 2005. It was also the first time I have attended RubyConf. He did a workshop titled "Continuations Demystified" with Chad Fowler there. In the workshop he explained continuations by comparing them to The Legend of Zelda, which is a Nintendo 64 video game. I had such a fun time.

You can download presentation materials from the following site:

<URL:http://www.zenspider.com/Languages/Ruby/RubyConf2005.html>

It's too late, but I've solved an exercise from the workshop, which exercise I didn't solve at that time.

The exercise is to implement throw/catch using continuations.

EXAMPLE:

result = cc_catch(:tag) {
  do_something()
  cc_throw(:tag, 1)
  fail "You never get here"
}
assert_equal 1, result

My answer is:

require "continuation"

def cc_tag_stack
  Thread.current[:cc_tag_stack] ||= []
end

def cc_catch(tag)
  callcc { |c|
    cc_tag_stack.push([tag, c])
    begin
      yield
    ensure
      cc_tag_stack.pop
    end
  }
end

def cc_throw(tag, obj = nil)
  stack = cc_tag_stack.dup
  while x = stack.pop
    t, c = x
    if t == tag
      # The ensure clause in cc_catch is not called when c.call(obj) is
      # called, so cc_tag_stack should be rewound here.
      Thread.current[:cc_tag_stack] = stack
      c.call(obj)
    end
  end
  raise NameError, "uncaught throw #{tag}"
end

I don't like this code very much because cc_throw is ugly. Jim's code is simpler:

$continuations = {}

def cc_catch(sym)
  callcc { |cc|
    $continuations[sym] ||= []
    $continuations[sym] << cc
    yield
  }
ensure
  $continuations[sym].pop
end

def cc_throw(sym, value=nil)
  cc = $continuations[sym]
  fail NameError, "uncaught throw `#{sym}'" if cc.nil? || cc.empty?
  cc.last.call(value)
end

I thank Jim for all his efforts for Ruby and the community. May his spirit rest in peace.

Tags: Ruby

2014-02-19 (Wed) [長年日記]

_ キャストパズル

キャストパズル

子供の知恵の輪を弄っていたらもうちょっと本格的なものをやりたくなったので、ハナヤマのキャストパズルというシリーズのキャストマーブルとキャストドーナツを購入した。

表示難易度がより高いキャストマーブル(写真左)の方はかなり感動的なアクションでさくっと外れたが、キャストドーナツ(写真右)の方はたぶんここから外すんだろうなという目処をつけてから外れるまで結構手間がかかった。元に戻すのも手間がかかったし。

構造を理解するとなるほどという造りなんだけど、外さないと内部構造がわからないのでもどかしい。

キャストマーブルの方が外す時と戻す時のアクションがシンプルかつアクロバティックなので、解けた後も楽しめる感じ。

知的立体パズルゲーム キャストマーブル

Tags: 買い物

2014-02-12 (Wed) [長年日記]

_ 二つのフラグをロックせずに書き換える方法

先日の記事でPR_SET_DUMPABLE周りのLinuxカーネルのコードを読んでいた時に面白いコードがあったのでメモ。

以下のコードは、fs/exec.cのset_dumpable()という関数の定義で、引数のvalueは0(coreダンプしない)・1(coreダンプする)・2(suidセーフにcoreダンプする)という三つの値を取る。

/*
 * set_dumpable converts traditional three-value dumpable to two flags and
 * stores them into mm->flags.  It modifies lower two bits of mm->flags, but
 * these bits are not changed atomically.  So get_dumpable can observe the
 * intermediate state.  To avoid doing unexpected behavior, get get_dumpable
 * return either old dumpable or new one by paying attention to the order of
 * modifying the bits.
 *
 * dumpable |   mm->flags (binary)
 * old  new | initial interim  final
 * ---------+-----------------------
 *  0    1  |   00      01      01
 *  0    2  |   00      10(*)   11
 *  1    0  |   01      00      00
 *  1    2  |   01      11      11
 *  2    0  |   11      10(*)   00
 *  2    1  |   11      11      01
 *
 * (*) get_dumpable regards interim value of 10 as 11.
 */
void set_dumpable(struct mm_struct *mm, int value)
{
	switch (value) {
	case SUID_DUMPABLE_DISABLED:
		clear_bit(MMF_DUMPABLE, &mm->flags);
		smp_wmb();
		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
		break;
	case SUID_DUMPABLE_ENABLED:
		set_bit(MMF_DUMPABLE, &mm->flags);
		smp_wmb();
		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
		break;
	case SUID_DUMPABLE_SAFE:
		set_bit(MMF_DUMP_SECURELY, &mm->flags);
		smp_wmb();
		set_bit(MMF_DUMPABLE, &mm->flags);
		break;
	}
}

コメントにもあるとおり、valueで指定した値は、カーネル内部では独立した二つのフラグで保持されるが、フラグの変更はアトミックに行われないので、中間状態の値を参照してしまう可能性がある。コメントの表でいうと、old→newが0→2、2→0のケースでは、二つのフラグが変更されるため、値の不整合が発生してしまう。

上記のコードでは、この問題を解決するために、フラグ操作の順番を注意深く制御することで、上記のケースでは中間状態が必ず決まった値を取るようにし、その値になっている場合は両方のフラグが立っている状態と見做すことにしている。 具体的には二つもとフラグをクリアする場合はMMF_DUMPABLEからクリアし、二つともフラグをセットする場合はMMF_DUMP_SECURELYからセットすることで、中間状態は必ずMMF_DUMP_SECURITYのみが立っている状態(2進表記で10)になる。

ちなみに上記のsmp_wmb()はメモリへの書き込みを待つためのマクロで、これによって実行順序を保証している(詳しくはがちゃぴん先生の記事参照)。

フラグを参照するget_dumpable()の方では、以下のように値がSUID_DUMPABLE_ENABLED(2進表記で01)より大きければ(つまり10か11なら)、SUID_DUMPABLE_SAFEを返すようになっている。

int __get_dumpable(unsigned long mm_flags)
{
	int ret;

	ret = mm_flags & MMF_DUMPABLE_MASK;
	return (ret > SUID_DUMPABLE_ENABLED) ? SUID_DUMPABLE_SAFE : ret;
}

int get_dumpable(struct mm_struct *mm)
{
	return __get_dumpable(mm->flags);
}
Tags: Linux C