You are on page 1of 23

A bot blog (/)

Home (/en/)

About this blog (/en/page/313/about-this-blog)

An Erlang OTP tutorial for beginners


ThisisanErlang/OTPtutorialfornoviceErlangprogrammers.IfyouarefamiliarwithErlang'ssyntax,knowhowto
compilemodulesandarecurioustodiscoverOTPandhowtoleverageitspowerinyourownErlangapplicationsthis
tutorialisforyou.TokickoffwearegoingtocreateanonOTPserverandsupervisor,examinetheirshortcomings
andthenturnourcodeintoaproperOTPapplication.
Thistutorialwilllookat:
HowtobuildanonOTPserverfromscratchandhowtoautomaticallyrestartitusinganonOTPsupervisor.
WhattheshortcomingsofourimplementationareandhowtoaddressesthemwithOTP.
Verybriefly,toolssuchasrebartosmothenourOTPjourneyandOTPsfolderconventions.
HowtouseOTPsgen_serverandsupervisorbehaviour.
TheOTPapplicationbehaviourtotieitalltogether.
First,however,someintroductionsareinorder.

About you
IassumethatyouarefamiliarwithErlangssyntax,thatyouknowwhatErlangmodulesareandhowtocompilethem
andthatyouunderstandfunctionalprogrammingconceptssuchasrecursionandsingleassignmentvariables.Youdo?
Great!LetslearnsomeOTP.Itisnotdifficulttogetstarted,asyouwillfindoutshortly,butfirstletmetellyoubriefly
aboutmyownErlangexperience.

About me
Iamprettymuchonthesamepageasyouexceptperhaps,thatIvestartedtoreadManningsErlang&OTPin
action.IamnotexaggeratingwhenIsaythatOTPinactionisoneofmyfavouritetechnicalbooks.Itcontainsawealth
ofeasytoabsorbinformation.Buyacopy(http://www.manning.com/logan)ifyoucan.
Scaffolding,Ithink,iswhattheycalleditinpsychologyclasswhenpeersteachpeers.Iamwritingthisasmuchto
cementmyownundertstandingofOTPastohelpkickstartyours.Letsstudytogether.Pleaseleaveacomment
wheneveryouthinkIamwrong.

About OTP
OTPstandsforOpenTelecomPlatformbutinspiteofitsnameitisnottiedtotelecomapplicationswhatsoever.Think
ofOTPasframeworktohelpyouwriterobustandhighlyavailableapplicationsontheErlangplatform.
Specifially,youmightliketothinkofOTPintermsofthefollowingconcepts:

aspecification(calledabehaviour)thatdictateswhichfunctionsyourmoduleshouldexport
aconcretemodulethatdeclaresthatitconformstoabehaviourandimplementsthesefunctions
aruntimecomponentthatcanexecutebehaviourcompliantmoduleswithintheOTPcontext
IfyouarecomingfromonObjectOrientedworldsuchasJavetheseconceptsroughlytranslateto:
aninterfaceorabstractclassthatdefinesabehaviour
aconcretederivedimplementationofthatinterfaceorabstractclass
acontainerthatknowshowtoinstrumentinstancesofsuchconcreteimplementations
Pleaseunderstandthattheseare,ofcourse,onlysuperficialsimilarities.Theymighthelpusunderstandnewconcepts
intermsofoldknowledge,but,makenomistake,wearecomparingapplesandoranges.

A non OTP server


Withtheintroductionsoutofthewayletsmakesurethatwereallyareonthesamepagebywritingasimpleserver
thatdoesnotuseOTP.Heresthecode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

?
%%%

%%%@docAsimpleserverthatdoesnotuseOTP.
%%%@authorHansChristianv.Stockhausen<hcatvst.io>
%%%@end
%%%

module(no_otp).%modulename(sameasourerlfilename,
%i.e.no_otp.erl)

%%API
export([%thefunctionsweexportourAPI
start/0,%startnewserverprocess
stop/0,%stopserver
say_hello/0,%print"Hello"tostdout
get_count/0%replywithnumberoftimesthemain
]).%loopwasexecuted

%%Callbacks
export([init/0]).%exportedsowecanspawnitinstart/0
%listedasaseparateexporthereasa
%hinttoclientsnottouseit

define(SERVER,?MODULE).%SERVERmacroisanaliasforMODULE,
%andexpandsto'no_otp'

record(state,{count}).%recordforourserverstate.Rather
%arbitrarilywetrackhowoftenthe
%mainloopisrunseeloop/1

%=============================================================================
%APIfunctionsthatourclientsusetointeractwiththeserver
%=============================================================================

start()>%spawnnewprocessthatcallsinit/0
spawn(?MODULE,init,[]).%thenewprocess'PIDisreturned

stop()>%sendtheatomstoptotheprocess
?SERVER!stop,%toinstructourservertoshutdown
ok.%andreturnoktocaller

say_hello()>%sendtheatomsay_hellototheprocess
?SERVER!say_hello,%toprint"Hello"tosdout
ok.

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

get_count()>%sendcallersPidandtheatomget_count
?SERVER!{self(),get_count},%torequestcountervalue
receive%waitformatchingresponseandreturn
{count,Value}>Value%Valuetothecaller
end.

%=============================================================================
%callbackfunctionsnottobeusedbyclientsdirectly
%=============================================================================

init()>%invokedbystart/0
register(?SERVER,self()),%registernewprocessPIDunderno_otp
loop(#state{count=0}).%startmainserverloop

%=============================================================================
%internalfunctionsnote,thesefunctionsarenotexported
%=============================================================================

loop(#state{count=Count})>%themainserverloop
receiveMsg>%whenAPIfunctionssendamessage
caseMsgof%checktheatomcontained
stop>%ifatomis'stop'
exit(normal)%exittheprocess
say_hello>%ifatomis'say_hello'
io:format("Hello~n")%write"Hello"tostdout
{From,get_count}>%ifMsgistuple{Pid(),get_count}
From!{count,Count}%replywithcurrentcountervalue
end%intaggedtupple{count,Value}
end,
loop(#state{count=Count+1}).%recursewithupdatedstate

Lets try our server


Savethecodeabovetoafilenamedno_otp.erl.
StarttheErlangshellwitherlinthesamedirectory.
Compilethemodule:c(no_otp).
Starttheserver:no_otp:start().
InstructtheservertoprintHelloacoupleoftimes:no_otp:say_hello().
Askforthecurrentinvocationcountervalue:no_otp:get_count().
Shuttheserverdown:no_otp:stop().
Hereismysession.Admittedly,onehastowonderwhatsthepointofget_count/0.Thereisnone,exceptto
demonstratehowtoimplementasynchronousfunction.

EshellV5.8.5(abortwith^G)
1>c(no_otp).
{ok,no_otp}
2>no_otp:start().
<0.39.0>
3>no_otp:get_count().
0
4>no_otp:say_hello().
Hello
ok
5>no_otp:get_count().
2
6>
Iinitiallyfoundthetermserverinthiscontextalittleconfusing.Howisthisaserver?Onsecondthought,itisnotatall
unliketheHTTPserverthatservedthispagetoyourbrowser.Itrunscontinouslyuntilyoustopit,itspeaksaprotocol
(notHTTPbutErlangsinternalmessagingprotocol)andrespondstoinputswithsideeffectsorresponsemessages.
Itsaserver,alright.
Iamnotgoingtodiscussthecodehereindetail,sincethecommentsalreadydothat,Ihope.However,Iwouldliketo
brieflyoutlinethestructure:
ImplementationdetailsarehiddenfromclientsbyathinAPIlayer.Thefunctionsstart/0,stop/0,etc..decouple
clientsfromtheinternalprotocol(i.e.theatomsstart,stop,thatthesefunctionssend)aswellasfrominternal
functions.Asaresultwecouldmodifyeitherorbothwithoutbreakingclientcode.
Thereexistsafunctioninit/0thatsetsuptheinitialstateandstartsthemainserverloop.Italsoregisteresour
server,byconvention,underthemodulenameandcouldperformadditionalinitialisationstepssuchasopeninga
databaseconnection.Notethatthisfunctionisexportedbyourmoduleeventhoughitisnotintendedtobecalled
byclientcode.Otherwiseourcalltospawn/3instart/0doesnotwork.
Thereisamainloopthatwaitsformessages,performssomeactionsinresponse,optionallyupdatestheserver
stateandfinallycallsitself.

Whats wrong with this server?


Notmuchreally.ThankstoErlangitshouldrunandrunandrunoncestarted.Thisisasuchasimpleserver,sowecan
bequiteconfidentthatourimplementationiscorrect.Also,thankstoourAPI,wecanbecertainthatclientsdont
inadvertentlycrashitbysendingmalformeddataorcanwe?
Startuptheserverasbeforeandthentypeno_otp!crash.attheshell.
Ouch!Sincetheserverisregisteredunderthenameno_otp(seeinit/0)wecansendarbitrarymessagestoitand
becauseourcodedoesnothaveamatchingcaseclauseforcrash:itcrashes.
Whattodo?Wecouldwriteacaseclausetomatchanymessageandsimplyignoreit.However,thatwouldbelikea
catchallclausewhenhandlingExceptions,whichweknowisevilandlikelytoobscurebugs.
Erlangfollowsaphilosophyofletitcrash.Therewegoagain.Iamsureyouvereadthatphrasemorethanonce.But
letsrecapwhatthatmeansforyou,theErlangprogrammer:Dontprogramdefensively,donttrytoanticipateall
circumstances,focusonyourspecification,onwhatthecodeshoulddoandkeepitcompactandsuccinct.Ifthereare

bugsinyourcode,itwillcrashandyoucanfixit.Ifitcrashes,becauseofbugsinyourclientscode,letitcrashand
restartinaknowngoodstate,ratherthantryingtokeepitaliveatallcosts.Dontcomplicatethecodebytryingtosort
outyourclientsmess.Itistheirstofix.Gotit?

Supervision
Fine,Illletitcrash,buthowcanIautomaticallyrestarttheserverwhenthathappens?Theansweristocodeanother
process,thatmonitorsourserverandrestartsitwhenitdetectsthatitdied.Suchaprocessisasupervisorandwhat
followsisthecodeforournonOTPversion.

A non-OTP supervisor
BeforeIshowyouthesupervisorcodehavealookattheannotatedErlangshellsessionbelowtoseehowtoexerciseit.
Thefunctionwhereis/0returnsthePidoftheprocessthatisregisteredunderaparticularname.Wecanseethat
sup:start_server/0mustinfacthavestartedourno_otpserver,sinceitshowsupunderprocessID<0.40.0>.Automatic
restartalsoseemstoworkasitshould.
EshellV5.8.5(abortwith^G)
1>c(sup).%compileoursupervisormodule
{ok,sup}
2>sup:start_server().%startthesupervisor
<0.39.0>%itgottheprocessID..39..
3>whereis(no_otp).%let'sfindourno_otpprocess
<0.40.0>%itgot..40..
4>no_otp:say_hello().%let'strytousetheAPI
Hello%works
ok
5>no_otp:get_count().%workstoo
1
6>no_otp!crash.%timetocrashit
crash
=ERRORREPORT===
Errorinprocess<0.40.0>withexitvalue:
{{case_clause,crash},[{no_otp,loop,1}]}%asexpectedacase_clauseerror
7>no_otp:get_count().%butanewprocesswasstarted
0%aswecanseehere
8>no_otp:stop().%nowlet'sstopit'normally'
ok
9>whereis(no_otp).%andcheckforanewinstance
undefined%butthereisnone.Correct!
10>
Now,forthesupervisorcode:

%%%

%%%@docAnonOTPsupervisor
%%%@authorHansChristianv.Stockhausen<hcatvst.io>
%%%@end
%%%

module(sup).
%%API
export([start_server/0]).
%%Callbacks
export([supervise/0]).
%%===========================================================================
==
%%API
%%===========================================================================
==
start_server()>
spawn(?MODULE,supervise,[]).
%%===========================================================================
==
%%Callbacks
%%===========================================================================
==
supervise()>
process_flag(trap_exit,true),%don'tcrashusbutrathersendusa
%messagewhenalinkedprocessdies
Pid=no_otp:start(),%startourserver
link(Pid),%andlinktoit
receive{'EXIT',Pid,Reason}>%waitforamessagethatinformsus
caseReasonof%thatourporcessshutdown
normal>ok%ifthereasonis'normal'donothing
_Other>start_server()%otherwisestartallover
end
end.
AsIwroteintheintroduction,IamanErlangnovicemyself.Ihavelittleconfidenceinthesupervisorcodeabove,and
thatsthepoint.IfwewanttowriteapplicationstheErlangway,ifwewanttoembracethephilosophyofletit
crash,wehavetostructureourcodeintermsofservers,supervisorsandsupervisorssupervisors.Thatsalotof
structuralcodeandalotofrepetitiveboilerplatewithambleopportunitytointroducebugs.Theconceptofserversand
supervisionhierarchiesiseasyenoughtounderstand.However,isitalsoeasytoget100%right?

OTP to the rescue


Fortunatelyforus,someoneelsehasdonethehardworkalready.ThereisOTP,aframeworkspecificallywrittento
helpuscomposeservers,supervisorsandothercomponents,whichwewillexamineatalaterstage.TheOTP
developershavegivenusarocksolidframeworktohookourown,possiblybrittlecode,intoaframeworkthatis
tested,battlehardendandknowntowork.
ItistimetorewriteourserverandsupervisorasOTPbehaviours.Butfirstly,letslookattwotools,tohelpusalong
theway.

Rebar & Emacs


JustbecauseOTPsavesusfromhavingtoreinventsupervisionhierarchiesthereis,asyouwillsee,stillafairamount
ofcodethatonehastotype.Alotremainsboilerplateandgetsrealtideousrealfast.Also,youwanttogetintothe
goodhabitofdocumentingyourcodewithEdocannotations,whichresultsinevenmorerepetitivetyping.Hence,a
littletoolsupportisinorder.
RebarisabuildtoolforErlang.Checktheirgithubwikipage(https://github.com/basho/rebar/rebar/wiki)fordetailed
instructions.Iamonlygoingtocoverwhatweneednow.
RunthefollowingcommandsfromyournixShelltoinstallrebarandtocreatethescaffoldingforourapphello.
$>mkdirhello
$>cdhello
$>wgethttps://raw.github.com/wiki/rebar/rebar/rebar
$>chmodu+xrebar
$>./rebarcreateappappid=hello
Ifyoulistthecontentsofthenewlycreatedsrcdirectoryyouwillseethatthefollowingfileshavebeengenerated:
hello_app.erlOTPapplicationbehaviour(ignorefornow)
hello.app.srcOTPapplicationconfigurationtemplate(alsoignoreplease)
hello_sup.erlminimalrootsupervisorimplementation
Whatsmissingisafiletocontainourservercode.Letsaskrebartogenerateoneforus.
$>./rebarcreatetemplate=simplesrvsrvid=hello_server
Ifyoudontwanttouserebaryoucan,ofcourse,createthefilesmanuallyandplacethemintoyoursrcfolder.Its
conventiontocallthefoldersrc,asIwillexplainshortly.
EmacsoffersanErlangmodethatreallyspeedsupyourEditCompileRuncycleandalsocomeswithtemplates.I
mostlyusevi,fordaytodayeditingtasks,butwasalwayslookingforareasontotryemacs.Ivefoundoneinits
Erlangmodeandsuggestyoutryittoo,ifyouhaventdonesoalready.Tryitnow:
emacssrc/hello_sup.erl
hitF10toopenthemenu
typeeforErlang
typectoselectthecompileoption
typecagaintocompilehello_sup.erl(NotetheshortcutCcCkforfuturereference)

Inspectthecompileroutput
pressC0toclosethecompilerwindow
pressCxCctoexitemacs
Powerfullstuffandwevebarelyscratchedthesurface,Iamsure,butfeelfreetofollowalongwithyourfavourite
editor.
BeforewefinallyimplementourOTPbehaviours,wehavetolookatOTPfolderlayout.

OTP folders
TocreatetheOTPfolderstructureusethefollowingcommandfrombash,alternativelyrebarcancreatethemforyou.
Onlysrcandebinarerequired.
mkdirpmy_app/{doc,ebin,priv,include,src,test}
docfordocumentationwhichtypicallyisgeneratedautomatically
ebinyourcompiledcode(.beam)
privresourcefilessuchashtml,imagesetc.
includepublicheaderfilesthatclientcodemayuse(.hrl)
srcsourcecodeandprivateheaderfiles(.erl,.hrl)
testtests(_test.erl)

OTP gen_server
Asthenameimplies,thegen_serverbehaviourhelpsuswritegenericservers.HeresourserverrewrittenasOTP
behaviour.
%%%

%%%@docAnOTPgen_serverexample
%%%@authorHansChristianv.Stockhausen<hcatvst.io>
%%%@end
%%%

module(hello_server).%Nothingnewinthissectionexceptforthe
%nextlinewherewetellthecompilerthat
behaviour(gen_server).%thismoduleimplementsthegen_server
%behaviour.Thecompilerwillwarnusif
define(SERVER,?MODULE).%wedonotprovideallcallbackfunctions
%thebehaviourannounces.Itknowswhat
record(state,{count}).%functionstoexpectbycalling
%gen_server:behaviour_info(callbacks).Tryi
t.
%%

%%APIFunctionExports
%%


export([%HerewedefineourAPIfunctionsasbefore
start_link/0,%startsandlinkstheprocessinonestep
stop/0,%stopsit
say_hello/0,%prints"Hello"tostdout
get_count/0]).%returnsthecountstate
%%

%%gen_serverFunctionExports
%%

export([%Thebehaviourcallbacks
init/1,%initializesourprocess
handle_call/3,%handlessynchronouscalls(withresponse)
handle_cast/2,%handlesasynchronouscalls(noresponse)
handle_info/2,%handlesoutofbandmessages(sentwith!)
terminate/2,%iscalledonshutdown
code_change/3]).%calledtohandlecodechanges
%%

%%APIFunctionDefinitions
%%

start_link()>%start_linkspawnsandlinkstoanew
gen_server:start_link(%processinoneatomicstep.Theparameters:
{local,?SERVER},%nametoregistertheprocessunderlocall
y
?MODULE,%themoduletofindtheinit/1callbackin
[],%whatparameterstopasstoinit/1
[]).%additionaloptionstostart_link
stop()>%Notethatwedonotuse!anymore.Instead
gen_server:cast(%weusecasttosendamessageasynch.to
?SERVER,%theregisteredname.Itisasynchronous
stop).%becausewedonotexpectaresponse.
say_hello()>%Prettymuchthesameasstopaboveexcept
gen_server:cast(%thatwesendtheatomsay_helloinstead.
?SERVER,%Againwedonotexpectaresponsebut
say_hello).%areonlyinterestedinthesideeffect.
get_count()>%Here,ontheotherhand,wedoexpecta
gen_server:call(%response,whichiswhyweusecallto

?SERVER,%synchronouslyinvokeourserver.Thecall
get_count).%blocksuntilwegettheresponse.Notehow
%gen_server:call/2hidesthesend/receive
%logicfromus.Nice.
%%

%%gen_serverFunctionDefinitions
%%

init([])>%thesearethebehaviourcallbacks.init/1is
{ok,#state{count=0}}.%calledinresponsetogen_server:start_lin
k/4
%andweareexpectedtoinitializestate.
handle_call(get_count,_From,#state{count=Count})>
{reply,
Count,%herewesynchronouslyrespondwithCount
#state{count=Count+1}%andalsoupdatestate
}.
handle_cast(stop,State)>%thisisthefirsthandle_caseclausethat
{stop,%dealswiththestopatom.Weinstructthe
normal,%gen_servertostopnormallyandreturn
State%thecurrentStateunchanged.
}%Note:thesemicolonhere....
handle_cast(say_hello,State)>%...becuasethesecondclauseishereto
io:format("Hello~n"),%handlethesay_helloatombyprinting"Hell
o"
{noreply,%again,thisisasynch,sonoreplyandweals
o
#state{count=
State#state.count+1}
}.%updateourstatehere
handle_info(Info,State)>%handle_infodealswithoutofbandmsgs,i
e
error_logger:info_msg("~p~n",[Info]),%msgsthatweren'tsentviacast
{noreply,State}.%orcall.Herewesimplylogsuchmessages.
terminate(_Reason,_State)>%terminateisinvokedbythegen_server
error_logger:info_msg("terminating~n"),%containeronshutdown.
ok.%welogitandacknowledgewithok.
code_change(_OldVsn,State,_Extra)>%calledduringreleaseup/down
{ok,State}.%gradetoupdateinternalstate.

%%
%%InternalFunctionDefinitions
%%
%wedon'thaveany.
TocompileourOTPserver,changeintotheapplicationdirectory(i.e.theoneabovesrc)andrun./rebarcompileor,if
youdecidednottouserebar,runerlcoebinsrc/*.erlinstead.rebarautomaticallycreatestheebindirectory,soifyou
useerlc,youneedtocreateitmanuallyfirst.
LetstakeourOTPserverforatestride.Heresmyoutput:
$>erlpaebin#starterlandpa(pathadd)ourebin
1>hello_server:start_link().%startourserver.
{ok,<0.34.0>}
2>hello_server:say_hello().%trythesay_hello/0APIfunction
Hello
ok
3>hello_server:get_count().%observehowtheserverkeepstrack
1
4>hello_server:get_count().%ofstate
2
5>hello_server!stop.%let'ssendanoutofbandmessage
=INFOREPORT===%here'stheoutputfromerror_logger
stop
stop
6>hello_server:stop().%nowlet'scallourstop/0APIfunction
=INFOREPORT===%thisistheoutputfromterminate
terminating
ok
7>whereis(hello_server).%andaswecanseetheprocessnolonger
undefined%exists.
8>
So,whathavewegainedbyturningournonOTPversionintoanOTPgen_server?
Firstofall,anyonefamiliarwithgen_servercanquicklyunderstandhowourcodeisorganized,whatAPIwe
exposeandwheretolookforimplementationdetails.Thisisthepowerofpatterns.
Threedistinctmessagingmechanisms:synchronous,asynchronousandoutofband.
Buttheresmore.If,asthefollowingsessionshows,westarttheSASLapplication(SystemApplicationSupport
Libraries)andthenforceacrash,wegetavaluableerrorreport,somethingournonOTPversiondoesnotbenefitfrom.
1>application:start(sasl).%let'sstartSASL
ok
2>

=PROGRESSREPORT====%I'vedeletedallstartupmes
sages
application:sasl%exceptforthelastonehere
started_at:nonode@nohost%forbrevity.SASLisup.
2>hello_server:start_link().%let'sstartourserver
{ok,<0.44.0>}
3>whereis(hello_server).%andcheckthatitwasregist
ered
<0.44.0>
4>gen_server:cast(hello_server,crash).%nowlet'scrashit
=INFOREPORT====17Dec2012::11:00:56===%excellent,gen_servercalled
terminate
terminating%sowecouldcleanup,close
sockets,etc.
=ERRORREPORT====17Dec2012::11:00:56===%whatfollowsisaSASLrepor
tthatwe
**Genericserverhello_serverterminating%forfree,simplybyusingOT
P.
**Lastmessageinwas{'$gen_cast',crash}
**WhenServerstate=={state,0}
**Reasonfortermination==%vahahere'swhatwent
wrong
**{function_clause,[{hello_server,handle_cast,[crash,{state,0}]},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
=CRASHREPORT====17Dec2012::11:00:56===%andhere'sevenmoreinfoon
theenvironent
crasher:
initialcall:hello_server:init/1
pid:<0.44.0>
registered_name:hello_server
exceptionexit:{function_clause,
[{hello_server,handle_cast,[crash,{state,0}]},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
infunctiongen_server:terminate/6
ancestors:[<0.32.0>]
messages:[]
links:[<0.32.0>]
dictionary:[]
trap_exit:false
status:running
heap_size:377
stack_size:24

reductions:142
neighbours:
neighbour:[{pid,<0.32.0>},
{registered_name,[]},
{initial_call,{erlang,apply,2}},
{current_function,{io,wait_io_mon_reply,2}},
{ancestors,[]},
{messages,[]},
{links,[<0.26.0>,<0.44.0>]},
{dictionary,[]},
{trap_exit,false},
{status,waiting},
{heap_size,2584},
{stack_size,30},
{reductions,6595}]
**exceptionexit:function_clause
infunctionhello_server:handle_cast/2
calledashello_server:handle_cast(crash,{state,0})
incallfromgen_server:handle_msg/5
incallfromproc_lib:init_p_do_apply/3
5>whereis(hello_server).%ourprocesscrashed,butat
leastthe
undefined%reportabovegivesusanide
awhy.

OTP supervisor
Heresthecodethatrebargeneratedforus.Itisasupervisortemplateandnotyetcustomized.

module(hello_sup).
behaviour(supervisor).
%%API
export([start_link/0]).
%%Supervisorcallbacks
export([init/1]).
%%Helpermacrofordeclaringchildrenofsupervisor
define(CHILD(I,Type),{I,{I,start_link,[]},permanent,5000,Type,
[I]}).
%%===================================================================
%%APIfunctions
%%===================================================================
start_link()>
supervisor:start_link({local,?MODULE},?MODULE,[]).
%%===================================================================
%%Supervisorcallbacks
%%===================================================================
init([])>
{ok,{{one_for_one,5,10},[]}}.
Wow,thatsshort!Wow,whatonearthdoesitmean?Weregoingtodissectitinamomentandthenfillinthegapto
makethistemplateworkwithourgen_server,butevenifwedontunderstandthedetailsyettheresalotwe
understandalready.
ThismoduleimplementstheOTPsupervisorbehaviour
IthasoneAPIfunctionstart_link/0andonebehaviourcallbackfunctioninit/1.
Thereisonlyonesinglefunctioncallinthisentirelisting(supervisor:start_link/3)
init/1,whereweexpectthemagictohappen,onlyreturnsatuple.
Well,init/1iswherethemagichappens.ButincontrasttoournonOTPsupervisor,wherewehadtocodethelogicto
monitorandrestartoursever,weareusingapurelydeclarative,albeitstillcrypticapproach,informofthetuple,to
configurethesupervisorcontainer.
Whatsgoingonwiththatdefine(CHILD(I,Type),...)?Itisamacro,justlikethefamilardefine(SERVER,?
MODULE).macro,exceptthatthisonetakestwoarguments.Youusethismacrobyprefixingitwith?andby
providingvaluesforIandType.So,forexample,?CHILD(hello_server,worker)wouldbeexpandedto{hello_server,
{hello_server,start_link,[]},permanent,5000,worker,[hello_server]}atcompiletime.Butthemacroisntused
anywhereinourcodeyet.Wherearewesupposedtouseit?

Letstakealookatthesupervisortemplateemacsgeneratesforus.Weareonlyinterestedintheinit/1function,which
iswhyIomittedtherest.Buttrytouseemacstogenerateasupervisortemplateyourself.Todiscoverwhatgoodness
Imhidinghit:F10,e,S,0,whereestandsforErlang,SforSkeletonsand0selectsthesupervisortemplate.Heres
whatIgot:
init([])>
RestartStrategy=one_for_one,
MaxRestarts=1000,
MaxSecondsBetweenRestarts=3600,
SupFlags={RestartStrategy,MaxRestarts,MaxSecondsBetweenRestarts},
Restart=permanent,
Shutdown=2000,
Type=worker,
AChild={'AName',{'AModule',start_link,[]},
Restart,Shutdown,Type,['AModule']},
{ok,{SupFlags,[AChild]}}.
Thisisprobablyeasiertounderstand,thanwhatrebargeneratedforus.Althoughitshould,ofcourse,atleast
stucturally,boildowntothesameconfigurationtuple.LetssubstitutebothatomsANameandAModulewith
hello_serverandresolvethelasttuplefully.Iamgoingtoinsertsomelinebreaksandcommentstomakeiteasierto
seewhatgoeswhere.

{ok,%ok,supervisorhere'swhatwewantyoutodo
{
{%Globalsupervisoroptions
one_for_one,%usetheoneforonerestartstrategy
1000,%andallowamaximumof1000restarts
3600%perhourforeachchildprocess
},
[%Thelistofchildprocessesyoushouldsupervise
{%Weonlyhaveone
hello_server,%Registeritunderthenamehello_server
{%Here'showtofindandstartthischild'scode
hello_server,%*themoduleiscalledhello_server
start_link,%*thefunctiontoinvokeiscalledstart_link
[]%*andhere'sthelistofdefaultparametersto
use
},
permanent,%childshouldrunpermantenly,restartoncrash
2000,%givechild2sectocleanuponsystemstop,th
enkill
worker%FYI,thischildisaworker,notasupervisor
[hello_server]%thesearethemodulestheprocessuses
}
]
}
}.
Ileaveituptoyoutodecideontheformatyouprefer.Youcanuserebarscompactnotation,emacsverbosetemplate
oreventheresolvedsnippetaboveinyoursupervisorsinit/1.Asanexercise,pleaseimplementyoursupervisornow
andcompileit.However,beforewetestit,letslookattheparametersweusedinthesnippetabove.
one_for_one:Thisrestartstrategyinstructsthesupervisortorestartthecrashedchildprocessonly.Inourcase
thereisonlyone,butifthereweremoresupervisedchildprocesses,theywouldnotbeaffected.Incontrast,a
restartstrategyofone_for_allautomaticallyrestartsallchildrenifonecrashes.Forautonomouschildprocesses
useone_for_one.If,ontheotherhand,childprocessesformatightlycoupledsubsystemitmaybebettertorestart
theentiresubsystemwithone_for_all.Consultman3erlsupervisororthispage
(http://www.erlang.org/doc/man/supervisor.html)foradditionalinformation.Inparticular,lookatthe
simple_one_for_onestrategywhichallowsyoutouseasupervisorasaprocessfactory.
permanent:Thistellsthesupervisorthatthechildshouldalwaysberestarted(subjecttotheglobalrestartrules).
OtherRestartvaluesaretemporaryandtransient.temporaryprocessesareneverrestartedbuttransientchild
processesareiftheyterminateabnormally(i.e.theycrashed).Forourusecasepermanentmakessense,butinthe
caseofaprocessfactory,transientortemporarymaybemoreapplicable.
worker:Herewewanttosuperviseagen_serveraworkerbutitiscommontobuildsupervisorhierarchies.
Settingthisvaluetosupervisorinsteadinformsthesupervisorthatthechildisalsoasupervisor.Thisistohelpit
managechildprocessesoptimally.
LetstryourOTPsupervisor.

1>hello_sup:start_link().%let'sstartourrootsupervisor
{ok,<0.34.0>}
2>whereis(hello_server).%andensurethatitstartedoutch
ildprocess
<0.35.0>
3>hello_server:say_hello().%nowlet'scallthechildAPI
Hello
ok
4>hello_server:get_count().%andverifythatallworksasbefo
re
1
5>gen_server:cast(hello_server,crash).%timetocrashtheworkerprocess
=INFOREPORT====%good,terminateisstillbeingca
lled
terminating
=ERRORREPORT====17Dec2012::13:55:51===
**Genericserverhello_serverterminating
**Lastmessageinwas{'$gen_cast',crash}
**WhenServerstate=={state,2}
**Reasonfortermination==%andvhereweseewhyitcrash
ed
**{function_clause,[{hello_server,handle_cast,[crash,{state,2}]},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
ok
6>whereis(hello_server).%butlook,anewprocesswasalread
ystarted
<0.40.0>
7>hello_server:get_count().%obviously,withanewstate
0
8>hello_server:stop().%let'scallourAPIstopfunction
=INFOREPORT===
terminating%lookinggood
ok
9>whereis(hello_server).%butoursupervisorstartedanother
server.
<0.44.0>
10>
Itworksasdesigned,although,perhaps,notasexpected.Thecalltostop_server/0stopsourprocess,butthesupervisor
immediatelystartsanewinstance.WecouldchangetheRestartparameterinoursupervisorsChildspecificationto
transient,butthenweareleftwithachildlesssupervisor.Idontthinkthisisaproblemthough,butrathertheresultof
oursomewhatarbitraryHelloscenario.LetsturnourattentionthereforetoOTPapplicationstolearnhowtostart
andstopapplicationsasawhole.

OTP application
Wehavealreadyseenanexampleofanapplicationinthispost:SASL,whichwelaunchedwithapplication:start(sasl).
Nowitistimetofindouthowwecanbundleourrootsupervisorandourserverinanapplicationsothatwecanstartit
inthesamemanner.
Unlikeourgen_serverandsupervisorimplementation,theOTPapplicationbehaviourrequirestwofiles.A
configurationfile,conventiallynamed,appname.appandanErlangsourcefilecalledappname_app.erl.Inourcase
theseare:
ebin/hello.app
src/hello_app.erl
Notethatthehello.appfileisplacedintotheebindirectory.Thismakessense.The.appfiletellsErlanghowtolaunch
ourapplication.Sincewemightonlyshipourappscompiled.beamfiles,notitssource,thisiswherethe.appfile
naturallybelongs.
Asarebarusersyouwillnoticethatthereexistsalsoasrc/hello.app.srcfile,asmentionedearlier.Thisfileservesasa
templateforyourebin/hello.appfilewhenyourun./rebarcompile.
Hereismy.erland.appfileasgeneratedbyrebar.
module(hello_app).
behaviour(application).
%%Applicationcallbacks
export([start/2,stop/1]).
%%===================================================================
%%Applicationcallbacks
%%===================================================================
start(_StartType,_StartArgs)>
hello_sup:start_link().
stop(_State)>
ok.
Thisisthehello_app.erlfilethatimplementstheOTPapplicationbehaviour.NeedIsaymore?
{application,hello,
[{description,[]},
{vsn,"1"},
{registered,[]},
{applications,[kernel,stdlib]},
{mod,{hello_app,[]}},
{env,[]},
{modules,[hello_app,hello_server,hello_sup]}]}.

ThisistheconfigurationfilethattellsErlanghowtostartourapplication.Itsjustatuple.Thefirstelementistheatom
application,thesecondtheapplicationnameandthethridalistofkeyvaluetuples.Ifyouarenotusingrebardont
forgetthefullstopattheend.Letsbrieflylookatthekeyvaluepairs.
description:WeshouldchangethistoThehelloOTPtutorialapp
vsn:Ourapplicationsversion.Itscommontousex.y.z,butfornow,rebarsdefaultisfine.
registered:Isalistofnamesourappregisteresitselfunder.Thisistypicallyonlytherootsupervisorsname.In
ourcaseweshouldaddhello.suphere,butitsnotrequired.
applications:Liststheapplicationsourappdependson.Everyapprequireskernelandsdlibtobeupandrunning.
Trytoaddsaslifyoulikeandseewhathappenswhenyoutrytostartyourapplication.
mod:TellsErlangwhichmodulecontainsourapplicationbehaviour.
env:Isalistofenviromentalvariablesyoumaywishtopasstoyourapplication.
modules:Isthelistofmodulesourapplicationconsistsof.
ItistimetostartourapplicationfromtheErlangshell.
1>application:start(hello).
ok
2>whereis(hello_sup).
<0.37.0>
3>whereis(hello_server).
<0.38.0>
4>hello_server:say_hello().
Hello
ok
5>application:stop(hello).
=INFOREPORT====17Dec2012::15:37:47===
application:hello
exited:stopped
type:temporary
ok
6>whereis(hello_sup).
undefined
7>whereis(hello_server).
undefined
Great,nowwecanstartandstopourapplicationsimplybyname.Buttheresmore.DoyourememberhowSASLs
extendedreportingbecameautomaticallyavailablewhenweturnedournonOTPserverintoagen_server?Trytorun
theshellsessionaboveanothertime,butfirststarttheApplicationMonitorusingappmon:start(),then,onceyouve
startedthehelloapp,clickonthehellobuttontodisplaytheprocesshierarchy.Prettynifty,dontyouthink?

Up next?
InterestedinapplyingyourOTPskills?Havealookaterlangdnsatmygithub
repositoryhttps://github.com/hcvst/erlangdns(https://github.com/hcvst/erlangdns).

(http://www.patternmatched.com/about/pmtcareers/)

(http://www.patternmatched.com/about/pmtcareers/)
(http://www.patternmatched.com/about/pmtcareers/)

Like

Tweet(http://twitter.com/share)

16Comments

Bot.co.zaBlog

Share

Recommend 4

showerst 3yearsago

Ithinkyouhaveaslighttypotheline
Savethecodeabovetoafilenamedno_top.erl.
shouldprobablyread:
Savethecodeabovetoafilenamedno_otp.erl.

Reply Share

hcvst

Mod >showerst

3yearsago

Thankyou.Ifixedit.

Reply Share

LeeWeiYeong>showerst 3yearsago

bump

Login

SortbyBest

Jointhediscussion

346

Reply Share

Colorado 3monthsago

Reallyappreciatethispostthankyou!

Reply Share

Stephan 3monthsago

Greatpost(still).Ifyouareon17insteadofcalling``appmon:start().``youcanuse
``observer:start().``OnethingthatI'venoticedisthatwhencrashingtheserverbye.g.callinga
functionthatisnotimplemented/exported,e.g.``hello_server:get_foo().``thenitremainsdownand
thesupervisorseemsnottobeabletorestartit.Isthisthesupposedbehaviour?

Reply Share

Asgeir 2yearsago

Hi,
inno_otp,you'renotobligedtoexportinit/0,itworkswithoutexportingit.Ibelievethereisno
obligationtoexportfunctionsyouwanttospawn.
Note:I've"forgotten"toexportinit/0andthecodestillworked.

Reply Share

JuliusBeckmann 2yearsago

Hifolks,
iranintoaproblemandfoundasolutionthatmighthelpsome:
Thislinedoesnotworkasexpected,becausethesupervisorcrashesalso.
"5>gen_server:cast(hello_server,crash).%timetocrashtheworkerprocess"
Itseemstodoso,becausetheprocessesarelinkedtotheerlangshell.Whatworkedwas:
"catchgen_server:cast(hello_server,crash)."
Myerlangversion:"ErlangR15B01(erts5.9.1)[source][smp:4:4][asyncthreads:0][kernel
poll:false]".
Thanksforthisveryinformativehowto!

Reply Share

2yearsago

Greatpost!Thankyou.

Reply Share

nikhilmarathe 3yearsago

Ihaven'tgotintoErlangyet,butIreallylikedyourwritingstyle!

Reply Share

kblake 3yearsago

Thisisgreat!

Reply Share

hcvst

Mod >kblake

3yearsago

Thanksalot.Gladyoufoundithelpful.IprettymuchwrotewhatIthinkwouldhavehelped
meinitially,butcreditgoestotheOTPbooktoo,ofcourse.Erlang'sgreatandjustalotof

fun.

Reply Share

LeeWeiYeong 3yearsago

Inyour'LetstakeourOTPserverforatestride.Heresmyoutput:'section,youmissedout
`c(hello_server).`tofirstloadyourhello_servercodebeforeusage.

Reply Share

hcvst

Mod >LeeWeiYeong

3yearsago

Hi,thanksforyourcomment.Sincewecompiledtheservermoduleintheprevious
paragraphit'senoughtoinvoke"erlpaebin".Tabcompletiondoesnotworksoyouhaveto
typehello_server:start_link()thefirsttimebutthenerlshouldfindthe.beamfileinebin.
Regards,HC
1

Reply Share

LeeWeiYeong>hcvst 3yearsago

Thanksfortheclarification.

Reply Share

KuldeepSinhChauhan 3yearsago

Thankyou.Veryhandywriteupwithpracticalsuggestionsaboutusing"rebar"and"emacs".Forme
it'saverytimelyread,IhavejustcompletedreadingfirsthalfofErlangOTPbook(yoursuggestion
onusingrebarandemacswillmakedifferencefurther).

Reply Share

hcvst

Mod >KuldeepSinhChauhan

3yearsago

Thankyouforyourcomment.Yes,rebarandemacsaregreat.WhatIfoundpleasantly
surprisingisthataddingemacstomytoolboxdidn'tconfusemy"vifingers"much.Ihad
expectedtostrugglewiththekeybindingswhenchangingbackandforthbetweenviand
emacsbutthatisnotthecase.

Reply Share

WHAT'STHIS?

ALSOONBOT.CO.ZABLOG

Sourcecodeeditorwithsyntax
highlightingforZotonicblogtinyMCE

MakeZotonicyoursHowtoinstalla
customtheme

2comments3yearsago

2comments3yearsago

AquaponicsFirststeps

AZotonictutorialoncustomtypes

10October199010:11:1210October1990

Next post (/en/article/366/a-beginner-s-zotonic-tutorial-on-custom-types)


Keywords: Erlang(/en/by_keyword/351/erlang) OTP(/en/by_keyword/352/otp) Tutorial(/en/by_keyword/370/tutorial)

(/en/page/354/otphierarchy)

WEBSITEPOWEREDBYZOTONIC(HTTP://ZOTONIC.COM)0.9.0.

You might also like