練習用コード。1つのプロセスで1件の予定を記憶する、すげーシンプルなリマインダ。

最初のバージョン(もちろん間違っている)

 -module(sched).
 -compile(export_all).

start() ->
    spawn(fun() -> calender(self()) end).

rpc(Pid, Request) ->
    Pid ! {self(), Request},
    receive
	{Pid, Response} ->
	    Response
    after 10000 ->
        response_wait_timeout
    end.


calender(Pid) ->
    receive
        {Pid, {put, Until, Task}} ->
	     TaskPid = spawn(fun() -> waituntil(Pid, Until, Task) end),
	     io:format("received",),
	     Pid ! {Pid, {TaskPid, ok}},
	     calender(Pid);
	{Pid, {cancel, TaskPid}} ->
	     TaskPid ! {Pid , cancel},
	     io:format("cancel kickked: ~p~n",[TaskPid]),
	     Pid ! {Pid, cancel_done},
	     calender(Pid);
	_Any ->
	     io:format("dropped : ~p~n",[_Any]),
	     Pid ! {Pid, mnn}
    end.

waituntil(Pid, Until, Task) ->
    receive
      {KillerPid, cancel} ->
          io:format("killed",),
          KillerPid! {KillerPid, "I am dropped"}
    after Until ->
      io:format("DONE",[]),
      Pid ! {Pid, Task}
    end.

デバッグプリントごりごりで、とにかくcancelできましたってやつ

 -module(sched1).
 -compile(export_all).

start() ->
    ParentPid = self(),
    spawn(fun() -> calender(ParentPid) end).

rpc(TargetPid, Request) ->
    io:format("rpc:~p: kick to ~p, by ~p~n",[self(),TargetPid, Request]),
    ReturnPid = self(),
    TargetPid ! {ReturnPid, Request},
    receive
	{ReturnPid, Response} ->
	    io:format("rpc:~p: response received normal response ~p~n",[self(),Response]),
	    Response;
        _Any ->
	    io:format("rpc:~p: response received invalid response ~p~n",[self(),_Any]),
	    _Any
    after 10000 ->
        response_wait_timeout
    end.

set_timer(Timeout, Task) ->
    CalenderPid = start(),
    rpc(CalenderPid, {put, Timeout, Task}).

cancel_timer(SchedulePid) ->
    rpc(SchedulePid, {cancel, self()}).

check_timer() ->
    WaitPid = self(),
    receive
      {WaitPid, Result} ->
	io:format("check_timer:~p: Result: ~p~n",[self(),Result])
    after 0 ->
      "not yet."
    end.

calender(Pid) ->
    io:format("calender:~p: caller Pid is set as : ~p~n",[self(),Pid]),
    receive
        {Pid, {put, Until, Task}} ->
	     io:format("calender:~p: put cmd kicked from ~p until: ~p task: ~p~n",[self(),Pid, Until, Task]),
	     TaskPid = spawn(fun() -> waituntil(Pid, Until, Task) end),
	     io:format("calender:~p: task pid: ~p spawned~n",[self(),TaskPid]),
	     Pid ! {Pid, {TaskPid, ok}},
	     calender(Pid);
	{Pid, {cancel, TaskPid}} ->
	     TaskPid ! {Pid , cancel},
	     io:format("calender:~p: cancel kicked from: ~p for task pid: ~p~n",[self(),Pid, TaskPid]),
	     Pid ! {Pid, cancel_done},
	     calender(Pid);
	{_T,_Any} ->
	     io:format("calender:~p: dropped message from: ~p, should be: ~p, remainings: ~p~n",[self(),_T,Pid,_Any]),
	     Pid ! {Pid, mnn}
    end.

waituntil(Pid, Until, Task) ->
    receive
      {KillerPid, {cancel, KillerPid}} ->
          io:format("waituntil:~p: killed~n",[self()]),
          KillerPid! {KillerPid, "I am dropped"};
      _Any ->
          io:format("waituntil:~p: invalid command ~p~n",[self(),_Any]),
          waituntil(Pid, Until, Task)
    after Until ->
      io:format("waituntil:~p:DONE set timer from ~p of task : ~p~n",[self(), Pid, Task]),
      Pid ! {Pid, Task}
    end.

実行結果サンプル1(かろうじてキャンセルはできた)

1> c(sched1).
{ok,sched1}
2> R=sched1:set_timer(60000, "wait 1 minute").
rpc:<0.31.0>: kick to <0.38.0>, by {put,60000,"wait 1 minute"}
calender:<0.38.0>: caller Pid is set as : <0.31.0>
calender:<0.38.0>: put cmd kicked from <0.31.0> until: 60000 task: "wait 1 minute"
calender:<0.38.0>: task pid: <0.39.0> spawned
calender:<0.38.0>: caller Pid is set as : <0.31.0>
rpc:<0.31.0>: response received normal response {<0.39.0>,ok}
{<0.39.0>,ok}
3> {S, ok} = R.
{<0.39.0>,ok}
4> S.
<0.39.0>
5> sched1:cancel_timer(S).
rpc:<0.31.0>: kick to <0.39.0>, by {cancel,<0.31.0>}
waituntil:<0.39.0>: killed
rpc:<0.31.0>: response received normal response "I am dropped"
"I am dropped"

実行結果サンプル2:タイムアウトしたケース(あ、メッセージ拾ってないな)

6> T=sched1:set_timer(600, "wait 0.6 sec").   
rpc:<0.31.0>: kick to <0.44.0>, by {put,600,"wait 0.6 sec"}
calender:<0.44.0>: caller Pid is set as : <0.31.0>
calender:<0.44.0>: put cmd kicked from <0.31.0> until: 600 task: "wait 0.6 sec"
calender:<0.44.0>: task pid: <0.45.0> spawned
calender:<0.44.0>: caller Pid is set as : <0.31.0>
rpc:<0.31.0>: response received normal response {<0.45.0>,ok}
{<0.45.0>,ok}
waituntil:<0.45.0>:DONE set timer from <0.31.0> of task : "wait 0.6 sec"

ところで、これは練習というか、素振りレベルなんで、これ以上は深追いしない(汗
デバッグしてて感じたのは、コードのスタイルというか、rpc関数ではself()を最初に埋めておく、というbest practiceみたいなのが身に付くと、かなり楽だなということ。
これが不統一なコードを書いてしまったところで激ハマった。
現状のコードは無駄が多いので、これは捨てるけれど、もう一度書くなら、rpcと対になるdebug_trace_printみたいな関数が書きやすいように意識してメッセージのスタイルを決めるだろう。

もひとつの練習

reverse proxy。タイムアウトしたら、ごめんなさいページを表示するってやつ。書いておくこと>自分
これが作れて、オーバーヘッドがかなり小さければ、DoSアタックやYahooとかからのリンクを受けたときのピークパフォーマンス(受け入れ可能な同時接続数)が予見可能かつかなり多くなるし、ごめんなさいページの仕様を実行中に書き換えたりもできる。