try... after and clean-up

Richard Carlsson richardc@REDACTED
Tue Jan 10 12:17:40 CET 2006


Hi!
The basic pattern should be something like this, for
each individual resource:

    {ok, Handle} = allocate_resource(),
    try
	use_resource(Handle)
    after
	free_resource(Handle)
    end

That is, you make sure that the 'try...after...end' is entered
*if and only if* the resource has been allocated. (If the
allocation failed, there is nothing to clean up.) It must not
be possible to get an exception _after_ the resource has been
successfully allocated, which prevents the free_resource()
from being executed (e.g., if the allocate_resource() call
above would return {true, Handle} rather than {ok, Handle}).
If that happens, you won't even know _how_ to refer to the
resource, since the handle will not be bound to any variable
in scope when that exception is finally caught (somewhere else).
Hence, don't put any unnecessary code between the allocation
and the start of the 'try'.

Assuming this is clear, the rest is easy. Preferably, you
lift out each use_resource() section to a separate function
rather than nesting, and maybe also put each allocate-use-free
block like the above in separate functions. You could even
abstract out the use-body, to get that functional feeling:

    with_x(F) ->
        {ok, R} = allocate_x(),
        try F(R) after free_x(R) end.

    ...
    with_x(fun (R) -> use_x(R, OtherArgs) end)
    ...

A less ambitious rewrite of your code could look as follows:

run(Args)
  when is_record(Args, bot_args) ->
    Bot = #bot {
      poker_server = Args#bot_args.host,
      dispatchers = [{"script",
		      Args#bot_args.script,
		      Args#bot_args.script_args
		     }],
      log = Args#bot_args.log,
      ignored = Args#bot_args.ignored,
      lobby_bot = Args#bot_args.lobby_bot
     },
    Bot1 = notrace(Bot, [cl_ssl_handshake_data,
			 srv_ssl_handshake_data,
			 srv_update_hint_text,
			 cl_handshake,
			 srv_handshake,
			 srv_news]),
    try
	UtilSock = util:connect_script_server(),
	try
	    Bot2 = Bot1#bot {
		     util_sock = UtilSock
		    },
	    Bot3 = util:connect(Bot2,
				[lobby_con_wrap(),
				 lobby_handle_wrap(),
				 connected]), % event to post
	    try
		run(Bot3, handle(Bot3, handshake))
	    after
		%% cleanup Bot3
	    end
	after
	    %% cleanup UtilSock
	end,
    after
	%% cleanup Bot1
    end.

Good luck!

	/Richard




More information about the erlang-questions mailing list