Wednesday, March 18, 2015

Resource management idioms in Erlang

Resource management problem

Resource management is an important question in any programming language. It stays important even if runtime provides garbage collector, because there are other resource besides memory. Resource is something, what client has to initialise and later clean up, for example, socket, file descriptor or connection to RDBMS.

Language specific solutions

Resource management problem is solved in different languages differently.

C

Every C developer has to take care of resource deallocation after some usage manually. For example, each memory allocation with malloc must be followed with free. From the first sight such approach might look not that difficult, because there are no exceptions in C. But in fact it makes code much more complicated. Situation is slightly improved in frameworks such as Glib.

C++

C++ is successor of C, it introduces classes with constructors and destructors and guarantees calling of object's destructor when it goes out of scope. As a result Resource Acquisition Is Initialisation (RAII) idiom emerged together with smart pointers, which are an implementation of the idiom. In C++ exceptions are also introduced and RAII helps in exception safety. So resource could be represented as an object, where it's initialised in constructor and deallocated in destructor.

Java

Even though objects in Java have finalize method, which is called before destruction, it can not be safely used for resource deallocation as C++ destructor. It's because Java runtime uses garbage collector and the moment of clean up is not guaranteed.
Instead try-finally block can be used
Type res = new Type();
try
{
    res.use();
}
finally
{
    res.deallocate();
}
The same approach is used in other languages with garbage collector (C#, Python, etc.) sometimes with additional syntax sugar.

Erlang

In Erlang developer can use all there techniques of resource management listed above.

Manual management

I assume that everybody is aware of disadvantages of C-style resource management and tries to limit it's usage, I would not go into details.

RAII

Some of OTP behaviours (gen_serv, gen_fsm) have init and terminate callbacks, which could be used for resource allocation and deallocation correspondingly. terminate is called right before process termination and fits for most usages of resource deallocation. The only potential issue could be a strict synchronisation of clean up with another process, which requires additional message sending.
Famous examples of RAII idiom from OTP are ETS and gen_tcp. ETS table is cleaned up by default, if process, which created it, terminates. Socket opened in context of process is closed on process termination as well.

try-catch

Resource deallocation function is guaranteed to be called in after clause of try-catch block. For example, this is how poolboy:transaction function is implemented.
transaction(Pool, Fun, Timeout) ->
    Worker = poolboy:checkout(Pool, true, Timeout),
    try
        Fun(Worker)
    after
        ok = poolboy:checkin(Pool, Worker)
    end.

Erlang-specific recommendations

The rule of thumb for choosing between try-catch block and RAII(separate process) is quite simple. Try-catch is easier and shorter, but is not applicable if allocated resource should exist during processing of next message/callback in caller process. It also can not be used if you need to share a resource between multiple processes.
Do not try to save few lines of boilerplate code for a new gen_server/gen_fsm module for resource management, when it's needed. It will eventually pay off with cleaner code.

General recommendations

Try to avoid manual (C-style) resource management.
Do not handle multiple resources neither in single try-catch block, nor in single object, which implements RAII idiom, because it's difficult to implement correctly.