ししちにじゅうはち 4x7=28

よんたったー https://twitter.com/keita44_f4

SML#のrefとCのptrに互換性が欲しい

Cのpthread_createとSML#でやりたいこと

SML#に多相型で安全なpthread_createを作りたいお話.
のはずだったんだけど,現状ぶち当たった問題点について.

もとのpthread_createは,

int pthread_create(pthread_t * thread,
                   pthread_attr_t * attr,
                   void * (*start_routine)(void *),
                   void * arg);

で,第3引数がスレッドとして実行する関数,第4引数がその引数になる.

SML#で素直にインポートすると以下のようになる.

type pthread_t = unit ptr
val pthread_create =
    _import "pthread_create"
    : (pthread_t ref, unit ptr, unit ptr -> unit ptr, unit ptr) -> int

でもunit ptr*1とか使ってらんねーよwww
ということで以下のように使いたくなる.

val pthreadCreate : 'a::{only ptr prefix type}. ('a -> unit) -> 'a -> int

ってことをやってたんだけど,この実装のために

val ptr : 'a::{only ptr prefix type}. 'a -> 'a ptr

を作ろうとして詰まってる途中の話.

実装の方針とその結果として詰まったこと

  1. "fun pthreadCreate (f:'a->unit) (x:'a)"において,xをpthread_createで使えるようunit ptrになんとかキャストする関数を作る
  2. 同様に,fをpthread_createで使えるように"fn (arg:unit ptr) => f (Pointer.load (_cast(arg) : 'a ptr))"の関数とする.
  3. 多相なpthreadCreateでけたー!

そこでまずは値の参照を手に入れる.
そのためにref*2を_cast*3使ってptrへキャストすればいけると思ったんだけど.

# _cast(ref 1) : int ptr;
val it = ptr : int ptr
# Pointer.load (_cast(ref 1) : int ptr);
val it = 1 : int
# Pointer.load (_cast(ref 1) : int ptr);
val it = 1 : int

お,いけそうかな?

# fun intPtr (x : int) = _cast(ref x) : int ptr;
val intPtr = fn : int -> int ptr
# val p = intPtr 1;
val p = ptr : int ptr
# Pointer.load p;
val it = 3 : int
# Pointer.load p;
val it = 2147483647 : int
# Pointer.load p;
val it = ~1463444104 : int

ぐぬぬぬ.どういうことだ.
キャスト(ref→ptr)してロードしただけなのに,値が変化している.
ref x部を関数内にいれたことでGCされてるか何か?

なんにせよこれではうまくいかない.
別案を考えなくては….

追記

そもそも自分の理解が間違っていた._(:3」∠)_

ref xはxのコピーx'を作ってその参照を返す.
だから上記intPtrのコードの目的は「xの参照を得る」なのに,動作は計算途中に束縛したx'の参照が得られる.
しかもこのx'はどっかでGCされてしまう?

そこで以下のように予め参照値の領域を確保して,コピーx'の参照値の保存先とすれば,一応目的は達成できそう.

# fun intPtr (x : int) (res : int ref) =
  let
    val () = res := x
  in
    _cast (res) : int ptr
  end;
val intPtr = fn : int -> int ref -> int ptr
# val p = intPtr 1 (ref 0);
val p = ptr : int ptr
# Pointer.load p;
val it = 1 : int
# Pointer.load p;
val it = 1 : int

これで行けるかな?
夕飯食ってから実装の続きをしよう.

*1:SML#においてCの参照型はptr型となり,SMLの参照型refとは別扱い

*2:SMLの参照型

*3:非公式サポートでコンパイラ実装に使われるキャスト