1%%--------------------------------------------------------------------
2%%
3%% %CopyrightBegin%
4%%
5%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
6%%
7%% Licensed under the Apache License, Version 2.0 (the "License");
8%% you may not use this file except in compliance with the License.
9%% You may obtain a copy of the License at
10%%
11%%     http://www.apache.org/licenses/LICENSE-2.0
12%%
13%% Unless required by applicable law or agreed to in writing, software
14%% distributed under the License is distributed on an "AS IS" BASIS,
15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16%% See the License for the specific language governing permissions and
17%% limitations under the License.
18%%
19%% %CopyrightEnd%
20%%
21%%
22%%----------------------------------------------------------------------
23%% File    : ETraP_Server_impl.erl
24%% Purpose :
25%%----------------------------------------------------------------------
26%% GENERAL CODE COMMENTS:
27%% ######################
28%% TypeChecking incoming arguments:
29%% --------------------------------
30%% We allow the user to configure the system so that external calls
31%% (not CosTransactions calls) may be typechecked or not when calling
32%% for example 'replay_completion'. With typecheck the user will get
33%% instant feedback. But since 'is_a' add quiet a lot extra overhead
34%% if the object is located on a remote ORB. Hence, it is up to the
35%% user to decide; speed vs. "safety".
36%%
37%% Log behavior
38%% ------------
39%% Log files are created in the current directory, which is why the
40%% application requires read/write rights for current directory. The
41%% file name looks like:
42%% "oe_nonode@nohost_subc_1429872479809947099_438" (the two last parts are
43%% erlang:system_time() and erlang:unique_integer([positive]))
44%% It is equal to what the object is started as, i.e., {regname, {global, X}}.
45%%
46%% If the application is unable to read the log it will exit and the
47%% supervisor definitions (found in ETraP_Common.hrl) determines how
48%% many times we will retry. If it's impossible to read the log it's
49%% considered as a disaster, i.e., user intervention is needed.
50%%
51%% If an Object is unreachable when a Coordinator is trying to inform
52%% of the true outcome of the transaction the application will retry N
53%% times with T seconds wait in between. If it's still impossible to
54%% reach the object it's considered as a disaster, i.e., user
55%% intervention is needed.
56%%
57%%----------------------------------------------------------------------
58
59-module('ETraP_Server_impl').
60
61%%--------------- INCLUDES -----------------------------------
62-include_lib("orber/include/corba.hrl").
63
64%% Local
65-include_lib("cosTransactions/src/ETraP_Common.hrl").
66-include_lib("cosTransactions/include/CosTransactions.hrl").
67
68
69%%--------------- IMPORTS-------------------------------------
70-import('ETraP_Common', [try_timeout/1]).
71
72%%--------------- EXPORTS-------------------------------------
73%%--------------- Inherit from CosTransactions::Resource ----
74-export([prepare/2,
75	 rollback/2,
76	 commit/2,
77	 commit_one_phase/2,
78	 forget/2]).
79
80%%--------------- Inherit from CosTransactions::Control -----
81-export([get_terminator/2,
82	 get_coordinator/2]).
83
84%%----- Inherit from CosTransactions::RecoveryCoordinator ---
85-export([replay_completion/3]).
86
87%%--------------- Inherit from CosTransactions::Coordinator -
88-export([create_subtransaction/2,
89	 get_txcontext/2,
90	 get_transaction_name/2,
91	 get_parent_status/2,
92	 get_status/2,
93	 get_top_level_status/2,
94	 hash_top_level_tran/2,
95	 hash_transaction/2,
96	 is_ancestor_transaction/3,
97	 is_descendant_transaction/3,
98	 is_related_transaction/3,
99	 is_same_transaction/3,
100	 is_top_level_transaction/2,
101	 register_resource/3,
102	 register_subtran_aware/3,
103	 register_synchronization/3,
104	 rollback_only/2]).
105
106%%--------- Inherit from CosTransactions::Synchronization ---
107%-export([before_completion/2,
108%	 after_completion/3]).
109
110
111%%--------------- gen_server specific ------------------------
112-export([init/1, terminate/2]).
113-export([handle_call/3, handle_cast/2, handle_info/2, code_change/3]).
114
115
116
117%%--------------- LOCAL DATA ---------------------------------
118-record(exc,
119	{rollback   = false,
120	 mixed      = false,
121	 hazard     = false,
122	 unprepared = false,
123	 commit     = false}).
124
125%%--------------- LOCAL DEFINITIONS --------------------------
126
127%%--------------- MISC MACROS --------------------------------
128-define(etr_log(Log, Data),    etrap_logmgr:log_safe(Log, Data)).
129-define(etr_read(Log, Cursor), etrap_logmgr:get_next(Log, Cursor)).
130
131-record(coord,
132	{status,           %% Status of the transaction.
133	 members = [],     %% List of registred resources.
134	 votedCommit = [], %% List of the ones that voted commit.
135	 raisedHeuristic = [], %% The members which raised an Heur. exc.
136	 subAw = [],       %% Resorces which want to be informed  of outcome.
137	 sync = [],
138	 exc = void,
139	 self,
140	 etsR}).
141
142%% Selectors
143-define(etr_get_status(L),     L#coord.status).
144-define(etr_get_members(L),    lists:reverse(L#coord.members)).
145-define(etr_get_vc(L),         lists:reverse(L#coord.votedCommit)).
146-define(etr_get_raisedH(L),    lists:reverse(L#coord.raisedHeuristic)).
147-define(etr_get_exc(L),        L#coord.exc).
148-define(etr_get_subAw(L),      lists:reverse(L#coord.subAw)).
149-define(etr_get_sync(L),       lists:reverse(L#coord.sync)).
150-define(etr_get_self(L),       L#coord.self).
151-define(etr_get_etsR(L),       L#coord.etsR).
152-define(etr_get_init(Env),     #coord{}).
153-define(etr_get_exc_init(),    #exc{}).
154%% Modifiers
155-define(etr_set_status(L, D),  L#coord{status = D}).
156-define(etr_set_members(L, D), L#coord{members = D}).
157-define(etr_add_member(L, D),  L#coord{members = [D|L#coord.members]}).
158-define(etr_set_vc(L, D),      L#coord{votedCommit = D}).
159-define(etr_add_vc(L, D),      L#coord{votedCommit = [D|L#coord.votedCommit]}).
160-define(etr_remove_vc(L, D),   L#coord{votedCommit =
161				       lists:delete(D, ?etr_get_vc(L))}).
162-define(etr_set_raisedH(L, D), L#coord{raisedHeuristic = [D]}).
163-define(etr_add_raisedH(L, D), L#coord{raisedHeuristic =
164				       [D|L#coord.raisedHeuristic]}).
165-define(etr_remove_raisedH(L, D), L#coord{raisedHeuristic =
166					  lists:delete(D, ?etr_get_raisedH(L))}).
167-define(etr_set_exc(L, D),     L#coord{exc = D}).
168-define(etr_set_subAw(L, D),   L#coord{subAw = [D]}).
169-define(etr_add_subAw(L, D),   L#coord{subAw = [D|L#coord.subAw]}).
170-define(etr_remove_subAw(L, D), L#coord{subAw =
171					lists:delete(D,?etr_get_subAw(L))}).
172-define(etr_set_sync(L, D),    L#coord{sync = [D]}).
173-define(etr_add_sync(L, D),    L#coord{sync = [D|L#coord.sync]}).
174-define(etr_remove_sync(L, D), L#coord{sync = lists:delete(D,?etr_get_sync(L))}).
175-define(etr_set_self(L, D),    L#coord{self = D}).
176-define(etr_set_etsR(L, D),    L#coord{etsR = D}).
177
178
179%%------------------------------------------------------------
180%% function : init, terminate
181%% Arguments:
182%% Returns  :
183%% Effect   : Functions demanded by the module ic.
184%%------------------------------------------------------------
185
186init(Env) ->
187    process_flag(trap_exit,true),
188    case catch start_object(Env) of
189	{'EXIT', Reason} ->
190	    %% Happens when, for example, we encounter an
191	    %% error when reading from the log file.
192	    {stop, Reason};
193	{'EXCEPTION', E} ->
194	    self() ! {suicide, self()},
195	    corba:raise(E);
196	Other ->
197	    Other
198    end.
199
200
201
202terminate(Reason, {Env, _Local}) ->
203    ?debug_print("STOP ~p   ~p~n", [?tr_get_etrap(Env), Reason]),
204    case Reason of
205	normal ->
206	    %% normal termination. Transaction completed.
207	    etrap_logmgr:stop(?tr_get_etrap(Env)),
208	    file:delete(?tr_get_etrap(Env)),
209	    ok;
210	_ ->
211	    ?tr_error_msg("Object(~p) terminated abnormal.~n",[?tr_get_etrap(Env)]),
212	    ok
213    end.
214
215
216%%------------------------------------------------------------
217%% function : handle_call, handle_cast, handle_info, code_change
218%% Arguments:
219%% Returns  :
220%% Effect   : Functions demanded by the gen_server module.
221%%------------------------------------------------------------
222
223code_change(_OldVsn, State, _Extra) ->
224    {ok, State}.
225
226handle_call(_,_, State) ->
227    {noreply, State}.
228
229handle_cast(_, State) ->
230    {noreply, State}.
231
232
233handle_info(Info, {Env, Local}) ->
234    ?debug_print("ETraP_Server:handle_info(~p)~n", [Info]),
235    Pid = self(),
236    case Info of
237	timeout ->
238	    ?tr_error_msg("Object( ~p ) timeout. Rolling back.~n",
239			  [?tr_get_etrap(Env)]),
240            {stop, normal, {Env, Local}};
241        {suicide, Pid} ->
242            {stop, normal, {Env, Local}};
243        _->
244            {noreply, {Env, Local}}
245    end.
246
247
248%%--------------- Inherit from CosTransactions::Control -----
249%%-----------------------------------------------------------%
250%% function : get_terminator
251%% Arguments: Self  - its own object reference.
252%%            State - Gen-Server State
253%% Returns  : a Terminator object reference.
254%% Effect   : Supports operations for termination of a transaction
255%%------------------------------------------------------------
256
257get_terminator(Self, {Env, Local}) ->
258    %% Only allows the root-coordinator to export the termonator.
259    %% The reason for this is that only the root-coordinator is allowed
260    %% to initiate termination of a transaction. This is however possible
261    %% to change and add restictions elsewhere, i.e. to verify if the
262    %% commit or rollback call is ok.
263    case catch ?tr_get_parents(Env) of
264	[] -> % No parents, it's a root-coordinator.
265	    % Create terminators environment.
266	    TEnv = ?tr_set_etrap(Env, Self),
267	    T = ?tr_start_child(?SUP_TERMINATOR(TEnv)),
268	    {reply, T, {Env, Local}, ?tr_get_timeout(TEnv)};
269	_ ->
270	    corba:raise(?tr_unavailable)
271    end.
272
273%%-----------------------------------------------------------%
274%% function : get_coordinator
275%% Arguments: Self  - its own object reference.
276%%            State - Gen-Server State
277%% Returns  : a Coordinator object reference. The OMG specification
278%%            states that a object reference must be returned.
279%% Effect   : Supports operations needed by resources to participate
280%%            in the transaction.
281%%------------------------------------------------------------
282
283get_coordinator(Self, State) ->
284    {reply, Self, State}.
285
286%%----- Inherit from CosTransactions::RecoveryCoordinator ---
287%%-----------------------------------------------------------%
288%% function : replay_completion
289%% Arguments:
290%% Returns  : Status
291%% Effect   : Provides a hint to the Coordinator that the commit
292%%            or rollback operations have not been performed on
293%%            the resource.
294%%------------------------------------------------------------
295
296replay_completion(_Self, {Env, Local}, Resource) ->
297    type_check(?tr_get_typeCheck(Env), ?tr_Resource,
298	       "RecoveryCoordinator:replay_completion", Resource),
299    case ?etr_get_status(Local) of
300	'StatusActive' ->
301	    corba:raise(?tr_unprepared);
302	Status ->
303	    case lists:any(?tr_IS_MEMBER(Resource), ?etr_get_members(Local)) of
304		true ->
305		    {reply, Status, {Env, Local}};
306		_ ->
307		    corba:raise(#'NO_PERMISSION'{completion_status=?COMPLETED_YES})
308	    end
309    end.
310
311%%--------------- Inherit from CosTransactions::Resource ----
312%%-----------------------------------------------------------%
313%% function : prepare
314%% Arguments:
315%% Returns  : a Vote
316%% Effect   : Is invoked to begin the two-phase-commit on the
317%%            resource.
318%%------------------------------------------------------------
319
320prepare(_Self, {Env, Local}) ->
321    %% Set status as prepared. No new Resources are allowed to register.
322    NewL = ?etr_set_status(Local, 'StatusPrepared'),
323
324    ?eval_debug_fun({?tr_get_etrap(Env), root_delay}, Env),
325
326    case catch send_prepare(?etr_get_members(NewL),
327			    ?tr_get_alarm(Env)) of
328	readOnly ->
329	    %% All voted ReadOnly, done. No need to log.
330	    {stop, normal, 'VoteReadOnly', {Env, NewL}};
331	%% Replace the reply above if allow synchronization
332%	    case ?etr_get_sync(Local) of
333%		[] ->
334%		    {stop, normal, 'VoteReadOnly', {Env, NewL}};
335%		_ ->
336%		    {reply, 'VoteReadOnly', {Env, NewL}}
337%	    end;
338	{commit, VC} ->
339	    %% All voted Commit.
340	    NewL2 = ?etr_set_vc(NewL, VC),
341	    case catch try_timeout(?tr_get_alarm(Env)) of
342		false ->
343		    case ?etr_log(?tr_get_etrap(Env), {pre_vote, commit, NewL2}) of
344			ok ->
345			    ?eval_debug_fun({?tr_get_etrap(Env), prepare1}, Env),
346			    {reply, 'VoteCommit', {Env, NewL2}};
347			_->
348			    %% Cannot log. Better to be safe than sorry; do rollback.
349			    %% However, try to log rollback.
350			    ?etr_log(?tr_get_etrap(Env),{pre_vote, rollback, NewL2}),
351			    send_decision({Env, NewL2}, 'VoteRollback', rollback)
352		    end;
353		_->
354		    ?etr_log(?tr_get_etrap(Env),
355			     {pre_vote, rollback, NewL2}),
356		    %% timeout, reply rollback.
357		    send_decision({Env, NewL2}, 'VoteRollback', rollback)
358	    end;
359	{rollback, VC} ->
360	    %% Rollback vote received.
361	    %% Send rollback to commit voters.
362	    N2 = ?etr_set_vc(NewL, VC),
363	    NewL2 = ?etr_set_status(N2,'StatusRolledBack'),
364	    ?etr_log(?tr_get_etrap(Env), {pre_vote, rollback, NewL2}),
365	    send_decision({Env, NewL2}, 'VoteRollback', rollback);
366	{'EXCEPTION', E, VC, Obj} ->
367	    NewL2 = case is_heuristic(E) of
368				true ->
369			    N2 = ?etr_set_vc(NewL, VC),
370			    N3 = ?etr_set_exc(N2, E),
371			    ?etr_set_raisedH(N3, Obj);
372			_->
373			    ?etr_set_vc(NewL, VC)
374		    end,
375	    ?etr_log(?tr_get_etrap(Env),{pre_vote,rollback, NewL2}),
376	    ?eval_debug_fun({?tr_get_etrap(Env), prepare2}, Env),
377	    send_decision({Env, NewL2}, {'EXCEPTION', E}, rollback);
378	{failed, VC} ->
379	    NewL2 = ?etr_set_vc(NewL, VC),
380	    ?etr_log(?tr_get_etrap(Env),{pre_vote, rollback, NewL2}),
381	    send_decision({Env, NewL2},
382			  {'EXCEPTION', ?tr_hazard}, rollback)
383    end.
384
385
386%%-----------------------------------------------------------%
387%% function : rollback
388%% Arguments: Self - the servers own objref.
389%%            {Env, Local} - the servers internal state.
390%% Returns  : ok
391%% Effect   : Rollback the transaction. If its status is
392%%            "StatusRolledBack", this is not the first
393%%            rollback call to this server. Might occur if
394%%            the parent coordinator just recoeverd from a crasch.
395%% Exception: HeuristicCommit, HeuristicMixed, HeuristicHazard
396%%------------------------------------------------------------
397
398rollback(Self, {Env, Local}) ->
399    case ?etr_get_status(Local) of
400	'StatusRolledBack' ->
401	    case ?etr_get_exc(Local) of
402		void ->
403		    {stop, normal, ok, {Env, Local}};
404		%% Replace the reply above if allow synchronization
405		    %% Rolled back successfullly earlier.
406%		    case ?etr_get_sync(Local) of
407%			[] ->
408%			    {stop, normal, ok, {Env, Local}};
409%			_ ->
410%			    {reply, ok, {Env, Local}}
411%		    end;
412		E ->
413		    %% Already rolledback with heuristic decision
414		    corba:raise(E)
415	    end;
416	'StatusPrepared' ->
417	    NewL = ?etr_set_status(Local, 'StatusRolledBack'),
418	    ?eval_debug_fun({?tr_get_etrap(Env), rollback}, Env),
419	    ?etr_log(?tr_get_etrap(Env), rollback),
420	    ?eval_debug_fun({?tr_get_etrap(Env), rollback2}, Env),
421	    send_decision({Env, NewL}, ok, rollback);
422	'StatusActive' ->
423	    NewL = ?etr_set_status(Local, 'StatusRolledBack'),
424	    ?etr_log(?tr_get_etrap(Env), {rollback, NewL}),
425	    send_info(?etr_get_members(NewL), 'CosTransactions_Resource', rollback),
426	    notify_subtrAware(rollback, ?etr_get_subAw(NewL), Self),
427	    {stop, normal, ok, {Env, NewL}}
428%% Replace the reply above if allow synchronization
429%	    case ?etr_get_sync(Local) of
430%		[] ->
431%		    {stop, normal, ok, {NewEnv, NewL}};
432%		_ ->
433%		    {reply, ok, {NewEnv, NewL}}
434%	    end;
435    end.
436
437
438%%-----------------------------------------------------------%
439%% function : commit
440%% Arguments: Self - the servers own objref.
441%%            {Env, Local} - the servers internal state.
442%% Returns  : ok
443%% Effect   : Commit the transaction.
444%% Exception: HeuristicRollback, HeuristicMixed, HeuristicHazard,
445%%            NotPrepared
446%%------------------------------------------------------------
447
448commit(_Self, {Env, Local}) ->
449    case ?etr_get_status(Local) of
450	'StatusPrepared' ->
451	    ?eval_debug_fun({?tr_get_etrap(Env), commit}, Env),
452	    NewL = ?etr_set_status(Local, 'StatusCommitted'),
453	    ?etr_log(?tr_get_etrap(Env),commit),
454	    ?eval_debug_fun({?tr_get_etrap(Env), commit2}, Env),
455	    send_decision({Env, NewL}, ok, commit);
456	'StatusCommitted' ->
457	    case ?etr_get_exc(Local) of
458		void ->
459		    {stop, normal, ok, {Env, Local}};
460		%% Replace the reply above if allow synchronization
461%		    case ?etr_get_sync(Local) of
462%			[] ->
463%			    {stop, normal, ok, {Env, Local}};
464%			_ ->
465%			    {reply, ok, {Env, Local}}
466%		    end;
467		E->
468		    corba:raise(E)
469	    end;
470	_ ->
471	    corba:raise(?tr_unprepared)
472    end.
473
474%%-----------------------------------------------------------%
475%% function : commit_one_phase
476%% Arguments: Self - the servers own objref.
477%%            {Env, Local} - the servers internal state.
478%% Returns  : ok
479%% Effect   : Commit the transaction using one-phase commit.
480%%            Use ONLY when there is only one registered Resource.
481%% Exception: HeuristicRollback, HeuristicMixed, HeuristicHazard,
482%%            TRANSACTION_ROLLEDBACK
483%%------------------------------------------------------------
484
485commit_one_phase(_Self, {Env, Local}) ->
486    case ?etr_get_members(Local) of
487	[Resource] ->
488	    case ?etr_get_status(Local) of
489		'StatusActive' ->
490		    %% Set status as prepared. No new Resources are allowed to register.
491		    NewL = ?etr_set_status(Local, 'StatusPrepared'),
492		    ?eval_debug_fun({?tr_get_etrap(Env), onePC}, Env),
493		    case try_timeout(?tr_get_alarm(Env)) of
494			false ->
495			    case catch 'CosTransactions_Resource':prepare(Resource) of
496				'VoteCommit' ->
497				    case try_timeout(?tr_get_alarm(Env)) of
498					false ->
499					    send_decision({Env, NewL}, ok, commit, [Resource]);
500					_->
501					    %% Timeout, rollback.
502					    send_decision({Env, NewL},
503							  {'EXCEPTION',
504							   #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
505							  rollback, [Resource])
506				    end;
507				'VoteRollback' ->
508				    {stop, normal,
509				     {'EXCEPTION',
510				      #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
511				     {Env, NewL}};
512				'VoteReadOnly' ->
513				    {stop, normal, ok, {Env, NewL}};
514				{'EXCEPTION', E}
515				when is_record(E, 'CosTransactions_HeuristicMixed') ->
516				    {reply, {'EXCEPTION', E}, {Env, NewL}};
517				{'EXCEPTION', E}
518				when is_record(E, 'CosTransactions_HeuristicHazard') ->
519				    {reply, {'EXCEPTION', E}, {Env, NewL}};
520				Other ->
521				    ?tr_error_msg("Coordinator:prepare( ~p ) failed. REASON ~p~n",
522						  [Resource, Other]),
523				    {stop, normal,
524				     {'EXCEPTION',
525				      #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
526				     {Env, NewL}}
527			    end;
528			_->
529			    NewL2 = ?etr_set_status(NewL, 'StatusRolledBack'),
530			    send_info(Resource, 'CosTransactions_Resource', rollback),
531			    {stop, normal,
532			     {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
533			     {Env, NewL2}}
534			%% Replace the reply above if allow synchronization
535%			    case ?etr_get_sync(NewL2) of
536%				[] ->
537%				    send_info(Resource, 'CosTransactions_Resource', rollback),
538%				    {stop, normal,
539%				     {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
540%				     {Env, NewL2}};
541%				_ ->
542%				    send_info(Resource, 'CosTransactions_Resource', rollback),
543%				    {reply,
544%				     {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
545%				     {Env, NewL2}}
546%			    end
547		    end;
548		_ ->
549		    case evaluate_status(?etr_get_status(Local)) of
550			commit ->
551			    test_exc(set_exception(?etr_get_exc_init(),
552						   ?etr_get_exc(Local)),
553				     commit, ok, {Env, Local});
554			_->
555			    test_exc(set_exception(?etr_get_exc_init(),
556						   ?etr_get_exc(Local)),
557				     rollback,
558				     {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
559				     {Env, Local})
560		    end
561	    end;
562	_->
563	    {reply, {'EXCEPTION', #'NO_PERMISSION'{completion_status=?COMPLETED_NO}},
564	     {Env, Local}}
565    end.
566
567%%-----------------------------------------------------------%
568%% function : forget
569%% Arguments: Self  - the servers own objref.
570%%            State - the servers internal state.
571%% Returns  : ok
572%% Effect   : The resource can forget all knowledge about the
573%%            transaction. Terminate this server.
574%%------------------------------------------------------------
575
576forget(_Self, {Env, Local}) ->
577    ?etr_log(?tr_get_etrap(Env), forget_phase),
578    send_forget(?etr_get_raisedH(Local), ?tr_get_etrap(Env)),
579    {stop, normal, ok, {Env, ?etr_set_exc(Local, void)}}.
580%% Replace the reply above if allow synchronization
581%    case ?etr_get_sync(Local) of
582%	[] ->
583%	    {stop, normal, ok, {Env, ?etr_set_exc(Local, void)}};
584%	_ ->
585%	    {reply, ok, {Env, ?etr_set_exc(Local, void)}}
586%    end.
587
588%%--------------- Inherrit from CosTransactions::Coordinator -
589
590%%-----------------------------------------------------------%
591%% function : get_status
592%% Arguments: Self  - its own object reference.
593%%            State - Gen-Server State
594%% Returns  : Status
595%% Effect   : Returns the status of the transaction associated
596%%            with the target object.
597%%------------------------------------------------------------
598
599get_status(_Self, {Env, Local}) ->
600    {reply, ?etr_get_status(Local), {Env, Local}}.
601
602
603%%-----------------------------------------------------------%
604%% function : get_parent_status
605%% Arguments: Self  - its own object reference.
606%%            State - Gen-Server State
607%% Returns  : Status
608%% Effect   : Returns the status of the parent transaction
609%%            associated with the target object. If top-level
610%%            transaction equal to get_status.
611%%------------------------------------------------------------
612
613get_parent_status(_Self, {Env, Local}) ->
614    case catch ?tr_get_parents(Env) of
615	[] ->
616	    {reply, ?etr_get_status(Local), {Env, Local}};
617	[Parent|_] ->
618	    case catch 'CosTransactions_Coordinator':get_status(Parent) of
619		{'EXCEPTION', _E} ->
620		    corba:raise(?tr_unavailable);
621		{'EXIT', _} ->
622		    corba:raise(?tr_unavailable);
623		Status ->
624		    {reply, Status, {Env, Local}}
625	    end
626    end.
627
628
629%%-----------------------------------------------------------%
630%% function : get_top_level_status
631%% Arguments: Self  - its own object reference.
632%%            State - Gen-Server State
633%% Returns  : Status
634%% Effect   : Returns the status of the top-level transaction
635%%            associated with the target object. If top-level
636%%            transaction equal to get_status.
637%%------------------------------------------------------------
638
639get_top_level_status(_Self, {Env, Local}) ->
640    case catch ?tr_get_parents(Env) of
641	[] ->
642	    {reply, ?etr_get_status(Local), {Env, Local}};
643	Ancestrors ->
644	    case catch 'CosTransactions_Coordinator':get_status(lists:last(Ancestrors)) of
645		{'EXCEPTION', _E} ->
646		    corba:raise(?tr_unavailable);
647		{'EXIT', _} ->
648		    corba:raise(?tr_unavailable);
649		Status ->
650		    {reply, Status, {Env, Local}}
651	    end
652    end.
653
654
655%%-----------------------------------------------------------%
656%% function : is_same_transaction
657%% Arguments: Self  - its own object reference.
658%%            State - Gen-Server State
659%%            Coordinator object reference
660%% Returns  : boolean
661%% Effect   :
662%%------------------------------------------------------------
663
664is_same_transaction(Self, {Env, Local}, Coordinator) ->
665    type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
666	       "Coordinator:is_same_transaction", Coordinator),
667    {reply, corba_object:is_equivalent(Self, Coordinator), {Env, Local}}.
668
669%%------------------------------------------------------------
670%% function : is_related_transaction
671%% Arguments: Self  - its own object reference.
672%%            State - Gen-Server State
673%%            Coordinator object reference
674%% Returns  : boolean
675%% Effect   :
676%%------------------------------------------------------------
677
678-spec is_related_transaction(_, _, _) -> no_return().
679is_related_transaction(_Self, {_Env, _Local}, _Coordinator) ->
680    corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_YES}).
681%    type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
682%	       "Coordinator:is_related_transaction", Coordinator),
683%    {reply, false, {Env, Local}}.
684
685
686%%------------------------------------------------------------
687%% function : is_ancestor_transaction
688%%            Coordinator object reference
689%% Returns  : boolean
690%% Effect   :
691%%------------------------------------------------------------
692
693-spec is_ancestor_transaction(_, _, _) -> no_return().
694is_ancestor_transaction(_Self, {_Env, _Local}, _Coordinator) ->
695    corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_YES}).
696%    type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
697%	       "Coordinator:is_ancestor_transaction", Coordinator),
698%    {reply, false, {Env, Local}}.
699
700
701%%-----------------------------------------------------------%
702%% function : is_descendant_transaction
703%% Arguments: Self  - its own object reference.
704%%            State - Gen-Server State
705%%            Coordinator object reference
706%% Returns  : boolean
707%% Effect   :
708%%------------------------------------------------------------
709
710is_descendant_transaction(Self, {Env, Local}, Coordinator) ->
711    type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
712	       "Coordinator:is_descendant_transaction", Coordinator),
713    {reply,
714     lists:any(?tr_IS_MEMBER(Coordinator), [Self|?tr_get_parents(Env)]),
715     {Env, Local}}.
716
717%%-----------------------------------------------------------%
718%% function : is_top_level_transaction
719%% Arguments: Self  - its own object reference.
720%%            State - Gen-Server State
721%% Returns  : boolean
722%% Effect   :
723%%------------------------------------------------------------
724
725is_top_level_transaction(_Self, {Env, Local}) ->
726     case catch ?tr_get_parents(Env) of
727	[] ->
728	     {reply, true, {Env, Local}};
729	 _ ->
730	     {reply, false, {Env, Local}}
731     end.
732
733%%-----------------------------------------------------------%
734%% function : hash_transaction
735%% Arguments: Self  - its own object reference.
736%%            State - Gen-Server State
737%% Returns  : hash code
738%% Effect   : Returns a hash code for the transaction associated
739%%            with the target object.
740%%------------------------------------------------------------
741
742hash_transaction(Self, {Env, Local}) ->
743    {reply, corba_object:hash(Self, ?tr_get_hashMax(Env)), {Env, Local}}.
744
745
746%%-----------------------------------------------------------%
747%% function : hash_top_level_tran
748%% Arguments: Self  - its own object reference.
749%%            State - Gen-Server State
750%% Returns  : hash code
751%% Effect   : Returns a hash code for the top-level transaction
752%%            associated with the target object. Equals
753%%            hash_transaction if it's a top-level transaction.
754%%------------------------------------------------------------
755
756hash_top_level_tran(Self, {Env, Local}) ->
757    case ?tr_get_parents(Env) of
758	[] ->
759	    {reply,
760	     corba_object:hash(Self, ?tr_get_hashMax(Env)),
761	     {Env, Local}};
762	Ancestrors ->
763	    case catch corba_object:hash(lists:last(Ancestrors),
764					 ?tr_get_hashMax(Env)) of
765		{'EXCEPTION', _E} ->
766		    corba:raise(?tr_unavailable);
767		Hash ->
768		    {reply, Hash, {Env, Local}}
769	    end
770    end.
771
772
773
774%%-----------------------------------------------------------%
775%% function : register_resource
776%% Arguments: Self  - its own object reference.
777%%            State - Gen-Server State
778%%            Resource object reference
779%% Returns  : RecoveryCoordinator (can be used during recovery)
780%% Effect   : Registers the specified resource as as participant
781%%            in the transaction associated with the target object.
782%% Exception: Inactive - Is prepared or terminated.
783%%------------------------------------------------------------
784
785register_resource(Self, {Env, Local}, Resource) ->
786    type_check(?tr_get_typeCheck(Env), ?tr_Resource,
787	       "Coordinator:register_resource", Resource),
788    case ?etr_get_status(Local) of
789	'StatusActive' -> % ok to register the Resource.
790	    NewLocal = ?etr_add_member(Local, Resource),
791	    RecoveryCoord = corba:create_subobject_key(Self, ?tr_get_etrap(Env)),
792	    {reply, RecoveryCoord, {Env, NewLocal}, ?tr_get_timeout(Env)};
793	_-> % Not active anymore. New members not ok.
794	    corba:raise(?tr_inactive)
795    end.
796
797
798
799%%-----------------------------------------------------------%
800%% function : register_subtran_aware
801%% Arguments: Self  - its own object reference.
802%%            State - Gen-Server State
803%%            SubTransactionAwareResource object reference
804%% Returns  : -
805%% Effect   : Registers the specified object such that it
806%%            will be notified when the subtransaction has
807%%            commited or rolled back.
808%%------------------------------------------------------------
809
810register_subtran_aware(Self, {Env, Local}, SubTrAwareResource) ->
811    case ?tr_get_parents(Env) of
812	[] ->
813	    corba:raise(?tr_NotSubtr);
814	_->
815	    type_check(?tr_get_typeCheck(Env), ?tr_SubtransactionAwareResource,
816		       "Coordinator:register_subtran_aware", SubTrAwareResource),
817	    NewL = ?etr_add_subAw(Local, SubTrAwareResource),
818	    {reply, ok, {Env, ?etr_set_self(NewL, Self)},
819	     ?tr_get_timeout(Env)}
820    end.
821
822%%-----------------------------------------------------------%
823%% function : register_synchronization
824%% Arguments: Self  - its own object reference.
825%%            State - Gen-Server State
826%%            Synchronization
827%% Returns  : -
828%% Effect   :
829%%------------------------------------------------------------
830
831-spec register_synchronization(_, _, _) -> no_return().
832register_synchronization(_Self, {_Env, _Local}, _Synchronization) ->
833    corba:raise(#'CosTransactions_SynchronizationUnavailable'{}).
834
835%register_synchronization(Self, {Env, Local}, Synchronization) ->
836%    type_check(?tr_get_typeCheck(Env), ?tr_Synchronization,
837%	       "Coordinator:register_synchronization", Synchronization),
838%    case ?etr_get_status(Local) of
839%	'StatusActive' ->
840%	    case catch ?tr_get_parents(Env) of
841%		[] ->
842%		    {reply, ok, {Env, ?etr_add_sync(Local, Synchronization)},
843%		     ?tr_get_timeout(Env)};
844%		[Parent|_] ->
845%		    case catch 'ETraP_Server':register_synchronization(Parent, Self) of
846%			{'EXCEPTION', E} ->
847%			    corba:raise(E);
848%			ok ->
849%			    {reply, ok, {Env, ?etr_add_sync(Local, Synchronization)},
850%			     ?tr_get_timeout(Env)};
851%			What ->
852%			    corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_MAYBE})
853%		    end
854%	    end;
855%	_ ->
856%	    corba:raise(?tr_inactive)
857%    end.
858
859%%-----------------------------------------------------------%
860%% function : rollback_only
861%% Arguments: Self  - its own object reference.
862%%            State - Gen-Server State
863%% Returns  : -
864%% Effect   : The transaction associated with the target object
865%%            is modified so that rollback IS the result.
866%%------------------------------------------------------------
867
868rollback_only(Self, {Env, Local}) ->
869    case ?etr_get_status(Local) of
870	'StatusActive' ->
871	    NewL = ?etr_set_status(Local, 'StatusRolledBack'),
872	    NewEnv = ?tr_set_rollback(Env, true),
873	    ?etr_log(?tr_get_etrap(Env),{rollback, NewL}),
874	    send_info(?etr_get_members(NewL), 'CosTransactions_Resource', rollback),
875	    notify_subtrAware(rollback, ?etr_get_subAw(NewL), Self),
876	    {stop, normal, ok, {NewEnv, NewL}};
877%% Replace the reply above if allow synchronization
878%	    case ?etr_get_sync(Local) of
879%		[] ->
880%		    {stop, normal, ok, {NewEnv, NewL}};
881%		_ ->
882%		    {reply, ok, {NewEnv, NewL}}
883%	    end;
884	_ ->
885	    corba:raise(?tr_inactive)
886    end.
887
888%%-----------------------------------------------------------%
889%% function : get_transaction_name
890%% Arguments: Self  - its own object reference.
891%%            State - Gen-Server State
892%% Returns  : string - which describes the transaction associated
893%%                     with the target object.
894%% Effect   : Intended for debugging.
895%%------------------------------------------------------------
896
897get_transaction_name(_Self, {Env, Local}) ->
898    {reply, ?tr_get_etrap(Env), {Env, Local}}.
899
900%%-----------------------------------------------------------%
901%% function : create_subtransaction
902%% Arguments: Self  - its own object reference.
903%%            State - Gen-Server State
904%% Returns  : A control object if subtransactions are allowed,
905%%            otherwise an exception is raised.
906%% Effect   : A new subtransaction is created whos parent is
907%%            the transaction associated with the target object.
908%% Exception: SubtransactionUnavailabe - no support for nested
909%%                                       transactions.
910%%            Inactive - already been prepared.
911%%------------------------------------------------------------
912
913create_subtransaction(Self, {Env, Local}) ->
914    case ?etr_get_status(Local) of
915	'StatusActive' ->
916	    case ?tr_get_subTraOK(Env) of
917		true ->
918		    ETraPName = 'ETraP_Common':create_name("subc"),
919		    Tname   = 'ETraP_Common':create_name("subt"),
920
921		    %% Create context for the new object.
922		    State   = ?tr_create_context(ETraPName, Tname,
923						 ?tr_get_typeCheck(Env),
924						 ?tr_get_hashMax(Env),
925						 ?tr_get_subTraOK(Env),
926						 ?tr_get_maxR(Env),
927						 ?tr_get_maxW(Env)),
928
929
930		    State2  = ?tr_add_parent(State, Self),
931
932		    State3  = ?tr_set_alarm(State2, ?tr_get_alarm(Env)),
933
934		    State4  = ?tr_set_timeout(State3, ?tr_get_timeout(Env)),
935
936		    Control = ?tr_start_child(?SUP_ETRAP(State4)),
937		    %% Set the SubCoordinator object reference and register it as participant.
938		    SubCoord = 'CosTransactions_Control':get_coordinator(Control),
939		    NewLocal = ?etr_add_member(Local, SubCoord),
940		    {reply, Control, {Env, NewLocal}, ?tr_get_timeout(Env)};
941		_ ->
942		    %% subtransactions not allowed, raise exception.
943		    corba:raise(?tr_subunavailable)
944	    end;
945	_->
946	    corba:raise(?tr_inactive)
947    end.
948
949%%-----------------------------------------------------------%
950%% function : get_txcontext
951%% Arguments:
952%% Returns  : PropagationContext
953%% Effect   :
954%%------------------------------------------------------------
955
956-spec get_txcontext(_, _) -> no_return().
957get_txcontext(_Self, {_Env, _Local}) ->
958    corba:raise(#'CosTransactions_Unavailable'{}).
959
960%get_txcontext(Self, {Env, Local}) ->
961%    Otid = #'CosTransactions_otid_t'{formatID=0,
962%				     bqual_length=0,
963%				     tid=[corba_object:hash(Self,
964%							   ?tr_get_hashMax(Env))]},
965%    TrIDs = create_TransIdentities(?tr_get_parents(Env), Env, [], Otid),
966%    C=case ?tr_get_parents(Env) of
967%	  [] ->
968%	      #'CosTransactions_TransIdentity'{coord=Self,
969%					       term=?tr_get_terminator(Env),
970%					       otid=Otid};
971%	  _->
972%	      #'CosTransactions_TransIdentity'{coord=Self,
973%					       term=?tr_NIL_OBJ_REF,
974%					       otid=Otid}
975%      end,
976%    case ?tr_get_timeout(Env) of
977%	infinity ->
978%	    #'CosTransactions_PropagationContext'{timeout=0,
979%						  current= C,
980%						  parents=TrIDs};
981%	T ->
982%	    #'CosTransactions_PropagationContext'{timeout=T/1000,
983%						  current= C,
984%						  parents=TrIDs}
985%    end.
986
987%create_TransIdentities([], _, Parents, _) -> Parents;
988%create_TransIdentities([Phead|Ptail], Env, Parents, Otid) ->
989%    NO=Otid#'CosTransactions_TransIdentity'{otid=
990%					    corba_object:hash(Phead,
991%							      ?tr_get_hashMax(Env))},
992%    create_TransIdentities([Phead|Ptail], Env, Parents++
993%			   [#'CosTransactions_TransIdentity'{coord=Phead,
994%							     term=?tr_NIL_OBJ_REF,
995%							     otid=NO}],
996%			   Otid).
997
998
999%%--------- Inherit from CosTransactions::Synchronization ---
1000
1001%%-----------------------------------------------------------%
1002%% function : before_completion
1003%% Arguments:
1004%% Returns  :
1005%% Effect   :
1006%%------------------------------------------------------------
1007
1008%before_completion(Self, {Env, Local}) ->
1009%    send_info(?etr_get_sync(Local),
1010%	      'CosTransactions_Synchronization', before_completion),
1011%    {reply, ok, {Env, Local}}.
1012
1013%%-----------------------------------------------------------%
1014%% function : after_completion
1015%% Arguments:
1016%% Returns  :
1017%% Effect   :
1018%%------------------------------------------------------------
1019
1020%after_completion(Self, {Env, Local}, Status) ->
1021%    send_info(?etr_get_sync(Local), Status,
1022%	      'CosTransactions_Synchronization', after_completion),
1023%    {stop, normal, ok, {Env, Local}}.
1024
1025%%--------------- IMPLEMENTATION SPECIFIC -------------------
1026%%-----------------------------------------------------------%
1027%% function : start_object
1028%% Arguments:
1029%% Returns  : EXIT, EXCEPTION, or {ok, State}
1030%% Effect   : used by init/1 only.
1031%%------------------------------------------------------------
1032
1033start_object(Env)->
1034    ?put_debug_data(self, Env),
1035    Local = ?etr_get_init(Env),
1036    LogName = ?tr_get_etrap(Env),
1037    case catch file:read_file_info(LogName) of
1038	{error, enoent} ->
1039	    %% File does not exist. It's the first time. No restart.
1040	    ?debug_print("ETraP_Server:init(~p)~n",[?tr_get_etrap(Env)]),
1041	    etrap_logmgr:start(LogName),
1042	    {ok,
1043	     {Env, ?etr_set_status(Local, 'StatusActive')},
1044	     ?tr_get_timeout(Env)};
1045	{error, Reason} ->
1046	    %% File exist but error occurred.
1047	    ?tr_error_msg("Control (~p) Cannot open log file: ~p~n",
1048			  [LogName, Reason]),
1049	    {stop, "unable_to_open_log"};
1050	_ ->
1051	    %% File exists, perform restart.
1052	    etrap_logmgr:start(LogName),
1053	    ?debug_print("RESTART ~p~n", [?tr_get_etrap(Env)]),
1054	    prepare_restart({Env, ?etr_set_status(Local, 'StatusUnknown')},
1055			    ?etr_read(?tr_get_etrap(Env), start))
1056    end.
1057
1058
1059%%-----------------------------------------------------------%
1060%% function : send_prepare
1061%% Arguments: List of registred resources.
1062%% Returns  : ok - equal to void
1063%% Effect   : calls send_prepare/3, which sends a prepare call
1064%% to resources participating in the transaction and then collect
1065%% their votes. send_prepare will block until
1066%% it recieves a reply from the resource.
1067%%------------------------------------------------------------
1068
1069send_prepare(RegResources, Alarm) ->
1070    send_prepare(RegResources, [], Alarm).
1071
1072% All voted ReadOnly. We are done.
1073send_prepare([], [], _) ->
1074    readOnly;
1075
1076% All voted commit (VC) or ReadOnly.
1077send_prepare([], VC, Alarm) ->
1078    case catch try_timeout(Alarm) of
1079	false ->
1080	    {commit, VC};
1081	_->
1082	    {rollback, VC}
1083    end;
1084
1085send_prepare([Rhead|Rtail], VC, Alarm) ->
1086    ?debug_print("send_prepare()~n",[]),
1087    case catch 'CosTransactions_Resource':prepare(Rhead) of
1088	'VoteCommit' ->
1089	    case catch try_timeout(Alarm) of
1090		false ->
1091		    _Env = ?get_debug_data(self),
1092		    ?eval_debug_fun({?tr_get_etrap(_Env), send_prepare}, _Env),
1093		    send_prepare(Rtail, VC++[Rhead], Alarm);
1094		_->
1095		    %% Timeout, rollback. However, the resource did vote
1096		    %% commit. Add it to the list.
1097		    send_info(Rtail, 'CosTransactions_Resource', rollback),
1098		    {rollback, VC++[Rhead]}
1099	    end;
1100	'VoteRollback' ->
1101	    %% Don't care about timeout since we voted rollback.
1102	    %% A rollback received. No need for more prepare-calls.
1103	    %% See OMG 10-51, Transaction Service:v1.0
1104	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1105	    {rollback, VC};
1106	'VoteReadOnly' ->
1107	    case catch try_timeout(Alarm) of
1108		false ->
1109		    send_prepare(Rtail, VC, Alarm);
1110		_->
1111		    %% timeout, reply rollback.
1112		    send_info(Rtail, 'CosTransactions_Resource', rollback),
1113		    {rollback, VC}
1114	    end;
1115	{'EXCEPTION',E} when is_record(E, 'TIMEOUT') ->
1116	    ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
1117			  [Rhead]),
1118	    %% Since we use presumed abort we will rollback the transaction.
1119	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1120	    {rollback, VC};
1121	{'EXCEPTION',E} when is_record(E, 'TRANSIENT') ->
1122	    ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
1123			  [Rhead]),
1124	    %% Since we use presumed abort we will rollback the transaction.
1125	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1126	    {rollback, VC};
1127	{'EXCEPTION',E} when is_record(E, 'COMM_FAILURE') ->
1128	    ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
1129			  [Rhead]),
1130	    %% Since we use presumed abort we will rollback the transaction.
1131	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1132	    {rollback, VC};
1133	{'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') ->
1134	    ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
1135			  [Rhead]),
1136	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1137	    {rollback, VC};
1138	{'EXCEPTION', Exc} ->
1139	    ?tr_error_msg("Coordinator:prepare( ~p )~nThe Object raised exception: ~p~n",
1140			  [Rhead, Exc]),
1141	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1142	    %% This can occur if a subtransaction get one or more
1143	    %% "VoteCommit" followed by a "VoteRollback".
1144	    %% The subtransaction then do a send_decision(rollback),
1145	    %% which can generate Heuristic decisions. Must rollback
1146	    %% since at least one participant voted rollback.
1147	    {'EXCEPTION', Exc, VC, Rhead};
1148	Other ->
1149	    ?tr_error_msg("Coordinator:prepare( ~p ) failed. REASON ~p~n",
1150			  [Rhead, Other]),
1151	    send_info(Rtail, 'CosTransactions_Resource', rollback),
1152	    {failed, VC}
1153    end.
1154
1155%%-----------------------------------------------------------%
1156%% function : type_check
1157%% Arguments: Bool - perform typecheck?
1158%%            ID   - Type it should be.
1159%%            Func - Name of the function (for error_msg)
1160%%            Obj  - objectrefernce to test.
1161%% Returns  : 'ok' or raises exception.
1162%% Effect   :
1163%%------------------------------------------------------------
1164type_check(false, _, _, _) ->
1165    ok;
1166type_check(_, ID, Func, Obj) ->
1167    case catch corba_object:is_a(Obj,ID) of
1168	true ->
1169	    ok;
1170	_ ->
1171	    ?tr_error_msg("~p( ~p )  Bad argument!!~n", [Func, Obj]),
1172	    corba:raise(?tr_badparam)
1173    end.
1174
1175%%-----------------------------------------------------------%
1176%% function : is_heuristic
1177%% Arguments: Exception
1178%% Returns  : boolean
1179%% Effect   : Returns true if the exception is a heuristic exc.
1180%%------------------------------------------------------------
1181
1182is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicMixed')    -> true;
1183is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicHazard')   -> true;
1184is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicCommit')   -> true;
1185is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicRollback') -> true;
1186is_heuristic(_)            -> false.
1187
1188%%-----------------------------------------------------------%
1189%% function : exception_set
1190%% Arguments: Genserver state
1191%% Returns  :
1192%% Effect   : Used when restarting.
1193%%------------------------------------------------------------
1194
1195exception_set({Env,Local}) ->
1196    case ?etr_get_exc(Local) of
1197	void ->
1198	    self() ! {suicide, self()},
1199	    {ok, {Env, Local}};
1200	_ ->
1201	    {ok, {Env, Local}}
1202    end.
1203
1204%%-----------------------------------------------------------%
1205%% function : set_exception
1206%% Arguments: Locally defined #exc{}
1207%%            Heuristic mixed or hazard Exeption
1208%% Returns  : Altered locally defined #exc{}
1209%% Effect   : Set the correct tuple member to true.
1210%%------------------------------------------------------------
1211
1212set_exception(Exc, E) when is_record(E, 'CosTransactions_HeuristicMixed')  ->
1213    Exc#exc{mixed  = true};
1214set_exception(Exc, E) when is_record(E, 'CosTransactions_HeuristicHazard') ->
1215    Exc#exc{hazard = true};
1216set_exception(Exc, _)            -> Exc.
1217
1218%%-----------------------------------------------------------%
1219%% function : send_forget
1220%% Arguments:
1221%% Returns  :
1222%% Effect   :
1223%%------------------------------------------------------------
1224
1225send_forget([], _) -> ok;
1226send_forget([Rhead|Rtail], LogName) ->
1227    ?debug_print("send_forget()~n",[]),
1228    _Env = ?get_debug_data(self),
1229    case catch 'CosTransactions_Resource':forget(Rhead) of
1230	ok ->
1231	    ?eval_debug_fun({?tr_get_etrap(_Env), send_forget1}, _Env),
1232	    ?etr_log(LogName, {forgotten, Rhead}),
1233	    ?eval_debug_fun({?tr_get_etrap(_Env), send_forget2}, _Env),
1234	    send_forget(Rtail, LogName);
1235	Other ->
1236	    ?tr_error_msg("CosTransactions_Coordinator failed sending forget to  ~p~nREASON: ~p~n",
1237			  [Rhead, Other]),
1238	    ?eval_debug_fun({?tr_get_etrap(_Env), send_forget3}, _Env),
1239	    ?etr_log(LogName, {not_forgotten, Rhead}),
1240	    ?eval_debug_fun({?tr_get_etrap(_Env), send_forget4}, _Env),
1241	    send_forget(Rtail, LogName)
1242    end.
1243
1244%%-----------------------------------------------------------%
1245%% function : send_decision
1246%% Arguments: List of registred resources which vote commit.
1247%%            Vote - the outcome of the transaction.
1248%% Returns  : ok - equal to void
1249%% Effect   : Inform those who voted commit of the outcome.
1250%% They who voted rollback already knows the outcome.
1251%% They who voted ReadOnly are not affected.
1252%%------------------------------------------------------------
1253
1254%%-- Adding extra parameters
1255send_decision({Env, Local}, Reply, Vote) ->
1256    send_decision({Env, Local}, Reply, ?etr_get_vc(Local), Vote, #exc{}, [], 0).
1257send_decision({Env, Local}, Reply, Vote, VC) ->
1258    send_decision({Env, Local}, Reply, VC, Vote, #exc{}, [], 0).
1259send_decision(State, no_reply, VC, Vote, Exc) ->
1260    send_decision(State, no_reply, VC, Vote, Exc, [], 0).
1261
1262%%-- Decision sent to all members. Do not reply (used when restarting).
1263send_decision({Env, Local}, no_reply, [], _, #exc{mixed = true}, [], _) ->
1264    {Env, ?etr_set_exc(Local, ?tr_mixed)};
1265send_decision({Env, Local}, no_reply, [], _, #exc{hazard = true}, [], _) ->
1266    {Env, ?etr_set_exc(Local, ?tr_hazard)};
1267send_decision({Env, Local}, no_reply, [], _, _, [], _) ->
1268    {Env, Local};
1269send_decision({Env, Local}, no_reply, [], Vote, Exc, Failed, Times) ->
1270    case ?tr_get_maxR(Env) of
1271	Times ->
1272	    ?tr_error_msg("MAJOR ERROR, failed sending commit decision to: ~p. Tried ~p times.", [Failed,Times]),
1273	    {Env, ?etr_set_exc(Local, ?tr_hazard)};
1274	_->
1275	    timer:sleep(?tr_get_maxW(Env)),
1276	    NewTimes = Times+1,
1277	    send_decision({Env, Local}, no_reply, Failed, Vote, Exc, [], NewTimes)
1278    end;
1279%%-- end special cases.
1280
1281%% Decision sent to all members. Test exceptions.
1282send_decision({Env, Local}, Reply, [], Vote, Exc, [], _) ->
1283    notify_subtrAware(Vote, ?etr_get_subAw(Local), ?etr_get_self(Local)),
1284    test_exc(Exc, Vote, Reply, {Env, Local});
1285%% Decision not sent to all members (one or more failed). Retry.
1286send_decision({Env, Local}, Reply, [], Vote, Exc, Failed, Times) ->
1287    case ?tr_get_maxR(Env) of
1288	Times ->
1289	    ?tr_error_msg("MAJOR ERROR, failed sending commit decision to: ~p. Tried ~p times.", [Failed,Times]),
1290	    notify_subtrAware(Vote, ?etr_get_subAw(Local), ?etr_get_self(Local)),
1291	    test_exc(Exc#exc{hazard = true}, Vote, Reply, {Env, Local});
1292	_->
1293	    NewTimes = Times+1,
1294	    timer:sleep(?tr_get_maxW(Env)),
1295	    send_decision({Env, Local}, Reply, Failed, Vote, Exc, [], NewTimes)
1296    end;
1297
1298send_decision({Env, Local}, Reply, [Rhead|Rtail], Vote, Exc, Failed, Times) ->
1299    ?debug_print("Coordinator:send_decision(~p) Try: ~p~n",[Vote, Times]),
1300    case catch 'CosTransactions_Resource':Vote(Rhead) of
1301        ok ->
1302	    ?etr_log(?tr_get_etrap(Env),{sent, Rhead}),
1303            send_decision({Env, Local}, Reply, Rtail, Vote, Exc, Failed, Times);
1304        {'EXCEPTION', E} when Vote == commit andalso
1305			      is_record(E, 'CosTransactions_NotPrepared') ->
1306	    ?debug_print("send_decision resource unprepared~n",[]),
1307            case catch 'CosTransactions_Resource':prepare(Rhead) of
1308                'VoteCommit' ->
1309                    send_decision({Env, Local}, Reply, [Rhead|Rtail], Vote, Exc, Failed, Times);
1310		'VoteRollback' ->
1311                    send_decision({Env, Local}, Reply, Rtail, Vote, Exc#exc{mixed = true}, Failed, Times);
1312		{'EXCEPTION', E} ->
1313		    {SetExc, NewL, DidFail} =
1314			evaluate_answer(E, Rhead, Vote, Exc,
1315					?tr_get_etrap(Env), Local),
1316		    send_decision({Env, NewL}, Reply, Rtail, Vote, SetExc, DidFail++Failed, Times)
1317            end;
1318	{'EXCEPTION', E} ->
1319	    {SetExc, NewL, DidFail} =
1320		evaluate_answer(E, Rhead, Vote, Exc, ?tr_get_etrap(Env), Local),
1321	    ?tr_error_msg("Resource:~p( ~p )~nRaised Exception: ~p~n",
1322			  [Vote, Rhead, E]),
1323	    send_decision({Env, NewL}, Reply, Rtail, Vote, SetExc, DidFail++Failed, Times);
1324	{'EXIT', _} ->
1325	    send_decision({Env, Local}, Reply, Rtail,
1326			  Vote, Exc, [Rhead|Failed], Times);
1327	Other ->
1328	    ?tr_error_msg("Resource:~p( ~p ) failed.~nREASON: ~p~n",
1329			  [Vote, Rhead, Other]),
1330	    case catch corba_object:non_existent(Rhead) of
1331		true when Vote == commit ->
1332		    %% Presumed rollback
1333		    send_decision({Env, Local}, Reply, Rtail, Vote,
1334				  Exc#exc{mixed = true}, Failed, Times);
1335		true ->
1336		    %% Presumed rollback
1337		    send_decision({Env, Local}, Reply, Rtail, Vote,
1338				  Exc#exc{hazard = true}, Failed, Times);
1339		_ ->
1340		    send_decision({Env, Local}, Reply, Rtail,
1341				  Vote, Exc, [Rhead|Failed], Times)
1342	    end
1343    end.
1344
1345%%-----------------------------------------------------------%
1346%% function : notify_subtrAware,
1347%% Arguments:
1348%% Returns  :
1349%% Effect   : Invoke an operation on a list of objects. We don't
1350%%            care about return values or exceptions.
1351%%------------------------------------------------------------
1352
1353notify_subtrAware(commit, Resources, Self) ->
1354    send_info(Resources, Self,
1355	      'CosTransactions_SubtransactionAwareResource',
1356	      commit_subtransaction);
1357notify_subtrAware(_, Resources, _) ->
1358    send_info(Resources, 'CosTransactions_SubtransactionAwareResource',
1359	      rollback_subtransaction).
1360
1361%%-----------------------------------------------------------%
1362%% function : send_info
1363%% Arguments: ObjectList - List of object refernces to call.
1364%%            M - Module
1365%%            F - Function
1366%%            (Arg - required arguments)
1367%% Returns  : ok
1368%% Effect   : A lightweight function to be used when we don't
1369%%            "care" about the return value.
1370%%------------------------------------------------------------
1371
1372send_info([], _, _, _) ->
1373    ok;
1374send_info([Rhead|Rtail], Arg, M, F) ->
1375    ?debug_print("~p( ~p )~n",[F, Arg]),
1376    case catch M:F(Rhead, Arg) of
1377	{'EXIT',R} ->
1378	    ?tr_error_msg("~p:~p(~p, ~p) returned {'EXIT',~p}", [M,F,Rhead,Arg,R]);
1379	{'EXCEPTION',E} ->
1380	    ?tr_error_msg("~p:~p(~p, ~p) returned {'EXCEPTION',~p}", [M,F,Rhead,Arg,E]);
1381	_->
1382	    ok
1383    end,
1384    send_info(Rtail, Arg, M, F).
1385
1386send_info([], _, _) ->
1387    ok;
1388send_info([Rhead|Rtail], M, F) ->
1389    ?debug_print("~p( )~n",[F]),
1390    case catch M:F(Rhead) of
1391	{'EXIT',R} ->
1392	    ?tr_error_msg("~p:~p(~p) returned {'EXIT',~p}", [M,F,Rhead,R]);
1393	{'EXCEPTION',E} ->
1394	    ?tr_error_msg("~p:~p(~p) returned {'EXCEPTION',~p}", [M,F,Rhead,E]);
1395	_->
1396	    ok
1397    end,
1398    send_info(Rtail, M, F).
1399
1400%%-----------------------------------------------------------%
1401%% function : evaluate_answer
1402%% Arguments:
1403%% Returns  :
1404%% Effect   : Check what kind of exception we received.
1405%%------------------------------------------------------------
1406
1407evaluate_answer(E, Rhead, _Vote, Exc, Log, Local)
1408  when is_record(E, 'CosTransactions_HeuristicMixed') ->
1409    ?etr_log(Log, {heuristic, {Rhead, E}}),
1410    {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []};
1411evaluate_answer(E, Rhead, _Vote, Exc, Log, Local)
1412  when is_record(E, 'CosTransactions_HeuristicHazard') ->
1413    ?etr_log(Log, {heuristic, {Rhead, E}}),
1414    {Exc#exc{hazard = true}, ?etr_add_raisedH(Local, Rhead), []};
1415evaluate_answer(E, Rhead, Vote, Exc, Log, Local)
1416  when is_record(E, 'CosTransactions_HeuristicCommit') ->
1417    case Vote of
1418	commit ->
1419	    ?etr_log(Log, {heuristic, {Rhead, E}}),
1420	    {Exc, ?etr_add_raisedH(Local, Rhead), []};
1421	_->
1422	    ?etr_log(Log, {heuristic, {Rhead, ?tr_mixed}}),
1423	    {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []}
1424    end;
1425evaluate_answer(E, Rhead, Vote, Exc, Log, Local)
1426  when is_record(E, 'CosTransactions_HeuristicRollback')->
1427    case Vote of
1428	rollback ->
1429	    ?etr_log(Log, {heuristic, {Rhead, ?tr_rollback}}),
1430	    {Exc, ?etr_add_raisedH(Local, Rhead), []};
1431	_->
1432	    ?etr_log(Log, {heuristic, {Rhead, ?tr_mixed}}),
1433	    {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []}
1434    end;
1435evaluate_answer(E, Rhead, Vote, Exc, Log, Local)
1436  when Vote == commit andalso is_record(E, 'TRANSACTION_ROLLEDBACK') ->
1437    ?etr_log(Log, {heuristic, {Rhead, ?tr_mixed}}),
1438    {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []};
1439evaluate_answer(E, Rhead, Vote, Exc, Log, Local) when is_record(E, 'TIMEOUT') ->
1440    ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
1441		  [Vote, Rhead, E]),
1442    case catch corba_object:non_existent(Rhead) of
1443	true ->
1444	    %% Since we have presumed abort, the child will
1445	    %% assume rollback if this server do not exist any more.
1446	    ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
1447	    {Exc#exc{hazard = true}, Local, []};
1448	_ ->
1449	    {Exc, Local, [Rhead]}
1450    end;
1451evaluate_answer(E, Rhead, Vote, Exc, Log, Local) when is_record(E, 'TRANSIENT') ->
1452    ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
1453		  [Vote, Rhead, E]),
1454    case catch corba_object:non_existent(Rhead) of
1455	true ->
1456	    %% Since we have presumed abort, the child will
1457	    %% assume rollback if this server do not exist any more.
1458	    ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
1459	    {Exc#exc{hazard = true}, Local, []};
1460	_ ->
1461	    {Exc, Local, [Rhead]}
1462    end;
1463evaluate_answer(E, Rhead, Vote, Exc, Log, Local) when is_record(E, 'COMM_FAILURE') ->
1464    ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
1465		  [Vote, Rhead, E]),
1466    case catch corba_object:non_existent(Rhead) of
1467	true ->
1468	    %% Since we have presumed abort, the child will
1469	    %% assume rollback if this server do not exist any more.
1470	    ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
1471	    {Exc#exc{hazard = true}, Local, []};
1472	_ ->
1473	    {Exc, Local, [Rhead]}
1474    end;
1475evaluate_answer(E, Rhead, Vote, Exc, Log, Local)when is_record(E, 'OBJECT_NOT_EXIST') ->
1476    ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
1477		  [Vote, Rhead, E]),
1478    %% Since we have presumed abort, the child will
1479    %% assume rollback if this server do not exist any more.
1480    ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
1481    {Exc#exc{hazard = true}, Local, []};
1482evaluate_answer(Unknown, Rhead, Vote, Exc, _Log, Local)->
1483    ?tr_error_msg("Coordinator:~p( ~p ). Unknown reply: ~p.~n",
1484		  [Vote, Rhead, Unknown]),
1485    {Exc, Local, []}.
1486
1487
1488%%-----------------------------------------------------------%
1489%% function : test_exc
1490%% Arguments: Exc   - instance of #exc{} locally declared.
1491%%            Vote  - 'rollback' or 'commit'
1492%%            Reply - If no exceptions this is the default reply.
1493%%            State - genserver state
1494%% Returns  :
1495%% Effect   : Raise the correct exception or simply reply to
1496%%            the genserver. NOTE that the testing for exceptions
1497%%            differs if we are performing a rollback or commit.
1498%%            Check if Mixed first; takes priority over Hazard.
1499%%            HeuristicRollback and VoteCommit together give
1500%%            HeuristicMixed
1501%%            HeuristicCommit and VoteRollback together give
1502%%            HeuristicMixed
1503%%------------------------------------------------------------
1504
1505test_exc(#exc{mixed = true}, _, _, {Env, Local}) ->
1506    {reply, {'EXCEPTION', ?tr_mixed}, {Env, ?etr_set_exc(Local, ?tr_mixed)}};
1507% Left out for now to avoid dialyzer warning.
1508%test_exc(#exc{rollback = true}, commit, _, {Env, Local}) ->
1509%    {reply, {'EXCEPTION', ?tr_mixed}, {Env, ?etr_set_exc(Local, ?tr_mixed)}};
1510% Left out for now to avoid dialyzer warning.
1511%test_exc(#exc{commit = true}, rollback, _, {Env, Local}) ->
1512%    {reply, {'EXCEPTION', ?tr_mixed}, {Env, ?etr_set_exc(Local, ?tr_mixed)}};
1513test_exc(#exc{hazard = true}, _, _, {Env, Local}) ->
1514    {reply, {'EXCEPTION', ?tr_hazard}, {Env, ?etr_set_exc(Local, ?tr_hazard)}};
1515test_exc(_, _, {'EXCEPTION', E}, {Env, Local})
1516  when is_record(E, 'TRANSACTION_ROLLEDBACK')->
1517    {stop, normal, {'EXCEPTION', E}, {Env, Local}};
1518%% Replace the case above if allow synchronization
1519%test_exc(_, _, {'EXCEPTION', E}, {Env, Local})
1520%  when record(E, 'TRANSACTION_ROLLEDBACK')->
1521%    case ?etr_get_sync(Local) of
1522%	[] ->
1523%	    {stop, normal, {'EXCEPTION', E}, {Env, Local}};
1524%	_->
1525%	    {reply, {'EXCEPTION', E}, {Env, Local}}
1526%    end;
1527test_exc(_, _, {'EXCEPTION', E}, State) ->
1528    {reply, {'EXCEPTION', E}, State};
1529test_exc(_, _, Reply, {Env, Local}) ->
1530    {stop, normal, Reply, {Env, Local}}.
1531%% Replace the case above if allow synchronization
1532%test_exc(_, _, Reply, {Env, Local}) ->
1533%    case ?etr_get_sync(Local) of
1534%	[] ->
1535%	    {stop, normal, Reply, {Env, Local}};
1536%	_ ->
1537%	    {reply, Reply, {Env, Local}}
1538%    end.
1539
1540%%-----------------------------------------------------------%
1541%% function : evaluate_status
1542%% Arguments:
1543%% Returns  :
1544%% Effect   :
1545%%------------------------------------------------------------
1546
1547evaluate_status(Status) ->
1548 case Status of
1549     'StatusCommitted'      -> commit;
1550     'StatusCommitting'     -> commit;
1551     'StatusMarkedRollback' -> rollback;
1552     'StatusRollingBack'    -> rollback;
1553     'StatusRolledBack'     -> rollback;
1554     'StatusActive'         -> rollback;
1555     'StatusPrepared'       -> rollback;
1556     'StatusUnknown'        -> rollback;
1557     'StatusNoTransaction'  -> rollback;
1558     'StatusPreparing'      -> rollback;
1559     _-> rollback
1560 end.
1561
1562
1563%%-----------------------------------------------------------%
1564%% function : prepare_restart
1565%% Arguments:
1566%% Returns  :
1567%% Effect   :
1568%%------------------------------------------------------------
1569
1570%% The file contains no data. The coordinator crashed before
1571%% a prepare-call was made. Presumed rollback.
1572prepare_restart(State, eof) ->
1573    ?debug_print("prepare_restart: eof, init~n",[]),
1574    self() ! {suicide, self()},
1575    {ok, State};
1576%% Collected all necessary votes. Do commit_restart.
1577prepare_restart({Env, _}, {{pre_vote, _Vote, Data}, Cursor}) ->
1578    ?debug_print("prepare_restart: pre_vote( ~p )~n",[_Vote]),
1579    if
1580	?tr_is_root(Env) ->
1581	    commit_restart({Env, Data},
1582			   ?etr_read(?tr_get_etrap(Env), Cursor), root);
1583	true ->
1584	    commit_restart({Env, Data},
1585			   ?etr_read(?tr_get_etrap(Env), Cursor), subCoord)
1586    end;
1587%% 'rollback' called without 'prepare'. This case occurs if the Coordinator
1588%% crashes when send_info or notify_subtrAware.
1589prepare_restart({Env, _}, {{rollback, NewL}, _Cursor}) ->
1590    ?debug_print("prepare_restart: pre_vote( rollback )~n",[]),
1591    send_info(?etr_get_members(NewL), 'CosTransactions_Resource', rollback),
1592    notify_subtrAware(rollback, ?etr_get_subAw(NewL), ?etr_get_self(NewL)),
1593    self() ! {suicide, self()},
1594    {ok, {Env, NewL}};
1595%% Something is wrong in the log.
1596prepare_restart(_, _) ->
1597    ?tr_error_msg("Internal log read failed:~n", []),
1598    {stop, {error, "restart failed"}}.
1599
1600%%-----------------------------------------------------------%
1601%% function : commit_restart
1602%% Arguments: Env - server context
1603%% Returns  :
1604%% Effect   :
1605%%------------------------------------------------------------
1606commit_restart({Env, Local}, Data, Phase) ->
1607    Exc = set_exception(#exc{}, ?etr_get_exc(Local)),
1608    commit_restart({Env, Local}, Data, Phase, Exc).
1609
1610%% Normal case. No errors no exceptions.
1611commit_restart({Env, Local}, {{sent, Obj}, Cursor}, Vote, Exc) ->
1612    ?debug_print("commit_restart: sent~n",[]),
1613    commit_restart({Env, ?etr_remove_vc(Local, Obj)},
1614		   ?etr_read(?tr_get_etrap(Env), Cursor), Vote, Exc);
1615commit_restart({Env, Local}, {{heuristic, {Obj,E}}, Cursor}, Vote, Exc) ->
1616    ?debug_print("commit_restart: heuristic    ~p~n",[E]),
1617    NewExc = set_exception(Exc, E),
1618    commit_restart({Env, ?etr_add_raisedH(Local, Obj)},
1619		   ?etr_read(?tr_get_etrap(Env), Cursor), Vote, NewExc);
1620
1621
1622%% --- cases which only can occure once in the log ------------
1623
1624%% The file contains no data. The coordinator crashed before
1625%% a decision was made. Causes rollback.
1626commit_restart({E, L}, eof, root, Exc) ->
1627    ?debug_print("commit_restart: eof init (root only)~n",[]),
1628    {Env, Local} = send_decision({E, L}, no_reply, ?etr_get_vc(L),
1629				 rollback, Exc),
1630    exception_set({Env, Local});
1631%% Replace the reply above if allow synchronization
1632%    case ?etr_get_sync(Local) of
1633%	[] ->
1634%	    exception_set({Env, Local});
1635%	SynchObjs ->
1636%	    {ok, {Env, Local}}
1637%    end;
1638
1639
1640%% Passed the prepare_restart. Not received a commit decision from the
1641%% parent.
1642commit_restart({E, L}, eof, subCoord, Exc) ->
1643    ?debug_print("commit_restart: eof init (subcoord only)~n",[]),
1644    case catch corba_object:non_existent(?tr_get_parent(E)) of
1645	true ->
1646	    %% Presumed rollback.
1647	    {Env, Local} = send_decision({E, L}, no_reply, ?etr_get_vc(L),
1648					 rollback, Exc),
1649	    self() ! {suicide, self()},
1650	    {ok, {Env, Local}};
1651%% Replace the reply above if allow synchronization
1652%	    case ?etr_get_sync(Local) of
1653%		[] ->
1654%		    self() ! {suicide, self()},
1655%		    {ok, {Env, Local}};
1656%		SynchObjs ->
1657%		    case ?tr_get_parents(Env) of
1658%			[] ->
1659%			    send_info(SynchObjs, ?etr_get_status(Local),
1660%				      'CosTransactions_Synchronization', after_completion);
1661%			_->
1662%			    ok
1663%		    end,
1664%		    self() ! {suicide, self()},
1665%		    {ok, {Env, Local}}
1666%	    end;
1667	_->
1668	    {ok, {E, L}}
1669    end;
1670
1671commit_restart({Env, Local}, eof, Vote, Exc) ->
1672    ?debug_print("commit_restart: eof    VOTE: ~p~n",[Vote]),
1673    case ?etr_get_vc(Local) of
1674	[] ->
1675	    ?debug_print("commit_restart: all sent, test exc~n",[]),
1676            exception_set({Env, Local});
1677	VC ->
1678	    ?debug_print("commit_restart: note done. send more~n",[]),
1679	    State = send_decision({Env, Local}, no_reply, VC, Vote, Exc),
1680	    exception_set(State)
1681    end;
1682
1683%% Decision made, i.e. rollback or commit.
1684commit_restart({Env, Local}, {rollback, Cursor}, _Phase, Exc) ->
1685    ?debug_print("commit_restart: decided rollback~n",[]),
1686    commit_restart({Env, ?etr_set_status(Local, 'StatusRolledBack')},
1687		   ?etr_read(?tr_get_etrap(Env), Cursor), rollback, Exc);
1688commit_restart({Env, Local}, {commit, Cursor}, _Phase, Exc) ->
1689    ?debug_print("commit_restart: decided commit~n",[]),
1690    commit_restart({Env, ?etr_set_status(Local, 'StatusCommitted')},
1691		   ?etr_read(?tr_get_etrap(Env), Cursor), commit, Exc);
1692commit_restart({Env, Local}, {forget_phase, Cursor}, _, _) ->
1693    ?debug_print("commit_restart: start sending forget~n",[]),
1694    forget_restart({Env, Local}, ?etr_read(?tr_get_etrap(Env), Cursor));
1695
1696commit_restart({_Env, _Local}, _R, _, _) ->
1697    ?debug_print("RESTART FAIL: ~p~n",[_R]),
1698    ?tr_error_msg("Internal log read failed:~n", []),
1699    exit("restart failed").
1700
1701%%-----------------------------------------------------------%
1702%% function : forget_restart
1703%% Arguments: {Env, Local} - server context
1704%% Returns  :
1705%% Effect   :
1706%%------------------------------------------------------------
1707
1708%% Exception logged. Test if we issued a 'forget()' to the Resource.
1709forget_restart({Env, Local}, eof) ->
1710    case ?etr_get_raisedH(Local) of
1711	[] ->
1712	    ?debug_print("forget_restart: all done~n",[]);
1713	Left ->
1714	    ?debug_print("forget_restart: not done. send more~n",[]),
1715	    send_forget(Left, ?tr_get_etrap(Env))
1716    end,
1717    self() ! {suicide, self()},
1718    {ok, {Env, Local}};
1719%% Replace the reply above if allow synchronization
1720%    case ?etr_get_sync(Local) of
1721%	[] ->
1722%	    self() ! {suicide, self()},
1723%	    {ok, {Env, Local}};
1724%	SynchObjs ->
1725%	    case ?tr_get_parents(Env) of
1726%		[] ->
1727%		    send_info(SynchObjs, ?etr_get_status(Local),
1728%			      'CosTransactions_Synchronization', after_completion),
1729%		    self() ! {suicide, self()},
1730%		    {ok, {Env, Local}};
1731%		_->
1732%		    {ok, {Env, Local}}
1733%	    end
1734%    end;
1735forget_restart({Env, Local}, {{forgotten, Obj}, Cursor}) ->
1736    ?debug_print("forget_restart: forgotten  heuristic~n",[]),
1737    NewL = ?etr_remove_raisedH(Local, Obj),
1738    forget_restart({Env, NewL}, ?etr_read(?tr_get_etrap(Env), Cursor));
1739forget_restart({Env, Local}, {{not_forgotten, Obj}, Cursor}) ->
1740    ?debug_print("forget_restart: not_forgotten~n",[]),
1741    NewL = ?etr_remove_raisedH(Local, Obj),
1742    send_forget([Obj], dummy),
1743    forget_restart({Env, NewL}, ?etr_read(?tr_get_etrap(Env), Cursor)).
1744
1745%%--------------- END OF MODULE ------------------------------
1746