Hatena::Grouperlang

檜山正幸のErlang未確認情報 RSSフィード

 | 

2008-12-15 (月)

事象待ち

| 17:16

timerとかに事象待ちがあるかと思ったらなかったので作った。

%% -*- coding: utf-8 -*-

%% @doc timerモジュールへの追加拡張.
-module(timer_ext).

-export([default_interval/0,
         wait_until/3, wait_until/2, 
         wait_and_do/4, wait_and_do/3]).
%-compile(export_all).

-define(DEFAULT_INTERVAL, 10). % 1/100 sec

%% @doc wait_until, wait_and_do のチェック間隔を指定する定数.
%% @spec () -> integer()
default_interval() ->
  ?DEFAULT_INTERVAL.

%% @doc 条件(引数なし述語)が真になるまで待つ.
%% 第1引数は、引数なしで真偽を返す関数.
%% 第2引数は、チェック間隔(ミリ秒).
%% 第3引数は、最大待ち時間(ミリ秒).
%% 条件が成立すればtrue, タイムアウトしたらfalseを返す.
%% @spec (function(), integer(), integer()) -> bool()
wait_until(Cond, Interval, Limit) when Interval > 0, Limit > 0 ->
  TimerRef = erlang:start_timer(Limit, self(), stop_waiting),
  wait_loop(Cond, Interval, TimerRef).

%% @spec (function(), integer(), reference()) -> bool()
wait_loop(Cond, Interval, TimerRef) ->
  receive 
    {timeout, TimerRef, stop_waiting} ->
      false
  % 他のメッセージが来てもここでは捨てない
  % wait_untilから抜けてから親や祖先が処理するかも知れない
  after Interval ->
      case Cond() of
        true ->
          true;
        false ->
          wait_loop(Cond, Interval, TimerRef)
      end
  end.

%% @equiv wait_until(Cond, default_interval(), Limit)
%% @spec (function(), integer()) -> bool()
wait_until(Cond, Limit)  ->
  wait_until(Cond, ?DEFAULT_INTERVAL, Limit).

%% @doc 条件(引数なし述語)が真になるまで待ち、
%% 指定された関数を実行する.
%% 第1引数は、引数なしで真偽を返す関数.
%% 第2引数は、チェック間隔(ミリ秒).
%% 第3引数は、最大待ち時間(ミリ秒).
%% 第4引数は、引数なし関数、または MFAタプル、またはMFAリスト.
%% タイムアウトしたらtimeoutを返す.
%% 関数が実行されれば {value, Value} を返す.
%% @spec (function(), integer(), integer(), FunSpec) -> Result
%% where
%%  FunSpce = function() 
%%          | {atom(), atom(), list()} 
%%          | cons(atom(), cons(atom(), list()))
%%  Result = timeout | {value, term()}
%% 
wait_and_do(Cond, Interval, Limit, Fun) ->
  case wait_until(Cond, Interval, Limit) of
    false ->
      timeout;
    true ->
      do_it(Fun)
  end.

%% @spec (FunSpec) -> {value, term()}
do_it(Fun) when is_function(Fun) ->
  {value, Fun()};
do_it({M, F, A}) ->
  {value, apply(M, F, A)};
do_it([M, F |A]) ->
  {value, apply(M, F, A)}.

%% @equiv wait_and_do(Cond, default_interval(), Limit, Fun)
%% @spec (function(), integer(), FunSpec) -> Result
%% where
%%  FunSpce = function() 
%%          | {atom(), atom(), list()} 
%%          | cons(atom(), cons(atom(), list()))
%%  Result = timeout | {value, term()}
wait_and_do(Cond, Limit, Fun) ->
  wait_and_do(Cond, ?DEFAULT_INTERVAL, Limit, Fun).

 |