diff --git a/src/couch_event_registry.erl b/src/couch_event_registry.erl index 19e70c2..adea994 100644 --- a/src/couch_event_registry.erl +++ b/src/couch_event_registry.erl @@ -57,63 +57,86 @@ terminate(_Reason, _St) -> handle_call({register, Pid, DbNames}, _From, St) -> - lists:foreach(fun(DbName) -> - ets:insert(?REGISTRY_TABLE, #client{dbname=DbName, pid=Pid}) - end, DbNames), - case ets:lookup(?MONITOR_TABLE, Pid) of - [] -> + ToAdd = [#client{dbname=DBN, pid=Pid} || DBN <- DbNames], + ets:insert(?REGISTRY_TABLE, ToAdd), + case ets:member(?MONITOR_TABLE, Pid) of + true -> + ok; + false -> Ref = erlang:monitor(process, Pid), - ets:insert(?MONITOR_TABLE, {Pid, Ref}); - [{Pid, _}] -> - ok + ets:insert(?MONITOR_TABLE, {Pid, Ref}) end, {reply, ok, St}; handle_call({unregister, Pid, DbNames}, _From, St) -> + % TODO: Check into a multi-pattern matchspec and the + % use of select_delete to see if that's faster. lists:foreach(fun(DbName) -> - unregister_pattern(#client{dbname=DbName, pid=Pid, _='_'}) + ets:match_delete(?REGISTRY_TABLE, pattern(DbName, Pid)) end, DbNames), + maybe_drop_monitor(Pid), {reply, ok, St}; handle_call({unregister_all, Pid}, _From, St) -> - unregister_pattern(#client{pid=Pid, _='_'}), + ets:match_delete(?REGISTRY_TABLE, pattern(Pid)), + drop_monitor(Pid), {reply, ok, St}; handle_call(Msg, From, St) -> couch_log:notice("~s ignoring call ~w from ~w", [?MODULE, Msg, From]), - {reply, ignored, St, 0}. + {reply, ignored, St}. handle_cast(Msg, St) -> couch_log:notice("~s ignoring cast ~w", [?MODULE, Msg]), - {noreply, St, 0}. + {noreply, St}. handle_info({'DOWN', _Ref, process, Pid, _Reason}, St) -> - unregister_pattern(#client{pid=Pid, _='_'}), - {noreply, St}; + ets:match_delete(?REGISTRY_TABLE, pattern(Pid)), + ets:delete(?REGISTRY_TABLE, Pid), + {norepy, St}; handle_info(Msg, St) -> couch_log:notice("~s ignoring info ~w", [?MODULE, Msg]), - {noreply, St, 0}. + {noreply, St}. code_change(_OldVsn, St, _Extra) -> {ok, St}. -unregister_pattern(Pattern) -> - Clients = ets:match_object(?REGISTRY_TABLE, Pattern), - Refs = lists:foldl(fun(#client{pid=Pid}=Cli, Acc) -> - ets:delete_object(?REGISTRY_TABLE, Cli), - case ets:lookup(?MONITOR_TABLE, Pid) of - [{Pid, Ref}] -> - ets:delete(?MONITOR_TABLE, Pid), - [Ref | Acc]; - [] -> - Acc - end - end, [], Clients), - lists:foreach(fun(Ref) -> - erlang:demonitor(Ref, [flush]) - end, Refs). +maybe_drop_monitor(Pid) -> + case ets:select_count(?REGISTRY_TABLE, mspec(Pid)) of + 0 -> drop_monitor(Pid); + _ -> ok + end. + + +drop_monitor(Pid) -> + case ets:lookup(?MONITOR_TABLE, Pid) of + [{Pid, Ref}] -> + ets:delete(?MONITOR_TABLE, Pid), + erlang:demonitor(Ref, [flush]); + [] -> + ok + end. + + +-compile({inline, [ + mspec/1, + pattern/1, + pattern/2 +]}). + + +mspec(Pid) -> + [{pattern(Pid), [], ['$_']}]. + + +pattern(Pid) -> + #client{pid=Pid, _='_'}. + + +pattern(DbName, Pid) -> + #client{dbname=DbName, pid=Pid, _='_'}.