summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/couchdb/couch_config.erl18
-rw-r--r--src/couchdb/couch_db.erl11
-rw-r--r--src/couchdb/couch_db_updater.erl13
-rw-r--r--src/couchdb/couch_external_server.erl4
-rw-r--r--src/couchdb/couch_file.erl19
-rw-r--r--src/couchdb/couch_native_process.erl6
-rw-r--r--src/couchdb/couch_os_process.erl1
-rw-r--r--src/couchdb/couch_query_servers.erl75
-rw-r--r--src/couchdb/couch_ref_counter.erl9
-rw-r--r--src/couchdb/couch_server.erl33
-rw-r--r--src/couchdb/couch_server_sup.erl2
-rw-r--r--src/couchdb/couch_stats_collector.erl8
-rw-r--r--src/couchdb/couch_util.erl39
-rw-r--r--src/couchdb/couch_view.erl10
-rw-r--r--src/couchdb/couch_view_group.erl4
-rwxr-xr-xtest/etap/010-file-basics.t13
-rwxr-xr-xtest/etap/040-util.t23
17 files changed, 121 insertions, 167 deletions
diff --git a/src/couchdb/couch_config.erl b/src/couchdb/couch_config.erl
index 0e0c3fcb..1fe5aa0d 100644
--- a/src/couchdb/couch_config.erl
+++ b/src/couchdb/couch_config.erl
@@ -111,7 +111,7 @@ terminate(_Reason, _State) ->
handle_call(all, _From, Config) ->
Resp = lists:sort((ets:tab2list(?MODULE))),
{reply, Resp, Config};
-handle_call({set, Sec, Key, Val, Persist}, From, Config) ->
+handle_call({set, Sec, Key, Val, Persist}, _From, Config) ->
true = ets:insert(?MODULE, {{Sec, Key}, Val}),
case {Persist, Config#config.write_filename} of
{true, undefined} ->
@@ -121,12 +121,9 @@ handle_call({set, Sec, Key, Val, Persist}, From, Config) ->
_ ->
ok
end,
- spawn_link(fun() ->
- [catch F(Sec, Key, Val, Persist) || {_Pid, F} <- Config#config.notify_funs],
- gen_server:reply(From, ok)
- end),
- {noreply, Config};
-handle_call({delete, Sec, Key, Persist}, From, Config) ->
+ [catch F(Sec, Key, Val, Persist) || {_Pid, F} <- Config#config.notify_funs],
+ {reply, ok, Config};
+handle_call({delete, Sec, Key, Persist}, _From, Config) ->
true = ets:delete(?MODULE, {Sec,Key}),
case {Persist, Config#config.write_filename} of
{true, undefined} ->
@@ -136,11 +133,8 @@ handle_call({delete, Sec, Key, Persist}, From, Config) ->
_ ->
ok
end,
- spawn_link(fun() ->
- [catch F(Sec, Key, deleted, Persist) || {_Pid, F} <- Config#config.notify_funs],
- gen_server:reply(From, ok)
- end),
- {noreply, Config};
+ [catch F(Sec, Key, deleted, Persist) || {_Pid, F} <- Config#config.notify_funs],
+ {reply, ok, Config};
handle_call({register, Fun, Pid}, _From, #config{notify_funs=PidFuns}=Config) ->
erlang:monitor(process, Pid),
% convert 1 and 2 arity to 3 arity
diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl
index bf40e6b1..e5635f55 100644
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@ -903,11 +903,10 @@ init({DbName, Filepath, Fd, Options}) ->
{ok, #db{fd_ref_counter=RefCntr}=Db} = gen_server:call(UpdaterPid, get_db),
couch_ref_counter:add(RefCntr),
couch_stats_collector:track_process_count({couchdb, open_databases}),
- process_flag(trap_exit, true),
{ok, Db}.
-terminate(_Reason, Db) ->
- couch_util:shutdown_sync(Db#db.update_pid),
+terminate(Reason, _Db) ->
+ couch_util:terminate_linked(Reason),
ok.
handle_call({open_ref_count, OpenerPid}, _, #db{fd_ref_counter=RefCntr}=Db) ->
@@ -935,11 +934,7 @@ handle_cast(Msg, Db) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-
-handle_info({'EXIT', _Pid, normal}, Db) ->
- {noreply, Db};
-handle_info({'EXIT', _Pid, Reason}, Server) ->
- {stop, Reason, Server};
+
handle_info(Msg, Db) ->
?LOG_ERROR("Bad message received for db ~s: ~p", [Db#db.name, Msg]),
exit({error, Msg}).
diff --git a/src/couchdb/couch_db_updater.erl b/src/couchdb/couch_db_updater.erl
index fdd79481..982ee03a 100644
--- a/src/couchdb/couch_db_updater.erl
+++ b/src/couchdb/couch_db_updater.erl
@@ -20,7 +20,6 @@
init({MainPid, DbName, Filepath, Fd, Options}) ->
- process_flag(trap_exit, true),
case lists:member(create, Options) of
true ->
% create a new header and writes it to the file
@@ -38,10 +37,8 @@ init({MainPid, DbName, Filepath, Fd, Options}) ->
{ok, Db2#db{main_pid=MainPid}}.
-terminate(_Reason, Db) ->
- couch_file:close(Db#db.fd),
- couch_util:shutdown_sync(Db#db.compactor_pid),
- couch_util:shutdown_sync(Db#db.fd_ref_counter),
+terminate(Reason, _Srv) ->
+ couch_util:terminate_linked(Reason),
ok.
handle_call(get_db, _From, Db) ->
@@ -217,11 +214,7 @@ handle_info(delayed_commit, Db) ->
Db2 ->
ok = gen_server:call(Db2#db.main_pid, {db_updated, Db2}),
{noreply, Db2}
- end;
-handle_info({'EXIT', _Pid, normal}, Db) ->
- {noreply, Db};
-handle_info({'EXIT', _Pid, Reason}, Db) ->
- {stop, Reason, Db}.
+ end.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
diff --git a/src/couchdb/couch_external_server.erl b/src/couchdb/couch_external_server.erl
index 045fcee9..8e495320 100644
--- a/src/couchdb/couch_external_server.erl
+++ b/src/couchdb/couch_external_server.erl
@@ -50,11 +50,9 @@ terminate(_Reason, {_Name, _Command, Pid}) ->
handle_call({execute, JsonReq}, _From, {Name, Command, Pid}) ->
{reply, couch_os_process:prompt(Pid, JsonReq), {Name, Command, Pid}}.
-handle_info({'EXIT', _Pid, normal}, State) ->
- {noreply, State};
handle_info({'EXIT', Pid, Reason}, {Name, Command, Pid}) ->
?LOG_INFO("EXTERNAL: Process for ~s exiting. (reason: ~w)", [Name, Reason]),
- {stop, Reason, {Name, Command, Pid}}.
+ {stop, normal, {Name, Command, Pid}}.
handle_cast(stop, {Name, Command, Pid}) ->
?LOG_INFO("EXTERNAL: Shutting down ~s", [Name]),
diff --git a/src/couchdb/couch_file.erl b/src/couchdb/couch_file.erl
index 4c6928a7..5904260c 100644
--- a/src/couchdb/couch_file.erl
+++ b/src/couchdb/couch_file.erl
@@ -176,21 +176,13 @@ sync(Fd) ->
gen_server:call(Fd, sync, infinity).
%%----------------------------------------------------------------------
-%% Purpose: Close the file.
+%% Purpose: Close the file. Is performed asynchronously.
%% Returns: ok
%%----------------------------------------------------------------------
close(Fd) ->
- MRef = erlang:monitor(process, Fd),
- try
- catch unlink(Fd),
- catch exit(Fd, shutdown),
- receive
- {'DOWN', MRef, _, _, _} ->
- ok
- end
- after
- erlang:demonitor(MRef, [flush])
- end.
+ Result = gen_server:cast(Fd, close),
+ catch unlink(Fd),
+ Result.
% 09 UPGRADE CODE
old_pread(Fd, Pos, Len) ->
@@ -227,7 +219,6 @@ init_status_error(ReturnPid, Ref, Error) ->
% server functions
init({Filepath, Options, ReturnPid, Ref}) ->
- process_flag(trap_exit, true),
case lists:member(create, Options) of
true ->
filelib:ensure_dir(Filepath),
@@ -441,8 +432,6 @@ handle_cast(close, Fd) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-handle_info({'EXIT', _, normal}, Fd) ->
- {noreply, Fd};
handle_info({'EXIT', _, Reason}, Fd) ->
{stop, Reason, Fd}.
diff --git a/src/couchdb/couch_native_process.erl b/src/couchdb/couch_native_process.erl
index a9869b29..5600c421 100644
--- a/src/couchdb/couch_native_process.erl
+++ b/src/couchdb/couch_native_process.erl
@@ -94,10 +94,8 @@ handle_call({prompt, Data}, _From, State) ->
{reply, Resp, NewState}
end.
-handle_cast(foo, State) -> {noreply, State}.
-handle_info({'EXIT',_,normal}, State) -> {noreply, State};
-handle_info({'EXIT',_,Reason}, State) ->
- {stop, Reason, State}.
+handle_cast(_Msg, State) -> {noreply, State}.
+handle_info(_Msg, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVersion, State, _Extra) -> {ok, State}.
diff --git a/src/couchdb/couch_os_process.erl b/src/couchdb/couch_os_process.erl
index 070b86fc..5ac13715 100644
--- a/src/couchdb/couch_os_process.erl
+++ b/src/couchdb/couch_os_process.erl
@@ -104,7 +104,6 @@ readjson(OsProc) when is_record(OsProc, os_proc) ->
% gen_server API
init([Command, Options, PortOptions]) ->
- process_flag(trap_exit, true),
PrivDir = couch_util:priv_dir(),
Spawnkiller = filename:join(PrivDir, "couchspawnkillable"),
BaseProc = #os_proc{
diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl
index 344ce8b2..3095b199 100644
--- a/src/couchdb/couch_query_servers.erl
+++ b/src/couchdb/couch_query_servers.erl
@@ -15,7 +15,7 @@
-export([start_link/0]).
--export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3]).
+-export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3,stop/0]).
-export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
-export([reduce/3, rereduce/3,validate_doc_update/5]).
-export([filter_docs/5]).
@@ -38,6 +38,9 @@
start_link() ->
gen_server:start_link({local, couch_query_servers}, couch_query_servers, [], []).
+stop() ->
+ exit(whereis(couch_query_servers), normal).
+
start_doc_map(Lang, Functions) ->
Proc = get_os_process(Lang),
lists:foreach(fun(FunctionSource) ->
@@ -216,18 +219,17 @@ init([]) ->
ok = couch_config:register(
fun("query_servers" ++ _, _) ->
- supervisor:terminate_child(couch_secondary_services, query_servers),
- supervisor:restart_child(couch_secondary_services, query_servers)
+ ?MODULE:stop()
end),
ok = couch_config:register(
fun("native_query_servers" ++ _, _) ->
- supervisor:terminate_child(couch_secondary_services, query_servers),
- [supervisor:restart_child(couch_secondary_services, query_servers)]
+ ?MODULE:stop()
end),
Langs = ets:new(couch_query_server_langs, [set, private]),
PidProcs = ets:new(couch_query_server_pid_langs, [set, private]),
LangProcs = ets:new(couch_query_server_procs, [set, private]),
+ InUse = ets:new(couch_query_server_used, [set, private]),
% 'query_servers' specifies an OS command-line to execute.
lists:foreach(fun({Lang, Command}) ->
true = ets:insert(Langs, {?l2b(Lang),
@@ -242,71 +244,75 @@ init([]) ->
process_flag(trap_exit, true),
{ok, {Langs, % Keyed by language name, value is {Mod,Func,Arg}
PidProcs, % Keyed by PID, valus is a #proc record.
- LangProcs % Keyed by language name, value is a #proc record
+ LangProcs, % Keyed by language name, value is a #proc record
+ InUse % Keyed by PID, value is #proc record.
}}.
-terminate(_Reason, {_Langs, PidProcs, _LangProcs}) ->
- [couch_util:shutdown_sync(P) || {P,_} <- ets:tab2list(PidProcs)],
+terminate(_Reason, _Server) ->
ok.
-handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, _From, {Langs, PidProcs, LangProcs}=Server) ->
+handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, _From, {Langs, PidProcs, LangProcs, InUse}=Server) ->
% Note to future self. Add max process limit.
Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
case ets:lookup(LangProcs, Lang) of
[{Lang, [P|Rest]}] ->
% find a proc in the set that has the DDoc
case proc_with_ddoc(DDoc, DDocKey, [P|Rest]) of
- {ok, Proc} ->
- rem_from_list(LangProcs, Lang, Proc),
- {reply, {ok, Proc, get_query_server_config()}, Server};
- Error ->
- {reply, Error, Server}
- end;
+ {ok, Proc} ->
+ % looks like the proc isn't getting dropped from the list.
+ % we need to change this to take a fun for equality checking
+ % so we can do a comparison on portnum
+ rem_from_list(LangProcs, Lang, Proc),
+ add_to_list(InUse, Lang, Proc),
+ {reply, {ok, Proc, get_query_server_config()}, Server};
+ Error ->
+ {reply, Error, Server}
+ end;
_ ->
case (catch new_process(Langs, Lang)) of
{ok, Proc} ->
add_value(PidProcs, Proc#proc.pid, Proc),
case proc_with_ddoc(DDoc, DDocKey, [Proc]) of
- {ok, Proc2} ->
- {reply, {ok, Proc2, get_query_server_config()}, Server};
- Error ->
- {reply, Error, Server}
- end;
+ {ok, Proc2} ->
+ rem_from_list(LangProcs, Lang, Proc),
+ add_to_list(InUse, Lang, Proc2),
+ {reply, {ok, Proc2, get_query_server_config()}, Server};
+ Error ->
+ {reply, Error, Server}
+ end;
Error ->
{reply, Error, Server}
end
end;
-handle_call({get_proc, Lang}, _From, {Langs, PidProcs, LangProcs}=Server) ->
+handle_call({get_proc, Lang}, _From, {Langs, PidProcs, LangProcs, InUse}=Server) ->
% Note to future self. Add max process limit.
case ets:lookup(LangProcs, Lang) of
[{Lang, [Proc|_]}] ->
+ add_value(PidProcs, Proc#proc.pid, Proc),
rem_from_list(LangProcs, Lang, Proc),
+ add_to_list(InUse, Lang, Proc),
{reply, {ok, Proc, get_query_server_config()}, Server};
_ ->
case (catch new_process(Langs, Lang)) of
{ok, Proc} ->
add_value(PidProcs, Proc#proc.pid, Proc),
+ add_to_list(InUse, Lang, Proc),
{reply, {ok, Proc, get_query_server_config()}, Server};
Error ->
{reply, Error, Server}
end
end;
-handle_call({unlink_proc, Pid}, _From, {_, PidProcs, _}=Server) ->
- rem_value(PidProcs, Pid),
- unlink(Pid),
- {reply, ok, Server};
-handle_call({ret_proc, Proc}, _From, {_, PidProcs, LangProcs}=Server) ->
+handle_call({ret_proc, Proc}, _From, {_, _, LangProcs, InUse}=Server) ->
% Along with max process limit, here we should check
% if we're over the limit and discard when we are.
- add_value(PidProcs, Proc#proc.pid, Proc),
add_to_list(LangProcs, Proc#proc.lang, Proc),
- link(Proc#proc.pid),
+ rem_from_list(InUse, Proc#proc.lang, Proc),
{reply, true, Server}.
handle_cast(_Whatever, Server) ->
{noreply, Server}.
-handle_info({'EXIT', Pid, Status}, {_, PidProcs, LangProcs}=Server) ->
+handle_info({'EXIT', Pid, Status}, {_, PidProcs, LangProcs, InUse}=Server) ->
case ets:lookup(PidProcs, Pid) of
[{Pid, Proc}] ->
case Status of
@@ -315,14 +321,11 @@ handle_info({'EXIT', Pid, Status}, {_, PidProcs, LangProcs}=Server) ->
end,
rem_value(PidProcs, Pid),
catch rem_from_list(LangProcs, Proc#proc.lang, Proc),
+ catch rem_from_list(InUse, Proc#proc.lang, Proc),
{noreply, Server};
[] ->
- case Status of
- normal ->
- {noreply, Server};
- _ ->
- {stop, Status, Server}
- end
+ ?LOG_DEBUG("Unknown linked process died: ~p (reason: ~p)", [Pid, Status]),
+ {stop, Status, Server}
end.
code_change(_OldVsn, State, _Extra) ->
@@ -399,7 +402,6 @@ get_ddoc_process(#doc{} = DDoc, DDocKey) ->
proc_set_timeout(Proc, list_to_integer(couch_config:get(
"couchdb", "os_process_timeout", "5000"))),
link(Proc#proc.pid),
- gen_server:call(couch_query_servers, {unlink_proc, Proc#proc.pid}),
Proc;
_ ->
catch proc_stop(Proc),
@@ -417,7 +419,6 @@ get_os_process(Lang) ->
proc_set_timeout(Proc, list_to_integer(couch_config:get(
"couchdb", "os_process_timeout", "5000"))),
link(Proc#proc.pid),
- gen_server:call(couch_query_servers, {unlink_proc, Proc#proc.pid}),
Proc;
_ ->
catch proc_stop(Proc),
diff --git a/src/couchdb/couch_ref_counter.erl b/src/couchdb/couch_ref_counter.erl
index 96d92333..5c8e7437 100644
--- a/src/couchdb/couch_ref_counter.erl
+++ b/src/couchdb/couch_ref_counter.erl
@@ -40,18 +40,17 @@ count(RefCounterPid) ->
-record(srv,
{
- referrers=dict:new(), % a dict of each ref counting proc.
- child_procs=[]
+ referrers=dict:new() % a dict of each ref counting proc.
}).
init({Pid, ChildProcs}) ->
[link(ChildProc) || ChildProc <- ChildProcs],
Referrers = dict:from_list([{Pid, {erlang:monitor(process, Pid), 1}}]),
- {ok, #srv{referrers=Referrers, child_procs=ChildProcs}}.
+ {ok, #srv{referrers=Referrers}}.
-terminate(_Reason, #srv{child_procs=ChildProcs}) ->
- [couch_util:shutdown_sync(Pid) || Pid <- ChildProcs],
+terminate(Reason, _Srv) ->
+ couch_util:terminate_linked(Reason),
ok.
diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl
index 61794dea..325d4a12 100644
--- a/src/couchdb/couch_server.erl
+++ b/src/couchdb/couch_server.erl
@@ -147,9 +147,8 @@ init([]) ->
max_dbs_open=MaxDbsOpen,
start_time=httpd_util:rfc1123_date()}}.
-terminate(_Reason, _Srv) ->
- [couch_util:shutdown_sync(Pid) || {_, {Pid, _LruTime}} <-
- ets:tab2list(couch_dbs_by_name)],
+terminate(Reason, _Srv) ->
+ couch_util:terminate_linked(Reason),
ok.
all_databases() ->
@@ -188,7 +187,8 @@ try_close_lru(StartTime) ->
[{_, {MainPid, LruTime}}] = ets:lookup(couch_dbs_by_name, DbName),
case couch_db:is_idle(MainPid) of
true ->
- couch_util:shutdown_sync(MainPid),
+ exit(MainPid, kill),
+ receive {'EXIT', MainPid, _Reason} -> ok end,
true = ets:delete(couch_dbs_by_lru, LruTime),
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, MainPid),
@@ -284,7 +284,8 @@ handle_call({delete, DbName, _Options}, _From, Server) ->
case ets:lookup(couch_dbs_by_name, DbName) of
[] -> Server;
[{_, {Pid, LruTime}}] ->
- couch_util:shutdown_sync(Pid),
+ exit(Pid, kill),
+ receive {'EXIT', Pid, _Reason} -> ok end,
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, Pid),
true = ets:delete(couch_dbs_by_lru, LruTime),
@@ -314,18 +315,14 @@ handle_cast(Msg, _Server) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-handle_info({'EXIT', _Pid, config_change}, Server) ->
- {stop, shutdown, Server};
-handle_info({'EXIT', Pid, Reason}, #server{dbs_open=DbsOpen}=Server) ->
- case ets:lookup(couch_dbs_by_pid, Pid) of
- [{Pid, DbName}] ->
- [{DbName, {Pid, LruTime}}] = ets:lookup(couch_dbs_by_name, DbName),
- true = ets:delete(couch_dbs_by_pid, Pid),
- true = ets:delete(couch_dbs_by_name, DbName),
- true = ets:delete(couch_dbs_by_lru, LruTime),
- {noreply, Server#server{dbs_open=DbsOpen - 1}};
- _ ->
- {stop,Reason,Server}
- end;
+handle_info({'EXIT', _Pid, config_change}, _Server) ->
+ exit(kill);
+handle_info({'EXIT', Pid, _Reason}, #server{dbs_open=DbsOpen}=Server) ->
+ [{Pid, DbName}] = ets:lookup(couch_dbs_by_pid, Pid),
+ [{DbName, {Pid, LruTime}}] = ets:lookup(couch_dbs_by_name, DbName),
+ true = ets:delete(couch_dbs_by_pid, Pid),
+ true = ets:delete(couch_dbs_by_name, DbName),
+ true = ets:delete(couch_dbs_by_lru, LruTime),
+ {noreply, Server#server{dbs_open=DbsOpen - 1}};
handle_info(Info, _Server) ->
exit({unknown_message, Info}).
diff --git a/src/couchdb/couch_server_sup.erl b/src/couchdb/couch_server_sup.erl
index f1f8f692..d89e987d 100644
--- a/src/couchdb/couch_server_sup.erl
+++ b/src/couchdb/couch_server_sup.erl
@@ -174,7 +174,7 @@ start_secondary_services() ->
{list_to_atom(Name),
{Module, Fun, Args},
permanent,
- 1000,
+ brutal_kill,
worker,
[Module]}
end
diff --git a/src/couchdb/couch_stats_collector.erl b/src/couchdb/couch_stats_collector.erl
index f7b9bb48..59d62a6e 100644
--- a/src/couchdb/couch_stats_collector.erl
+++ b/src/couchdb/couch_stats_collector.erl
@@ -60,7 +60,7 @@ increment(Key) ->
Key2 = make_key(Key),
case catch ets:update_counter(?HIT_TABLE, Key2, 1) of
{'EXIT', {badarg, _}} ->
- catch ets:insert(?HIT_TABLE, {Key2, 1}),
+ true = ets:insert(?HIT_TABLE, {Key2, 1}),
ok;
_ ->
ok
@@ -70,16 +70,16 @@ decrement(Key) ->
Key2 = make_key(Key),
case catch ets:update_counter(?HIT_TABLE, Key2, -1) of
{'EXIT', {badarg, _}} ->
- catch ets:insert(?HIT_TABLE, {Key2, -1}),
+ true = ets:insert(?HIT_TABLE, {Key2, -1}),
ok;
_ -> ok
end.
record(Key, Value) ->
- catch ets:insert(?ABS_TABLE, {make_key(Key), Value}).
+ true = ets:insert(?ABS_TABLE, {make_key(Key), Value}).
clear(Key) ->
- catch ets:delete(?ABS_TABLE, make_key(Key)).
+ true = ets:delete(?ABS_TABLE, make_key(Key)).
track_process_count(Stat) ->
track_process_count(self(), Stat).
diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl
index a9831fde..ca36a6db 100644
--- a/src/couchdb/couch_util.erl
+++ b/src/couchdb/couch_util.erl
@@ -12,7 +12,7 @@
-module(couch_util).
--export([priv_dir/0, start_driver/1]).
+-export([priv_dir/0, start_driver/1,terminate_linked/1]).
-export([should_flush/0, should_flush/1, to_existing_atom/1]).
-export([rand32/0, implode/2, collate/2, collate/3]).
-export([abs_pathname/1,abs_pathname/2, trim/1, ascii_lower/1]).
@@ -21,7 +21,7 @@
-export([file_read_size/1, get_nested_json_value/2, json_user_ctx/1]).
-export([to_binary/1, to_integer/1, to_list/1, url_encode/1]).
-export([json_encode/1, json_decode/1]).
--export([verify/2,simple_call/2,shutdown_sync/1]).
+-export([verify/2]).
-export([compressible_att_type/1]).
-include("couch_db.hrl").
@@ -59,35 +59,14 @@ to_existing_atom(V) when is_binary(V) ->
to_existing_atom(V) when is_atom(V) ->
V.
-shutdown_sync(Pid) when not is_pid(Pid)->
- ok;
-shutdown_sync(Pid) ->
- MRef = erlang:monitor(process, Pid),
- try
- catch unlink(Pid),
- catch exit(Pid, shutdown),
- receive
- {'DOWN', MRef, _, _, _} ->
- ok
- end
- after
- erlang:demonitor(MRef, [flush])
- end.
-
-simple_call(Pid, Message) ->
- MRef = erlang:monitor(process, Pid),
- try
- Pid ! {self(), Message},
- receive
- {Pid, Result} ->
- Result;
- {'DOWN', MRef, _, _, Reason} ->
- exit(Reason)
- end
- after
- erlang:demonitor(MRef, [flush])
- end.
+terminate_linked(normal) ->
+ terminate_linked(shutdown);
+terminate_linked(Reason) ->
+ {links, Links} = process_info(self(), links),
+ [catch exit(Pid, Reason) || Pid <- Links],
+ ok.
+
to_hex([]) ->
[];
diff --git a/src/couchdb/couch_view.erl b/src/couchdb/couch_view.erl
index 234460ce..cbbbd4ac 100644
--- a/src/couchdb/couch_view.erl
+++ b/src/couchdb/couch_view.erl
@@ -269,9 +269,8 @@ init([]) ->
{ok, #server{root_dir=RootDir}}.
-terminate(_Reason, _Srv) ->
- [couch_util:shutdown_sync(Pid) || {Pid, _} <-
- ets:tab2list(couch_groups_by_updater)],
+terminate(Reason, _Srv) ->
+ couch_util:terminate_linked(Reason),
ok.
@@ -312,7 +311,10 @@ do_reset_indexes(DbName, Root) ->
fun({_DbName, Sig}) ->
?LOG_DEBUG("Killing update process for view group ~s. in database ~s.", [Sig, DbName]),
[{_, Pid}] = ets:lookup(group_servers_by_sig, {DbName, Sig}),
- couch_util:shutdown_sync(Pid)
+ exit(Pid, kill),
+ receive {'EXIT', Pid, _} ->
+ delete_from_ets(Pid, DbName, Sig)
+ end
end, Names),
delete_index_dir(Root, DbName),
file:delete(Root ++ "/." ++ ?b2l(DbName) ++ "_temp").
diff --git a/src/couchdb/couch_view_group.erl b/src/couchdb/couch_view_group.erl
index 68966a88..bb4102c2 100644
--- a/src/couchdb/couch_view_group.erl
+++ b/src/couchdb/couch_view_group.erl
@@ -341,8 +341,8 @@ handle_info({'DOWN',_,_,_,_}, State) ->
terminate(Reason, #group_state{updater_pid=Update, compactor_pid=Compact}=S) ->
reply_all(S, Reason),
- couch_util:shutdown_sync(Update),
- couch_util:shutdown_sync(Compact),
+ catch exit(Update, Reason),
+ catch exit(Compact, Reason),
ok.
code_change(_OldVsn, State, _Extra) ->
diff --git a/test/etap/010-file-basics.t b/test/etap/010-file-basics.t
index 595d87e9..09b2f2b1 100755
--- a/test/etap/010-file-basics.t
+++ b/test/etap/010-file-basics.t
@@ -67,19 +67,6 @@ test() ->
{ok, BinPos} = couch_file:append_binary(Fd, <<131,100,0,3,102,111,111>>),
etap:is({ok, foo}, couch_file:pread_term(Fd, BinPos),
"Reading a term from a written binary term representation succeeds."),
-
- BigBin = list_to_binary(lists:duplicate(100000, 0)),
- {ok, BigBinPos} = couch_file:append_binary(Fd, BigBin),
- etap:is({ok, BigBin}, couch_file:pread_binary(Fd, BigBinPos),
- "Reading a large term from a written representation succeeds."),
-
- ok = couch_file:write_header(Fd, hello),
- etap:is({ok, hello}, couch_file:read_header(Fd),
- "Reading a header succeeds."),
-
- {ok, BigBinPos2} = couch_file:append_binary(Fd, BigBin),
- etap:is({ok, BigBin}, couch_file:pread_binary(Fd, BigBinPos2),
- "Reading a large term from a written representation succeeds 2."),
% append_binary == append_iolist?
% Possible bug in pread_iolist or iolist() -> append_binary
diff --git a/test/etap/040-util.t b/test/etap/040-util.t
index 9c8f3d5a..4500d38a 100755
--- a/test/etap/040-util.t
+++ b/test/etap/040-util.t
@@ -35,6 +35,29 @@ test() ->
etap:is(foobarbaz, couch_util:to_existing_atom("foobarbaz"),
"A list of atoms is one munged atom."),
+ % terminate_linked
+ Self = self(),
+
+ spawn(fun() ->
+ SecondSelf = self(),
+ ChildPid = spawn_link(fun() ->
+ SecondSelf ! {child, started},
+ receive shutdown -> ok end
+ end),
+ PidUp = receive
+ {child, started} -> ok
+ after 1000 ->
+ {error, timeout}
+ end,
+ etap:is(ok, PidUp, "Started a linked process."),
+ couch_util:terminate_linked(normal),
+ Self ! {pid, ChildPid}
+ end),
+ receive
+ {pid, Pid} ->
+ etap:ok(not is_process_alive(Pid), "Linked process was killed.")
+ end,
+
% implode
etap:is([1, 38, 2, 38, 3], couch_util:implode([1,2,3],"&"),
"use & as separator in list."),