程序代写代做代考 concurrency compiler Advanced Programming – Riding the OTP

Advanced Programming – Riding the OTP

Advanced Programming
Riding the OTP

Ken Friis Larsen
kflarsen@diku.dk

Department of Computer Science
University of Copenhagen

October 11, 2018

1 / 17

Today’s Menu

I Library code for making robust servers

I Open Telecom Platform (OTP)

2 / 17

Generic Servers

I Goal: Abstract out the difficult handling of concurrency to a
generic library

I The difficult parts:
I The start–request_reply(/async)–loop pattern
I Registering processes
I Supervisors
I Hot-swapping of code

3 / 17

Basic Server Library

start(Name, Mod) ->
register(Name, spawn(fun() -> loop(Name, Mod, Mod:init())

end)).
request_reply(Pid, Request) ->
Pid ! {self(), Request},
receive
{Pid, Reply} -> Reply

end.
loop(Name, Mod, State) ->
receive

{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {Name, Reply},
loop(Name, Mod, State1)

end.

4 / 17

Example: Phonebook Callback Module, 1

-module(pb).
-import(basicserver, [request_reply/2]).

%% Interface
start() -> basicserver:start(phonebook, pb).
add(Contact) -> request_reply(phonebook, {add, Contact}).
list_all() -> request_reply(phonebook, list_all).
update(Contact) -> request_reply(phonebook, {update, Contact}).

5 / 17

Example: Phonebook Callback Module, 2

%% Callback functions
init() -> #{}.

handle({add, {Name, _, _} = Contact}, Contacts) ->
case maps:is_key(Name, Contacts) of

false -> {ok, Contacts#{Name => Contact}};
true -> {{error, Name, is_already_there},

Contacts}
end;

handle(list_all, Contacts) ->
List = maps:to_list(Contacts),
{{ok, lists:map(fun({_, C}) -> C end, List)},
Contacts};

handle({update, {Name, _, _} = Contact}, Contacts) ->
{ok, Contacts#{Name => Contact}}.

6 / 17

Hot Code Swapping

swap_code(Name, Mod) -> request_reply(Name, {swap_code, Mod}).
request_reply(Pid, Request) ->

Pid ! {self(), Request},
receive {Pid, Reply} -> Reply
end.

loop(Name, Mod, State) ->
receive

{From, {swap_code, NewMod}} ->
From ! {Name, ok},
loop(Name, NewMod, State);

{From, Request} ->
{Reply, State1} = Mod:handle(Request, State),
From ! {Name, Reply},
loop(Name, Mod, State1)

end.

7 / 17

What is the behaviour

I For a callback module to work with basicserver and codeswap
it need to export two functions init and handle.
It would be great if someone could help us get that right. . .

I The compiler can help us

I Add the following to basicserver.erl:

-callback init() -> State :: term().
-callback handle(Arg :: term(), State :: term()) ->

{ ok, State :: term() } |
{ {error, Reason :: term()}, State :: term() }.

I Add the following to pb.erl:

-behaviour(basicserver).

8 / 17

Open Telecom Platform (OTP)

I Library(/framework/platform) for building large-scale,
fault-tolerant, distributed applications.

I A central concept is the OTP behaviour
I Some behaviours

I supervisor
I gen_server
I gen_statem (or gen_fsm)
I gen_event

I See proc_lib and sys modules for basic building blocks.

9 / 17

Using gen_server

I Step 1: Decide module name

I Step 2: Write client interface functions
I Step 3: Write the six server callback functions:

I init/1
I handle_call/3
I handle_cast/2
I handle_info/2
I terminate/2
I code_change/3

(you can implement the callback functions by need.)

10 / 17

Supervisors

(Image credit Fred Herbert)

11 / 17

Using gen_statem

I Step 1: Decide module name

I Step 2: Write client interface functions
I Step 3: Write following callback functions:

I init/1
I callback_mode/0 should return state_functions or

handle_event_function
I terminate/3
I code_change/4
I handle_event/4 or some StateName/3functions

12 / 17

Example State Machine: A Door

I A door can be locked or open

Locked Open

press buttons

correct code

timeout 5s

13 / 17

Callback module for gen_statem, part 1

-module(door).
-behaviour(gen_statem).
-export([…]).

start(Code) ->
gen_statem:start({local, door}, door,

lists:reverse(Code), []).

button(Digit) ->
gen_statem:cast(door, {button, Digit}).

stop() ->
gen_statem:stop(door).

14 / 17

Callback module for gen_statem, part 2

locked(cast, {button, Digit}, {SoFar, Code}) ->
beep(Digit),
case [Digit|SoFar] of

Code ->
do_unlock(),
{next_state, open, {[], Code}, 5000};

Incomplete when length(Incomplete)
{next_state, locked, {Incomplete, Code}};

_Wrong ->
thats_not_gonna_do_it(),
{keep_state, {[], Code}}

end.

open(timeout, _, State) ->
do_lock(),
{next_state, locked, State}.

15 / 17

Summary

I To make a robust system we need two parts: one to do the job
and one to take over in case of errors

I Structure your code into the infrastructure parts and the
functional parts.

I Use gen_server for building robust servers.

I Use gen_statem (or gen_fsm) for servers that can be in different
states.

16 / 17

Exam

I One week take-home project (2/11–9/11)

I Hand in via Digital Exam

I Max group size is 1 (one)

I The University has a zero-tolerance policy against exam fraud
(including assisting).

17 / 17