[トップ][ノート][編集履歴][一覧][最近の更新][->English]

Erlang

Category of Erlang

並列関数型言語Erlangのねた。

つくったもの

Link

Programming/Erlang/udpmsg

Category of Programming
Category of Erlang

せっかくだからErlang版も貼っとく。 じつはずっと昔に書いたコードだったりする。

-module(udpmsg).
-export([send/2]).

-include_lib("kernel/include/inet.hrl").

-define(IPMSG_VERSION,      16#0001).
-define(IPMSG_DEFAULT_PORT, 16#0979).
-define(IPMSG_SENDMSG,      16#020).

-define(DEFAULT_UDP_OPTS, [
                           {active, false},
%                          {broadcast, true},
                           {reuseaddr, true}
                          ]).

send_format(Num, Host, User, Cmd) ->
    lists:flatten(io_lib:format("~.10b:~.10b:~s:~s:~.10b:",   [?IPMSG_VERSION, Num, User, Host, Cmd])).
send_format(Num, Host, User, Cmd, Extra) ->
    lists:flatten(io_lib:format("~.10b:~.10b:~s:~s:~.10b:~s", [?IPMSG_VERSION, Num, User, Host, Cmd, Extra])).

send(Host, Msg) ->
    Num = erlang:phash(erlang:localtime(), 16777215),
    case gen_udp:open(0, ?DEFAULT_UDP_OPTS) of
        {ok, Socket} ->
            EntMsg = send_format(Num, "myhost", "myname", ?IPMSG_SENDMSG, Msg),
            gen_udp:send(Socket, Host, ?IPMSG_DEFAULT_PORT, EntMsg),
            gen_udp:close(Socket);
        Error ->
            Error
    end.

OpenBSD/Erlang/hipe

Category of OpenBSD
Category of Erlang

ErlangにはHiPE(High Performance Erlang)というしかけがあって、 beam(erlangのためのvm)からネイティブコードを呼びだすよう変換することができる(hipeに関するスライド)。

FreeBSDでもx86だとhipeを有効にしてビルドする(FreeBSD-cvs:ports/lang/erlang/)ので、 OpenBSDでも動かないとおかしいわな、というわけで、otp-R11B-1をいじってみた。

まあ結果としては動いたのだが、別の問題が発覚したのでメモ。

さて、Erlangでhipeを有効にするには./configureに--enable-hipeを追加するだけでよろしい。OpenBSD-cvs:ports/lang/erlang/Makefileを見て、

./configure --disable-jinterface --disable-odbc --enable-threads --enable-kernel-poll --enable-hipe

このような感じか。

で、そのままだと、erts/emulator/hipe/hipe_x86_signal.cでこける。 ざっとコードを見たところ、OpenBSD-man:sigaltstackをdlsymして自前のsigaltstackでラップしているらしいことがわかる。

以前のエントリ(OpenBSD/2006/07/27/dlopen)を踏まえて、 このようなパッチを書いてみた: files:openbsd/otp_src_R11B-1-patch-erts_emulator_hipe_hipe_x86_signal.c

で、上のパッチを書いているときに気が付いたのだが、hipe_x86_signal.cから切り出した部分からのテストコード、

$ cat sigstack.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>

/*
 * Set alternate signal stack for the invoking thread.
 */
static void set_sigaltstack(void *ss_sp)
{
    struct sigaltstack ss;

    ss.ss_sp = ss_sp;
    ss.ss_flags = SS_ONSTACK;
    ss.ss_size = SIGSTKSZ;
    if (sigaltstack(&ss, NULL) < 0) {
    /* might be a broken pre-2.4 Linux kernel, try harder */
    ss.ss_flags = 0;
    if (sigaltstack(&ss, NULL) < 0) {
        perror("sigaltstack");
        abort();
    }
    }
}

main()
{
        static unsigned long my_sigstack[SIGSTKSZ/sizeof(long)];

        set_sigaltstack(my_sigstack);
}

が、-pthread付きでビルドしたときと、無しのときとで挙動が異なる。

$ cc sigstack.c
$ ./a.out
$
$ cc sigstack.c -pthread
$ ./a.out
sigaltstack: Invalid argument
$

結局のところ、 OpenBSD-cvs:src/lib/libpthread/uthread/uthread_sigaltstack.c 33行目はどうみても、

        if (ss == NULL) {

の間違いです。本当にありがとうございました。 本当にコードレビューしているのか怪しいものである。

ということでlibpthreadを再構築して、Erlangをビルド。

$ erl
Erlang (BEAM) emulator version 5.5.1 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.1  (abort with ^G)
1>

hipeが有効になった。 ちなみにhipe付きのbeamコードは、

$ erlc +native file

とするか、erl上で、

> c(module, [native, {hipe, ['O3']}]).

とすればよい。めでたしめでたし。

Erlang/side-effect

Category of Erlang

Erlangと副作用

関数型言語であるErlangは副作用を起こす操作は一般の文法上には存在していない(代入もcall/ccも無い)が、 それでもそのような結果を引き起こすことは可能だ。

Erlangで副作用を発生させる手段には2種類ある(他にもあったらやんわりと指摘頂きたい)。

ドライバは外部のオブジェクト(CやJavaやその他実行ファイル)とリストをやりとりするものだから、 当然副作用は起こりうる(というより、この場合は副作用を起こすことが目的である)ので、ここでは説明しない。

workerとして振る舞わせるには、例えば、

 -behaviour(gen_server).

とプログラム中に書くことでgen_serverとして機能するようになる。

gen_serverとして書かれたコードは5つのコールバック関数を持ち、 supervisorから呼び出されるようになる。

Erlangでは、

 Pid = spawn(?MODULE, foo, []),
 Pid ! Message,
 ...

のようなコードを用いて、サーバ側の状態の変更をさせるのが一般的なのだが、 gen_serverでは、そうする必要が無くなる(単にコールバックをトリガしてやるだけでよい)。 その代償として、 その「状態」を管理することができなくなる(状態の変更はgen_serverとsupervisorの間で行なわれる)。 これが副作用に見えるわけだ。

例を挙げる。

-module(inc).
-behaviour(gen_server).

-export([start_link/0]).
-export([inc/0]).
-export([init/1, handle_call/3]).

start_link() ->
    gen_server:start_link({local, inc}, inc, [], []).

inc() ->
    gen_server:call(?MODULE, inc).

init(_Args) ->
    process_flag(trap_exit, true),
    {ok, 0}.

handle_call(inc, _From, State) ->
    Reply = State,
    {reply, Reply, State + 1}.

先ほど5つのコールバック関数と書いたが、 ここではinit/1とhandle_call/3のみ使用した。

start_link/0とinc/0が利用者側のコードで、 他の関数は触ることができないようになっている。 状態の変更はhandle_call/3で行なっている。

実行結果は以下のようになる。

1> inc:start_link().
{ok,<x.x.x>}
2> inc:inc().
0
3> inc:inc().
1
4> inc:inc().
2

Erlangに関する全てのエントリ