couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bbast...@apache.org
Subject [3/3] couch-index commit: updated refs/heads/8409-view-cache to 5510d33
Date Mon, 27 Feb 2017 22:05:42 GMT
Port monitor tracking from couchdb-couch


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch-index/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch-index/commit/5510d33c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch-index/tree/5510d33c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch-index/diff/5510d33c

Branch: refs/heads/8409-view-cache
Commit: 5510d33c061ea426252260c58a4db21d75356b1c
Parents: 55e8d84
Author: Benjamin Bastian <benjamin.bastian@gmail.com>
Authored: Mon Feb 27 14:05:34 2017 -0800
Committer: Benjamin Bastian <benjamin.bastian@gmail.com>
Committed: Mon Feb 27 14:05:34 2017 -0800

----------------------------------------------------------------------
 src/couch_index_monitor.erl | 191 +++++++++++++++++++++++++++++++++++
 src/couch_index_server.erl  | 209 ++++++++++++++++++++++++++-------------
 2 files changed, 332 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch-index/blob/5510d33c/src/couch_index_monitor.erl
----------------------------------------------------------------------
diff --git a/src/couch_index_monitor.erl b/src/couch_index_monitor.erl
new file mode 100644
index 0000000..ed89661
--- /dev/null
+++ b/src/couch_index_monitor.erl
@@ -0,0 +1,191 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_index_monitor).
+
+
+-export([
+    spawn_link/2,
+    close/1,
+    set_pid/2,
+
+    notify/1,
+    notify/2,
+    cancel/2
+]).
+
+-export([
+    init/2
+]).
+
+-record(st, {
+    name,
+    type,
+    is_sys,
+    ref,
+    client_refs,
+    closing
+}).
+
+
+-define(BY_COUNTERS, couchdb_indexes_by_counters).
+-define(BY_IDLE, couchdb_indexes_by_idle).
+
+
+spawn_link(Name, IsSys) ->
+    erlang:spawn_link(?MODULE, init, [Name, IsSys]).
+
+
+close(Monitor) ->
+    Monitor ! exit,
+    ok.
+
+
+set_pid(Monitor, Pid) ->
+    Monitor ! {set_pid, Pid},
+    ok.
+
+
+notify(Monitor) ->
+    notify(Monitor, self()).
+
+
+notify(Monitor, Client) when is_pid(Client) ->
+    Monitor ! {notify, Client},
+    ok;
+
+notify(Monitor, {Client, _}) when is_pid(Client) ->
+    notify(Monitor, Client).
+
+
+cancel(Name, {Client, Monitor, IsSys})
+        when Client == self(), is_pid(Monitor) ->
+    Monitor ! {cancel, self()},
+    case (catch ets:update_counter(?BY_COUNTERS, Name, -1)) of
+        0 when not IsSys ->
+            true = ets:insert(?BY_IDLE, Name),
+            ok;
+        _ ->
+            ok
+    end;
+
+% This happens if a #db{} record is shared across processes
+% like fabric does with fabric_util:get_db/2
+cancel(_Name, {Client, Monitor, _})
+        when is_pid(Client), is_pid(Monitor) ->
+    ok.
+
+
+init(Name, IsSys) ->
+    {ok, CRefs} = khash:new(),
+    loop(#st{
+        name = Name,
+        is_sys = IsSys,
+        ref = undefined,
+        client_refs = CRefs,
+        closing = false
+    }).
+
+
+
+handle_info(exit, St) ->
+    {stop, normal, St};
+
+handle_info({set_pid, Pid}, #st{ref = undefined} = St) ->
+    Ref = erlang:monitor(process, Pid),
+    {noreply, St#st{ref = Ref}};
+
+handle_info({set_pid, Pid}, #st{ref = Ref} = St) when is_reference(Ref) ->
+    erlang:demonitor(Ref, [flush]),
+    handle_info({set_pid, Pid}, St#st{ref = undefined});
+
+handle_info({notify, Client}, St) when is_pid(Client) ->
+    case khash:get(St#st.client_refs, Client) of
+        {Ref, Count} when is_reference(Ref), is_integer(Count), Count > 0 ->
+            khash:put(St#st.client_refs, Client, {Ref, Count + 1});
+        undefined ->
+            Ref = erlang:monitor(process, Client),
+            case khash:size(St#st.client_refs) of
+                0 ->
+                    % Our first monitor after being idle
+                    khash:put(St#st.client_refs, Client, {Ref, 1}),
+                    true = ets:delete(?BY_IDLE, St#st.name);
+                N when is_integer(N), N > 0 ->
+                    % Still not idle
+                    khash:put(St#st.client_refs, Client, {Ref, 1}),
+                    ok
+            end
+    end,
+    {noreply, St};
+
+handle_info({cancel, Client}, St) when is_pid(Client) ->
+    case khash:get(St#st.client_refs, Client) of
+        {Ref, 1} when is_reference(Ref) ->
+            erlang:demonitor(Ref, [flush]),
+            khash:del(St#st.client_refs, Client),
+            maybe_set_idle(St);
+        {Ref, Count} when is_reference(Ref), is_integer(Count), Count > 1 ->
+            khash:put(St#st.client_refs, Client, {Ref, Count - 1})
+    end,
+    {noreply, St};
+
+handle_info({'DOWN', Ref, process, _, _}, #st{ref = Ref} = St) ->
+    {stop, normal, St};
+
+handle_info({'DOWN', _Ref, process, Pid, _Reason}, St) ->
+    #st{name=Name} = St,
+    case khash:get(St#st.client_refs, Pid) of
+        {Ref, N} when is_reference(Ref), is_integer(N), N > 0 ->
+            ets:update_counter(?BY_COUNTERS, Name, -N),
+            khash:del(St#st.client_refs, Pid),
+            maybe_set_idle(St);
+        undefined ->
+            % Ignore unknown processes
+            ok
+    end,
+    {noreply, St};
+
+handle_info(Msg, St) ->
+    {stop, {bad_info, Msg}, St}.
+
+
+maybe_set_idle(St) ->
+    case khash:size(St#st.client_refs) of
+        0 when St#st.is_sys ->
+            % System dbs don't go idle so they're
+            % never a candidate to get closed
+            ok;
+        0 ->
+            % We're now idle
+            ets:insert(?BY_IDLE, {St#st.name});
+        N when is_integer(N), N > 0 ->
+            % We have other clients
+            ok
+    end.
+
+
+loop(St) ->
+    receive
+        Other ->
+            do_handle_info(Other, St)
+    end.
+
+
+do_handle_info(Msg, St) ->
+    try handle_info(Msg, St) of
+        {noreply, NewSt} ->
+            loop(NewSt);
+        {stop, Reason, _NewSt} ->
+            exit(Reason)
+    catch T:R ->
+        exit({T, R})
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch-index/blob/5510d33c/src/couch_index_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_index_server.erl b/src/couch_index_server.erl
index e93147e..2c5f9d9 100644
--- a/src/couch_index_server.erl
+++ b/src/couch_index_server.erl
@@ -13,7 +13,6 @@
 -module(couch_index_server).
 -behaviour(gen_server).
 -behaviour(config_listener).
--behaviour(couch_monitor_server).
 
 -vsn(2).
 
@@ -29,20 +28,21 @@
     handle_db_event/3
 ]).
 
-% Exported for couch_monitor_server behavior
--export([close/2, tab_name/1]).
-
 -include_lib("couch/include/couch_db.hrl").
 
 -define(BY_SIG, couchdb_indexes_by_sig).
+-define(BY_COUNTERS, couchdb_indexes_by_counters).
 -define(BY_PID, couchdb_indexes_by_pid).
+-define(BY_IDLE, couchdb_indexes_by_idle).
 -define(BY_DB, couchdb_indexes_by_db).
 -define(RELISTEN_DELAY, 5000).
 -define(MAX_INDEXES_OPEN, 50).
 
 -record(st, {
     root_dir,
-    monitor_state
+    max_indexes,
+    open,
+    sys_open
 }).
 
 start_link() ->
@@ -102,8 +102,12 @@ get_index(Module, Db, DDoc, Fun) when is_binary(DDoc) ->
 get_index(Module, Db, DDoc, Fun) when is_function(Fun, 1) ->
     {ok, InitState} = Module:init(Db, DDoc),
     {ok, FunResp} = Fun(InitState),
-    {ok, Pid} = get_index_int(Module, InitState, couch_db:is_system_db(Db)),
-    {ok, Pid, FunResp};
+    case get_index_int(Module, InitState, couch_db:is_system_db(Db)) of
+        {ok, Pid} ->
+            {ok, Pid, FunResp};
+        {error, all_active} ->
+            {error, all_active}
+    end;
 get_index(Module, Db, DDoc, _Fun) ->
     {ok, InitState} = Module:init(Db, DDoc),
     get_index_int(Module, InitState, couch_db:is_system_db(Db)).
@@ -113,12 +117,13 @@ get_index_int(Module, IdxState, SysOwned) ->
     DbName = Module:get(db_name, IdxState),
     Sig = Module:get(signature, IdxState),
     Args = {Module, IdxState, DbName, Sig, SysOwned},
-    case couch_monitor_server:incref(?MODULE, {DbName, Sig}) of
+    case incref({DbName, Sig}) of
         ok ->
-            {ok, {Pid, Monitor, _SysOwned}} = couch_monitor_server:lookup(?MODULE, name,
{DbName, Sig}),
-            ok = couch_monitor:notify(Monitor),
+            [{_, {Pid, Monitor, _SysOwned}}] = ets:lookup(?BY_SIG, {DbName, Sig}),
+            ok = couch_index_monitor:notify(Monitor),
             {ok, Pid};
         _ ->
+            couch_log:error("CALLING~n", []),
             gen_server:call(?MODULE, {get_index, Args}, infinity)
     end.
 
@@ -126,74 +131,118 @@ get_index_int(Module, IdxState, SysOwned) ->
 init([]) ->
     process_flag(trap_exit, true),
     ok = config:listen_for_changes(?MODULE, couch_index_util:root_dir()),
-    ets:new(?BY_DB, [protected, bag, named_table]),
+    ets:new(?BY_DB, [public, bag, named_table]),
+    ets:new(?BY_SIG, [public, set, named_table]),
+    ets:new(?BY_PID, [public, set, named_table]),
+    ets:new(?BY_COUNTERS, [set, public, named_table]),
+    ets:new(?BY_IDLE, [set, public, named_table]),
     couch_event:link_listener(?MODULE, handle_db_event, nil, [all_dbs]),
     RootDir = couch_index_util:root_dir(),
     couch_file:init_delete_dir(RootDir),
-    MaxIndexesOpen = list_to_integer(config:get("couchdb", "max_indexes_open", integer_to_list(?MAX_INDEXES_OPEN))),
-    MonState = couch_monitor_server:new(?MODULE, MaxIndexesOpen),
-    {ok, #st{root_dir=RootDir, monitor_state=MonState}}.
+    MaxIndexes = list_to_integer(
+        config:get("couchdb", "max_indexes_open", integer_to_list(?MAX_INDEXES_OPEN))),
+    {ok, #st{root_dir=RootDir, max_indexes=MaxIndexes, open=0, sys_open=0}}.
 
 
 terminate(_Reason, _State) ->
-    Pids = [Pid || {Pid, _} <- ets:tab2list(tab_name(pid))],
+    Pids = [Pid || {Pid, _} <- ets:tab2list(?BY_PID)],
     lists:map(fun couch_util:shutdown_sync/1, Pids),
     ok.
 
 make_room(State, false) ->
-    case couch_monitor_server:maybe_close_idle(State#st.monitor_state) of
-        {ok, NewMonState} ->
-            {ok, State#st{monitor_state=NewMonState}};
+    case maybe_close_idle(State) of
+        {ok, NewState} ->
+            {ok, NewState};
         Other ->
             Other
     end;
 make_room(State, true) ->
     {ok, State}.
 
+-spec maybe_close_idle(#st{}) -> {ok, #st{}} | {error, all_active}.
+maybe_close_idle(#st{open=Open, max_indexes=Max}=State) when Open < Max ->
+    {ok, State};
+
+maybe_close_idle(State) ->
+    try
+        {ok, close_idle(State)}
+    catch error:all_active ->
+        {error, all_active}
+    end.
+
+-spec close_idle(#st{}) -> #st{}.
+close_idle(State) ->
+    ets:safe_fixtable(?BY_IDLE, true),
+    try
+        close_idle(State, ets:first(?BY_IDLE))
+    after
+        ets:safe_fixtable(?BY_IDLE, false)
+    end.
+
+
+-spec close_idle(#st{}, term()) -> #st{}.
+close_idle(_State, '$end_of_table') ->
+    erlang:error(all_active);
+
+close_idle(State, Name) ->
+    case ets:lookup(?BY_SIG, Name) of
+        [{_, {Pid, _Monitor, SysOwned}}] ->
+            true = ets:delete(?BY_IDLE, Name),
+            couch_index:stop(Pid),
+            closed(State, SysOwned);
+        [] ->
+            true = ets:delete(?BY_IDLE, Name),
+            close_idle(State, ets:next(?BY_IDLE, Name))
+    end.
+
+
 handle_call({get_index, {_Mod, _IdxState, DbName, Sig, SysOwned}=Args}, From, State) ->
-    case couch_monitor_server:lookup(?MODULE, name, {DbName, Sig}) of
-        not_found ->
+    case ets:lookup(?BY_SIG, {DbName, Sig}) of
+        [] ->
             case make_room(State, SysOwned) of
                 {ok, NewState} ->
                     spawn_link(fun() -> new_index(Args) end),
-                    Monitor = couch_monitor:spawn_link(?MODULE, {DbName, Sig}, SysOwned),
-                    couch_monitor_server:insert(?MODULE, name, {DbName, Sig}, {[From], Monitor,
SysOwned}),
+                    Monitor = couch_index_monitor:spawn_link({DbName, Sig}, SysOwned),
+                    ets:insert(?BY_SIG, {{DbName, Sig}, {[From], Monitor, SysOwned}}),
                     {noreply, NewState};
-                {error, Reason} ->
-                    {reply, {error, Reason}, State}
+                {error, all_active} ->
+                    {reply, {error, all_active}, State}
             end;
-        {ok, {Waiters, Monitor, SysOwned}} when is_list(Waiters) ->
-            couch_monitor_server:insert(?MODULE, name, {DbName, Sig}, {[From | Waiters],
Monitor, SysOwned}),
+        [{_, {Waiters, Monitor, SysOwned}}] when is_list(Waiters) ->
+            ets:insert(?BY_SIG, {{DbName, Sig}, {[From | Waiters], Monitor, SysOwned}}),
             {noreply, State};
-        {ok, {Pid, Monitor, _SysOwned}} when is_pid(Pid) ->
-            ok = couch_monitor_server:incref(?MODULE, {DbName, Sig}),
-            ok = couch_monitor:notify(Monitor, From),
+        [{_, {Pid, Monitor, _SysOwned}}] when is_pid(Pid) ->
+            ok = incref({DbName, Sig}),
+            ok = couch_index_monitor:notify(Monitor, From),
             {reply, {ok, Pid}, State}
     end;
 handle_call({async_open, {DbName, DDocId, Sig}, {ok, Pid}}, _From, State) ->
-    {ok, {Waiters, Monitor, SysOwned}} = couch_monitor_server:lookup(?MODULE, name, {DbName,
Sig}),
+    [{_, {Waiters, Monitor, SysOwned}}] = ets:lookup(?BY_SIG, {DbName, Sig}),
     link(Pid),
-    couch_monitor_server:insert(?MODULE, name, {DbName, Sig}, {Pid, Monitor, SysOwned}),
-    couch_monitor_server:insert(?MODULE, pid, Pid, {DbName, Sig}),
-    couch_monitor_server:insert(?MODULE, counters, {DbName, Sig}, 0),
+    ets:insert(?BY_SIG, {{DbName, Sig}, {Pid, Monitor, SysOwned}}),
+    ets:insert(?BY_PID, {Pid, {DbName, Sig}}),
+    ets:insert(?BY_COUNTERS, {{DbName, Sig}, 0}),
     ets:insert(?BY_DB, {DbName, {DDocId, Sig}}),
     lists:foreach(fun(From) ->
         {Client, _} = From,
-        ok = couch_monitor_server:incref(?MODULE, {DbName, Sig}),
-        ok = couch_monitor:notify(Monitor, Client),
+        ok = incref({DbName, Sig}),
+        ok = couch_index_monitor:notify(Monitor, Client),
         gen_server:reply(From, {ok, Pid})
     end, Waiters),
-    {reply, ok, State#st{monitor_state=couch_monitor_server:opened(State#st.monitor_state,
SysOwned)}};
+    {reply, ok, opened(State, SysOwned)};
 handle_call({async_error, {DbName, _DDocId, Sig}, Error}, {FromPid, _}, State) ->
-    {ok, {Waiters, Monitor, _SO}} = couch_monitor_server:lookup(?MODULE, name, {DbName, Sig}),
+    [{_, {Waiters, Monitor, _SO}}] = ets:lookup(?BY_SIG, {DbName, Sig}),
     [gen_server:reply(From, Error) || From <- Waiters],
-    couch_monitor_server:delete(?MODULE, {DbName, Sig}, FromPid),
-    ok = couch_monitor:close(Monitor),
+    true = ets:delete(?BY_COUNTERS, {DbName, Sig}),
+    true = ets:delete(?BY_SIG, {DbName, Sig}),
+    true = ets:delete(?BY_PID, FromPid),
+    true = ets:delete(?BY_IDLE, {DbName, Sig}),
+    ok = couch_index_monitor:close(Monitor),
     {reply, ok, State};
 handle_call({reset_indexes, DbName}, _From, State) ->
     {reply, ok, reset_indexes(DbName, State)};
 handle_call(open_index_count, _From, State) ->
-    {reply, couch_monitor_server:num_open(State#st.monitor_state), State};
+    {reply, State#st.open, State};
 handle_call(get_server, _From, State) ->
     {reply, State, State}.
 
@@ -202,18 +251,19 @@ handle_cast({reset_indexes, DbName}, State) ->
     {noreply, reset_indexes(DbName, State)}.
 
 handle_info({'EXIT', Pid, Reason}, Server) ->
-    case couch_monitor_server:lookup(?MODULE, pid, Pid) of
-        {ok, {DbName, Sig}} ->
+    case ets:lookup(?BY_PID, Pid) of
+        [{_, {DbName, Sig}}] ->
+            [{_, {_W, _M, SysOwned}}] = ets:lookup(?BY_SIG, {DbName, Sig}),
             [{DbName, {DDocId, Sig}}] =
                 ets:match_object(?BY_DB, {DbName, {'$1', Sig}}),
-            rem_from_ets(DbName, Sig, DDocId, Pid);
-        not_found when Reason /= normal ->
+            rem_from_ets(DbName, Sig, DDocId, Pid),
+            {noreply, closed(Server, SysOwned)};
+        [] when Reason /= normal ->
             couch_log:error("Looked up: ~p ~p ~p", [?MODULE, pid, Pid]),
             exit(Reason);
         _Else ->
-            ok
-    end,
-    {noreply, Server};
+            {noreply, Server}
+    end;
 handle_info(restart_config_listener, State) ->
     ok = config:listen_for_changes(?MODULE, couch_index_util:root_dir()),
     {noreply, State};
@@ -247,16 +297,6 @@ handle_config_terminate(_Server, _Reason, _State) ->
     erlang:send_after(?RELISTEN_DELAY, whereis(?MODULE), restart_config_listener),
     {ok, couch_index_util:root_dir()}.
 
-% couch_monitor_server behavior implementation
-tab_name(name) -> couch_index;
-tab_name(pid) -> couch_index_by_pid;
-tab_name(counters) -> couch_index_counters;
-tab_name(idle) -> couch_index_idle.
-
-close(DbNameSig, {Pid, _Monitor, SysOwned}) ->
-    couch_log:error("Closing Index: ~p", [DbNameSig]),
-    couch_index:stop(Pid),
-    {true, SysOwned}.
 
 new_index({Mod, IdxState, DbName, Sig, _SysOwned}) ->
     DDocId = Mod:get(idx_name, IdxState),
@@ -272,25 +312,28 @@ new_index({Mod, IdxState, DbName, Sig, _SysOwned}) ->
 
 
 reset_indexes(DbName, State) ->
-    #st{root_dir=Root, monitor_state=MonState} = State,
+    #st{root_dir=Root} = State,
     % shutdown all the updaters and clear the files, the db got changed
-    Fun = fun({_, {DDocId, Sig}}, MonStateAcc) ->
-        {ok, {Pid, Monitor, SysOwned}} = couch_monitor_server:lookup(?MODULE, name, {DbName,
Sig}),
-        couch_monitor:close(Monitor),
+    Fun = fun({_, {DDocId, Sig}}, StateAcc) ->
+        [{_, {Pid, Monitor, SysOwned}}] = ets:lookup(?BY_SIG, {DbName, Sig}),
+        couch_index_monitor:close(Monitor),
         MRef = erlang:monitor(process, Pid),
         gen_server:cast(Pid, delete),
         receive {'DOWN', MRef, _, _, _} -> ok end,
         rem_from_ets(DbName, Sig, DDocId, Pid),
-        couch_monitor_server:closed(MonStateAcc, SysOwned)
+        closed(StateAcc, SysOwned)
     end,
-    NewMonState = lists:foldl(Fun, MonState, ets:lookup(?BY_DB, DbName)),
+    NewState = lists:foldl(Fun, State, ets:lookup(?BY_DB, DbName)),
     Path = couch_index_util:index_dir("", DbName),
     couch_file:nuke_dir(Root, Path),
-    State#st{monitor_state=NewMonState}.
+    NewState.
 
 
 rem_from_ets(DbName, Sig, DDocId, Pid) ->
-    couch_monitor_server:delete(?MODULE, {DbName, Sig}, Pid),
+    true = ets:delete(?BY_COUNTERS, {DbName, Sig}),
+    true = ets:delete(?BY_SIG, {DbName, Sig}),
+    true = ets:delete(?BY_PID, Pid),
+    true = ets:delete(?BY_IDLE, {DbName, Sig}),
     ets:delete_object(?BY_DB, {DbName, {DDocId, Sig}}).
 
 
@@ -302,13 +345,43 @@ handle_db_event(DbName, deleted, St) ->
     {ok, St};
 handle_db_event(DbName, {ddoc_updated, DDocId}, St) ->
     lists:foreach(fun({_DbName, {_DDocId, Sig}}) ->
-        case couch_monitor_server:lookup(?MODULE, name, {DbName, Sig}) of
-            {ok, {IndexPid, _Monitor, _SysOwned}} ->
+        case ets:lookup(?BY_SIG, {DbName, Sig}) of
+            [{_, {IndexPid, _Monitor, _SysOwned}}] ->
                 (catch gen_server:cast(IndexPid, ddoc_updated));
-            not_found ->
+            [] ->
                 ok
         end
     end, ets:match_object(?BY_DB, {DbName, {DDocId, '$1'}})),
     {ok, St};
 handle_db_event(_DbName, _Event, St) ->
     {ok, St}.
+
+
+-spec opened(#st{}, boolean()) -> #st{}.
+opened(State, IsSysOwned) ->
+    case IsSysOwned of
+        true -> State#st{sys_open=State#st.sys_open + 1};
+        false -> State#st{open=State#st.open + 1}
+    end.
+
+
+-spec closed(#st{}, boolean()) -> #st{}.
+closed(State, IsSysOwned) ->
+    case IsSysOwned of
+        true -> State#st{sys_open=State#st.sys_open - 1};
+        false -> State#st{open=State#st.open - 1}
+    end.
+
+
+incref(Name) ->
+    case (catch ets:update_counter(?BY_COUNTERS, Name, 1)) of
+        1 ->
+            ets:delete(?BY_IDLE, Name),
+            ok;
+        N when is_integer(N), N > 0 ->
+            ok;
+        N when is_integer(N) ->
+            {invalid_refcount, N};
+        {'EXIT', {badarg, _}} ->
+            missing_counter
+	end.


Mime
View raw message