29 Oct 2007, 04:59
Generic-user-small

Peter Miller (4 posts)

I’m new to Erlang and learning about it by reading through “Programming Erlang” (thank you Joe Armstrong for this excellent book). I just finished chapter 8 and was mulling over the first of the programming exercises from section 8.11:

bq. Write a function start(AnAtom, Fun) to register AnAtomas spawn(Fun). Make sure your program works correctly in the case when two parallel processes simultaneously evaluate start/2. In this case, you must guarantee that one of these processes succeeds and the other fails.

I was stumped at first, but then I found the following discussion post, “Programming Erlang Exercise 8.11”:http://www.nabble.com/Programming-Erlang-Exercise-8.11-tf4485540.html#a12791301 , which presented a seemingly logical solution.

The solution in that thread did raise 2 interesting questions for me that I wanted to throw out to any experienced Erlang programmers:

  1. How is the BIF register/2 function implemented to be an atomic call? I looked in the Erlang documentation online and could not find any details. As a BIF it is implemented in C, so I suppose there a lot of possibilities, but is there any way for a curious person to find out?

  2. Is this problem of multiple processes trying to call register/2 at the same time something that you (experienced with Erlang) run into a lot and have to code around or is this problem more theoretical?

10 Dec 2007, 03:23
Alain_o_dea_pragsmall

Alain O'Dea (41 posts)

I am new to Erlang as well and this problem had me so stumped that I nearly abandoned the book altogether. Years of using Object-oriented programming languages with bolted on concurrency support had left me with limited problem solving skills for concurrent problems.

Erlang’s source is available and it should contain the implementation of register/2

# I imagine this is a common problem. After looking at Ladislav Lenart’s solution to 8.11 I am happy that there is a relatively concise way to solve it

Here is my version of Ladislav Lenart’s solution altered to behave like register/2 in terms of exceptions thrown and return values:

-module (start_register).
-export ([start/2]).

start(Atom, Fun) ->
    Registrant = self(),
    spawn(
        fun() ->
            try register(Atom, self()) of
                true ->
                    Registrant ! true,
                    Fun()
            catch
                error:badarg ->
                    Registrant ! false
            end
        end),
    receive
        true  -> true;
        false -> erlang:error(badarg)
    end.

This looks like the basis of a atomic transaction design pattern for Erlang. I imagine a collection of such design patterns exists. I would really like to see it if it does.

20 Dec 2007, 05:00
Generic-user-small

Peter Miller (4 posts)

Agreed. Thanks for sharing your version of the solution.

24 Sep 2008, 10:16
Generic-user-small

Bryan (6 posts)

Is there an easy way to prove that a given solution works? Being new to Erlang I’m not sure how I’d generate a testbench that would prove a solution works.

28 Sep 2008, 14:32
Alain_o_dea_pragsmall

Alain O'Dea (41 posts)

Hi Bryan: I’m not even sure that this is testable since it would involve testing for the non-existence of a race condition. Formal verification is possible since Erlang uses CSP (Communicating Sequential Processes). I’m not sure how it is done, but it is possible to prove the correctness of code like this.

A design pattern does exist and is implemented in the distribution: the OTP behaviours all do this transactional spawn and register using gen:start/6. The code for that is (excerpt from Erlang/OTP R12-B Source for gen.erl):

start(GenMod, LinkP, Name, Mod, Args, Options) ->
    case where(Name) of
	undefined ->
	    do_spawn(GenMod, LinkP, Name, Mod, Args, Options);
	Pid ->
	    {error, {already_started, Pid}}
    end.

The real answer to Peter’s Question 2 is that this is a theoretical problem unlikely to appear in a practical Erlang/OTP system. If you are using registered processes, you are in all likelihood using OTP behaviours which manage the transactional semantics for you. A reasonable Erlang/OTP system would not attempt to register the same name twice. Registered names work best for persistent server processes and these are: 1) hard to write correctly, and 2) already written correctly and battle tested in OTP.

20 Mar 2014, 19:23
Generic-user-small

Matthew Whillock (8 posts)

Hi,

Nearly four years late but here goes…

My “solution” is:

start(AnAtom, Fun) ->
    case whereis(AnAtom) of
        undefined ->
            Pid1 = spawn(Fun),
            register(AnAtom, Pid1),
            Pid1;
        _ -> fail
    end.

Is this correct? But really stumps me is part 2 of the exercise. I guess we need to use our solution to part 1 but this is doing my head in. Anybody got example solutions to these pesky critters?

Cheers, Matt

20 Mar 2014, 19:24
Generic-user-small

Matthew Whillock (8 posts)

Obviously I meant “but what really stumps me…”

Cheers, Matt

20 Mar 2014, 19:35
Generic-user-small

Matthew Whillock (8 posts)

Aha! Solutions found in Chapter 8 problems post. Must get some eyes or something…

Cheers, Matt

  You must be logged in to comment