-
Notifications
You must be signed in to change notification settings - Fork 5
/
event-handlers.html
527 lines (390 loc) · 36.3 KB
/
event-handlers.html
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
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="keywords" content="Erlang, OTP, behaviour, gen_event, generic, Event Handler, callback, protocol, module, curling" />
<meta name="description" content="Learning about event handlers and their OTP equivalent (gen_event) in the context of writing a system for a curling game." />
<meta name="google-site-verification" content="mi1UCmFD_2pMLt2jsYHzi_0b6Go9xja8TGllOSoQPVU" />
<link rel="stylesheet" type="text/css" href="static/css/screen.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/sh/shCore.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/sh/shThemeLYSE2.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/print.css" media="print" />
<link href="rss" type="application/rss+xml" rel="alternate" title="LYSE news" />
<link rel="icon" type="image/png" href="favicon.ico" />
<link rel="apple-touch-icon" href="static/img/touch-icon-iphone.png" />
<link rel="apple-touch-icon" sizes="72x72" href="static/img/touch-icon-ipad.png" />
<link rel="apple-touch-icon" sizes="114x114" href="static/img/touch-icon-iphone4.png" />
<title>Event Handlers | Learn You Some Erlang for Great Good!</title>
</head>
<body>
<div id="wrapper">
<div id="header">
<h1>Learn you some Erlang</h1>
<span>for great good!</span>
</div> <!-- header -->
<div id="menu">
<ul>
<li><a href="content.html" title="Home">Home</a></li>
<li><a href="faq.html" title="Frequently Asked Questions">FAQ</a></li>
<li><a href="rss" title="Latest News">RSS</a></li>
<li><a href="static/erlang/learn-you-some-erlang.zip" title="Source Code">Code</a></li>
</ul>
</div><!-- menu -->
<div id="content">
<div class="noscript"><noscript>Hey there, it appears your Javascript is disabled. That's fine, the site works without it. However, you might prefer reading it with syntax highlighting, which requires Javascript!</noscript></div>
<h2>Event Handlers</h2>
<h3><a class="section" name="handle-this">Handle This! *pumps shotgun*</a></h3>
<p>There is a certain thing that I've avoided getting into in a few of the previous examples. If you look back at the <a class="chapter" href="designing-a-concurrent-application.html">reminder app</a>, you'll see that I somehow mentioned that we could notify clients, whether they were IM, mail, etc. In the <a class="chapter" href="finite-state-machines.html">previous chapter</a>, our trading system used <code>io:format/2</code> to notify people of what was going on.</p>
<p>You can probably see the common link between both cases. They're all about letting people (or some process or application) know about an event that happened at some point in time. In one case, we only output the results while in the other, we took the Pid of subscribers before sending them a message.</p>
<p>The output approach is minimalist and can not be extended with ease. The one with subscribers is certainly valid. In fact, it's pretty useful when each of the subscribers has a long-running operation to do after receiving an event. In simpler cases, where you do not necessarily want a standby process waiting for events for each of the callbacks, a third approach can be taken.</p>
<p>This third approach simply takes a process which accepts functions and lets them run on any incoming event. This process is usually called an <em>event manager</em> and it might end up looking a bit like this:</p>
<img class="center explanation" src="static/img/event-manager.png" width="318" height="171" alt="Shows a bubble labeled 'your server', another one labeled 'event manager'. An arrow (representing an event) goes from your server to the event manager, which has the event running in callback functions (illustrated as f, y and g)" />
<p>Doing things that way has a few advantages:</p>
<ul>
<li>If your server has many subscribers, it can keep going because it only needs to forward events once</li>
<li>If there is a lot of data to be transferred, it's only done once and all callbacks operate on that same instance of the data</li>
<li>You don't need to spawn processes for short lived tasks</li>
</ul>
<p>And of course there are a few downsides too:</p>
<ul>
<li>If all functions need to run for a long time, they're going to block each other. This can be prevented by actually having the function forward the event to a process, basically turning the event manager as an event forwarder (something similar to what we did for the reminder app)</li>
<li>In fact, a function that loops indefinitly can prevent any new event from being handled until something crashes.</li>
</ul>
<p>There is a way to solve these downsides, which is a bit underwhelming. Basically, you have to turn the event manager approach into the subscriber one. Luckily, the event manager approach is flexible enough to do it with ease and we'll see how to do it later in this chapter.</p>
<p>I usually write a very basic version of the OTP behaviour we'll see in pure Erlang beforehand, but in this case, I'll instead come straight to the point. Here comes <code>gen_event</code>.</p>
<h3><a class="section" name="generic-event-handlers">Generic Event Handlers</a></h3>
<p>The <code>gen_event</code> behaviour differs quite a bit from the <code>gen_server</code> and <code>gen_fsm</code> behaviours in that you are never really starting a process. The whole part I've described above about 'accepting a callback' is the reason for this. The <code>gen_event</code> behaviour basically runs the process that accepts and calls functions, and you only provide a module with these functions. This is to say, you have nothing to do with regards to event manipulation except give your callback functions in a format that pleases the <em>event manager</em>. All managing is done for free; you only provide what's specific to your application. This is not really surprising given OTP is, again, all about separating what's generic from specific.</p>
<p>This separation, however, means that the standard <code>spawn -> init -> loop -> terminate</code> pattern will only be applied to event handlers. Now if you recall what has been said before, event handlers are a bunch of functions running in the manager. This means the currently presented model:</p>
<img class="center explanation" src="static/img/common-pattern.png" width="381" height="140" alt="common process pattern: spawn -> init -> loop -> exit"/>
<p>Switches to something more like this for the programmer:</p>
<img class="center explanation" src="static/img/event-handler-pattern.png" width="521" height="169" alt=" spawn event manager -> attach handler -> init handler -> loop (with handler calls) -> exit handlers" />
<p>Each event handler can hold its own state, carried around by the manager for them. Each event handler can then take the form:</p>
<img class="center explanation" src="static/img/event-handler-pattern-local.png" width="251" height="214" alt="init --> handle events + special messages --> terminate" />
<p>This is nothing too complex so let's get it on with the event handlers' callbacks themselves.</p>
<h4>init and terminate</h4>
<p>The init and terminate functions are similar to what we've seen in the previous behaviours with servers and finit state machines. <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#init/1">init/1</a></code> takes a list of arguments and returns <code>{ok, State}</code>. Whatever happens in <code>init/1</code> should have its counterpart in <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#terminate/2">terminate/2</a></code>.</p>
<h4>handle_event</h4>
<p>The <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#handle_event/2">handle_event(Event, State)</a></code> function is more or less the core of <code>gen_event</code>'s callback modules. It works like <code>gen_server</code>'s <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#handle_cast/2">handle_cast/2</a></code> in that it works asynchronously. It differs with regards to what it can return though:</p>
<ul>
<li><code>{ok, NewState}</code></li>
<li><code>{ok, NewState, hibernate}</code>, which puts the event manager itself into hibernation until the next event</li>
<li><code>remove_handler</code></li>
<li><code>{swap_handler, Args1, NewState, NewHandler, Args2}</code></li>
</ul>
<img class="right" src="static/img/hibernate.png" width="282" height="229" alt="A sleeping polar bear with a birthday hat" title="Happy Bearthday! Hibearnate! Bearrible puns!" />
<p>The tuple <code>{ok, NewState}</code> works in a way similar to what we've seen with <code>gen_server:handle_cast/2</code>; it simply updates its own state and doesn't reply to anyone. In the case of <code>{ok, NewState, hibernate}</code> it is to be noted that the whole event manager is going to be put in hibernation. Remember that event handlers run in the same process as their manager. Then <code>remove_handler</code> drops the handler from the manager. This can be useful whenever your event handler knows its done and it has nothing else to do. Finally, there's <code>{swap_handler, Args1, NewState, NewHandler, Args2}</code>. This one is not used too frequently, but what it does is remove the current event handler and replace it with a new one. This is done by first calling <code>CurrentHandler:terminate(Args1, NewState)</code> and removing the current handler, then adding a new one by calling <code>NewHandler:init(Args2, ResultFromTerminate)</code>. This can be useful in the cases where you know some specific event happened and you're better of giving control to a new handler. This is likely the kind of thing where you'll simply know when you need it. Again, it's not that frequently used.</p>
<p>All incoming events can come from <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#notify/2">gen_event:notify/2</a></code> which is asynchronous like <code>gen_server:cast/2</code> is. There is also <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#sync_notify/2">gen_event:sync_notify/2</a></code> which is synchronous. This is a bit funny to say, because <code>handle_event/2</code> remains asynchronous. The idea here is that the function call only returns once all event handlers have seen and treated the new message. Until then, the event manager will keep blocking the calling process by not replying.</p>
<h4>handle_call</h4>
<p>This is similar to a <code>gen_server</code>'s <code>handle_call</code> callback, except that it can return <code>{ok, Reply, NewState}</code>, <code>{ok, Reply, NewState, hibernate}</code>, <code>{remove_handler, Reply}</code> or <code>{swap_handler, Reply, Args1, NewState, Handler2, Args2}</code>. The <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#call/3">gen_event:call/3-4</a></code> function is used to make the call.</p>
<p>This raises a question. How does this work when we have something like 15 different event handlers? Do we expect 15 replies or just one that contains them all? Well, in fact we'll be forced to choose only one handler to reply to us. We'll get into the details of how this is done when we actually see how to attach handlers to our event manager, but if you're impatient, you can look at how the function <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#add_handler/3">gen_event:add_handler/3</a></code> works to try to figure it out.</p>
<h4>handle_info</h4>
<p>The <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#handle_info/2">handle_info/2</a></code> callback is pretty much the same as <code>handle_event</code> (same return values and everything), with the exception that it only treats out of band messages, such as exit signals, messages sent directly to the event manager with the <code>!</code> operator, etc. It has use cases similar to those of <code>handle_info</code> in <code>gen_server</code> and in <code>gen_fsm</code>.</p>
<h4>code_change</h4>
<p>Code change works in exactly the same manner as it does for <code>gen_server</code>s, except it's for each individual event handler. It takes 3 arguments, <var>OldVsn</var>, <var>State</var>, and <var>Extra</var>, which are in order, the version number, the current handler's state and data we can ignore for now. All it needs to do is return <code>{ok, NewState}</code>.</p>
<h3><a class="section" name="it-s-curling-time">It's Curling Time!</a></h3>
<p>With the callbacks seen, we can start looking at implementing something with <code>gen_event</code>. For this part of the chapter, I've chosen to make a set of event handlers used to track game updates of one of the most entertaining sports in the world: curling.</p>
<p>If you've never seen or played curling before (which is a shame!), the rules are relatively simple:</p>
<img class="center explanation" src="static/img/curling-ice.png" width="381" height="150" alt="A top view of a curling ice/game" />
<p>You have two teams and they try to send a <a class="docs" href="static/img/curling-stone.png">curling stone</a> sliding on the ice, trying to get to the middle of the red circle. They do this with 16 stones and the team with the stone closest to the center wins a point at the end of the round (called an <em>end</em>). If the team has the two closest stones, it earns two points, and so on. There are 10 ends and the team with the most points at the end of the 10 ends wins the game.</p>
<p>There are more rules making the game more fascinating, but this is a book on Erlang, not extremely fascinating winter sports. If you want to learn more about the rules, I suggest you head up to the <a class="external" href="http://en.wikipedia.org/wiki/Curling">Wikipedia article on curling</a>.</p>
<p>For this entirely real-world-relevant scenario, we'll be working for the next winter Olympic Games. The city where everything happens are just done building the arena where the matches will take place and they're working on getting the scoreboard ready. It turns out that we have to program a system that will let some official enter game events, such as when a stone has been thrown, when a round ends or when a game is over, and then route these events to the scoreboard, a stats system, news reporters' feeds, etc.</p>
<p>Being as clever as we are, we know this is a chapter on gen_event and deduce we will likely use it to accomplish our task. We won't implement all the rules given this is more of an example, but feel free to do so when we're done with the chapter. I promise not to be mad.</p>
<p>We'll start with the scoreboard. Because they're installing it right now, we'll make use of a fake module that would usually let us interact with it, but for now it'll only use standard output to show what's going on. This is where <a class="source" href="static/erlang/curling_scoreboard_hw.erl">curling_scoreboard_hw.erl</a> comes in:</p>
<pre class="brush:erl">
-module(curling_scoreboard_hw).
-export([add_point/1, next_round/0, set_teams/2, reset_board/0]).
%% This is a 'dumb' module that's only there to replace what a real hardware
%% controller would likely do. The real hardware controller would likely hold
%% some state and make sure everything works right, but this one doesn't mind.
%% Shows the teams on the scoreboard.
set_teams(TeamA, TeamB) ->
io:format("Scoreboard: Team ~s vs. Team ~s~n", [TeamA, TeamB]).
next_round() ->
io:format("Scoreboard: round over~n").
add_point(Team) ->
io:format("Scoreboard: increased score of team ~s by 1~n", [Team]).
reset_board() ->
io:format("Scoreboard: All teams are undefined and all scores are 0~n").
</pre>
<p>So this is all the functionality the scoreboard has. They usually have a timer and other awesome functionalities, but whatever. Seems like the Olympics committee didn't feel like having us implementing trivialities for a tutorial.</p>
<p>This hardware interface lets us have a little bit of design time to ourselves. We know that there are a few events to handle for now: adding teams, going to the next round, setting the number of points. We will only use the <code>reset_board</code> functionality when starting a new game and won't need it as part of our protocol. The events we need might take the following form in our protocol:</p>
<ul>
<li><code>{set_teams, TeamA, TeamB}</code>, where this is translated to a single call to <code>curling_scoreboard_hw:set_teams(TeamA, TeamB)</code>;</li>
<li><code>{add_points, Team, N}</code>, where this is translated to <var>N</var> calls to <code>curling_scoreboard_hw:add_point(Team)</code>;</li>
<li><code>next_round</code>, which gets translated to a single call with the same name.</li>
</ul>
<p>We can start our implementation with this basic event handler skeleton:</p>
<pre class="brush:erl">
-module(curling_scoreboard).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
terminate/2]).
init([]) ->
{ok, []}.
handle_event(_, State) ->
{ok, State}.
handle_call(_, State) ->
{ok, ok, State}.
handle_info(_, State) ->
{ok, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
</pre>
<p>This is a skeleton that we can use for every <code>gen_event</code> callback module out there. For now, the scoreboard event handler itself won't need to do anything special except forward the calls to the hardware module. We expect the events to come from <code>gen_event:notify/2</code>, so the handling of the protocol should be done in <code>handle_event/2</code>. The file <a class="source" href="static/erlang/curling_scoreboard.erl">curling_scoreboard.erl</a> shows the updates:</p>
<pre class="brush:erl">
-module(curling_scoreboard).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
terminate/2]).
init([]) ->
{ok, []}.
handle_event({set_teams, TeamA, TeamB}, State) ->
curling_scoreboard_hw:set_teams(TeamA, TeamB),
{ok, State};
handle_event({add_points, Team, N}, State) ->
[curling_scoreboard_hw:add_point(Team) || _ <- lists:seq(1,N)],
{ok, State};
handle_event(next_round, State) ->
curling_scoreboard_hw:next_round(),
{ok, State};
handle_event(_, State) ->
{ok, State}.
handle_call(_, State) ->
{ok, ok, State}.
handle_info(_, State) ->
{ok, State}.
</pre>
<p>You can see the updates done to the <code>handle_event/2</code> function. Trying it:</p>
<pre class="brush:eshell">
1> c(curling_scoreboard_hw).
{ok,curling_scoreboard_hw}
2> c(curling_scoreboard).
{ok,curling_scoreboard}
3> {ok, Pid} = gen_event:start_link().
{ok,<0.43.0>}
4> gen_event:add_handler(Pid, curling_scoreboard, []).
ok
5> gen_event:notify(Pid, {set_teams, "Pirates", "Scotsmen"}).
Scoreboard: Team Pirates vs. Team Scotsmen
ok
6> gen_event:notify(Pid, {add_points, "Pirates", 3}).
ok
Scoreboard: increased score of team Pirates by 1
Scoreboard: increased score of team Pirates by 1
Scoreboard: increased score of team Pirates by 1
7> gen_event:notify(Pid, next_round).
Scoreboard: round over
ok
8> gen_event:delete_handler(Pid, curling_scoreboard, turn_off).
ok
9> gen_event:notify(Pid, next_round).
ok
</pre>
<p>A few things are going on here. The first of them is that we're starting the <code>gen_event</code> process as a standalone thing. We then attach our event handler to it dynamically with <code>gen_event:add_handler/3</code>. This can be done as many times as you want. However, as mentioned in the <code>handle_call</code> part earlier, this might cause problems when you want to work with a particular event handler. If you want to call, add or delete a specific handler when there's more than one instance of it, you'll have to find a way to uniquely identify it. My favorite way of doing it (one that works great if you don't have anything more specific in mind) is to just use <code>make_ref()</code> as a unique value. To give this value to the handler, you add it by calling <code>add_handler/3</code> as <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#add_handler/3">gen_event:add_handler(Pid, {Module, Ref}, Args)</a></code>. From this point on, you can use <code>{Module, Ref}</code> to talk to that specific handler. Problem solved.</p>
<img class="left" src="static/img/curling-stone.png" width="169" height="120" alt="A curling stone" />
<p>Anyway, you can then see that we send messages to the event handler, which successfully calls the hardware module. We then remove the handler. Here, <code>turn_off</code> is an argument to the <code>terminate/2</code> function, which our implementation currently doesn't care about. The handler is gone, but we can still send events to the event manager. Hooray.</p>
<p>One awkward thing with the code snippet above is that we're forced to call the <code>gen_event</code> module directly and show everyone what our protocol looks like. A better option would be to provide an <a class="source" href="static/erlang/curling.erl" title="curling abstraction module">abstraction module</a> on top of it that just wraps all we need. This will look a lot nicer to everyone using our code and will, again, let us change the implementation if (or when) we need to do it. It will also let us specify what handlers are necessary to include for a standard curling game:</p>
<pre class="brush:erl">
-module(curling).
-export([start_link/2, set_teams/3, add_points/3, next_round/1]).
start_link(TeamA, TeamB) ->
{ok, Pid} = gen_event:start_link(),
%% The scoreboard will always be there
gen_event:add_handler(Pid, curling_scoreboard, []),
set_teams(Pid, TeamA, TeamB),
{ok, Pid}.
set_teams(Pid, TeamA, TeamB) ->
gen_event:notify(Pid, {set_teams, TeamA, TeamB}).
add_points(Pid, Team, N) ->
gen_event:notify(Pid, {add_points, Team, N}).
next_round(Pid) ->
gen_event:notify(Pid, next_round).
</pre>
<p>And now running it:</p>
<pre class="brush:eshell">
1> c(curling).
{ok,curling}
2> {ok, Pid} = curling:start_link("Pirates", "Scotsmen").
Scoreboard: Team Pirates vs. Team Scotsmen
{ok,<0.78.0>}
3> curling:add_points(Pid, "Scotsmen", 2).
Scoreboard: increased score of team Scotsmen by 1
Scoreboard: increased score of team Scotsmen by 1
ok
4> curling:next_round(Pid).
Scoreboard: round over
ok
</pre>
<img class="right" src="static/img/alert.png" width="169" height="270" alt="Some kind of weird looking alien sitting on a toilet, surprised at the newspapers it is reading" title="BREAKING NEWS: WEIRD CREATURES READ NEWSPAPERS" />
<p>This doesn't look like much of an advantage, but it's really about making it nicer to use that code (and reduces the possibilities of writing the messages wrong). </p>
<h3><a class="section" name="alert-the-press">Alert the Press!</a></h3>
<p>We've got the basic scoreboard done, now we want international reporters to be able to get live data from our official in charge of updating our system. Because this is an example program, we won't go through the steps of setting up a socket and writing a protocol for the updates, but we'll put the system in place to do it by putting an intermediary process in charge of it.</p>
<p>Basically, whenever a news organization feels like getting into the game feed, they'll register their own handler that just forwards them the data they need. We'll effectively going to turn our gen_event server into some kind of message hub, just routing them to whoever needs them.</p>
<p>The first thing to do is update the <a class="source" href="static/erlang/curling.erl">curling.erl</a> module with the new interface. Because we want things to be easy to use, we'll only add two functions, <code>join_feed/2</code> and <code>leave_feed/2</code>. Joining the feed should be doable just by inputting the right Pid for the event manager and the Pid to forward all the events to. This should return a unique value that can then be used to unsubscribe from the feed with <code>leave_feed/2</code>:</p>
<pre class="brush:erl">
%% Subscribes the pid ToPid to the event feed.
%% The specific event handler for the newsfeed is
%% returned in case someone wants to leave
join_feed(Pid, ToPid) ->
HandlerId = {curling_feed, make_ref()},
gen_event:add_handler(Pid, HandlerId, [ToPid]),
HandlerId.
leave_feed(Pid, HandlerId) ->
gen_event:delete_handler(Pid, HandlerId, leave_feed).
</pre>
<p>Note that I'm using the technique described earlier for multiple handlers (<code>{curling_feed, make_ref()}</code>). You can see that this function expects a gen_event callback module named <a class="source" href="static/erlang/curling_feed.erl">curling_feed</a>. If I only used the name of the module as a <var>HandlerId</var>, things would have still worked fine,except that we would have no control on which handler to delete when we're done with one instance of it. The event manager would just pick one of them in an undefined manner. Using a Ref makes sure that some guy from the <a class="external" href="http://en.wikipedia.org/wiki/Head-Smashed-In_Buffalo_Jump">Head-Smashed-In Buffalo Jump</a> press leaving the place won't disconnect a journalist from <em>The Economist</em> (no idea why they'd do a report on curling, but what do you know). Anyway, here is the implementation I've made of the <code>curling_feed</code> module:</p>
<pre class="brush:erl">
-module(curling_feed).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
terminate/2]).
init([Pid]) ->
{ok, Pid}.
handle_event(Event, Pid) ->
Pid ! {curling_feed, Event},
{ok, Pid}.
handle_call(_, State) ->
{ok, ok, State}.
handle_info(_, State) ->
{ok, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
</pre>
<p>The only interesting thing here is still the <code>handle_event/2</code> function, which blindly forwards all events to the subscribing Pid. Now when we use the new modules:</p>
<pre class="brush:eshell">
1> c(curling), c(curling_feed).
{ok,curling_feed}
2> {ok, Pid} = curling:start_link("Saskatchewan Roughriders", "Ottawa Roughriders").
Scoreboard: Team Saskatchewan Roughriders vs. Team Ottawa Roughriders
{ok,<0.165.0>}
3> HandlerId = curling:join_feed(Pid, self()).
{curling_feed,#Ref<0.0.0.909>}
4> curling:add_points(Pid, "Saskatchewan Roughriders", 2).
Scoreboard: increased score of team Saskatchewan Roughriders by 1
ok
Scoreboard: increased score of team Saskatchewan Roughriders by 1
5> flush().
Shell got {curling_feed,{add_points,"Saskatchewan Roughriders",2}}
ok
6> curling:leave_feed(Pid, HandlerId).
ok
7> curling:next_round(Pid).
Scoreboard: round over
ok
8> flush().
ok
</pre>
<p>And we can see that we added ourselves to the feed, got the updates, then left and stopped receiving them. You can actually try to add many processes many times and it will work fine.</p>
<p>This introduces a problem though. What if one of the curling feed subscribers crashes? Do we just keep the handler going on there? Ideally, we wouldn't have to. In fact, we don't have to. All that needs to be done is to change the call from <code>gen_event:add_handler/3</code> to <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_event.html#add_sup_handler/3">gen_event:add_sup_handler/3</a></code>. If you crash, the handler is gone. Then on the opposite end, if the <code>gen_event</code> manager crashes, the message <code>{gen_event_EXIT, Handler, Reason}</code> is sent back to you so you can handle it. Easy enough, right? Think again.</p>
<h4>Don't Drink Too Much Kool-Aid</h4>
<img class="left" src="static/img/leash.png" width="163" height="261" alt="alien kid on a leash" />
<p>It might have happened at some time in your childhood that you went to your aunt or grandmother's place for a party or something. If you were mischievous in any way, you would have several adults looking over you, on top of your parents. Now if you ever did something wrong, you would get scolded by your mom, dad, aunt, grandmother and then everyone would keep telling you after that even though you already clearly knew you had done something wrong. Well <code>gen_event:add_sup_handler/3</code> is a bit like that; no, seriously.</p>
<p>Whenever you use <code>gen_event:add_sup_handler/3</code>, a link is set up between your process and the event manager so both of them are supervised and the handler knows if its parent process fails. If you recall the <a class="chapter" href="errors-and-processes.html#monitors">Errors and Processes</a> chapter and its section on monitors, I have mentioned that monitors are <cite>great for writing libraries which need to know what's going on with other processes</cite> because they can be stacked, at the opposite of links. Well <code>gen_event</code> predates the appearance of monitors in Erlang and a strong commitment to backwards compatibility introduced this pretty bad wart. Basically, because you could have the same process acting as the parent of many event handlers, so the library doesn't ever unlink the processes (except when it terminates for good) just in case. Monitors would actually solve the problem, but they aren't being used there.</p>
<p>This mean that everything goes alright when your own process crashes: the supervised handler is terminated (with the call to <code>YourModule:terminate({stop, Reason}, State)</code>). Everything goes alright when your handler itself crashes (but not the event manager): you will receive <code>{gen_event_EXIT, HandlerId, Reason}</code>. When the event manager is shut down though, you will either:</p>
<ul>
<li>Receive the <code>{gen_event_EXIT, HandlerId, Reason}</code> message then crash because you're not trapping exits;</li>
<li>Receive the <code>{gen_event_EXIT, HandlerId, Reason}</code> message, then a standard <code>'EXIT'</code> message that is either superfluous or confusing.</li>
</ul>
<p>That's quite a wart, but at least you know about it. You can try and switch your event handler to a supervised one if you feel like it. It'll be safer even if it risks being more annoying in some cases. Safety first.</p>
<p>We're not done yet! what happens if some member of the media is not there on time? We need to be able to tell them from the feed what the current state of the game is. For this, we'll write an additional event handler named <a class="source" href="static/erlang/curling_accumulator.erl">curling_accumulator</a>. Again, before writing it entirely, we might want to add it to the <code>curling</code> module with the few calls we want:</p>
<pre class="brush:erl">
-module(curling).
-export([start_link/2, set_teams/3, add_points/3, next_round/1]).
-export([join_feed/2, leave_feed/2]).
-export([game_info/1]).
start_link(TeamA, TeamB) ->
{ok, Pid} = gen_event:start_link(),
%% The scoreboard will always be there
gen_event:add_handler(Pid, curling_scoreboard, []),
%% Start the stats accumulator
gen_event:add_handler(Pid, curling_accumulator, []),
set_teams(Pid, TeamA, TeamB),
{ok, Pid}.
%% skipping code here
%% Returns the current game state.
game_info(Pid) ->
gen_event:call(Pid, curling_accumulator, game_data).
</pre>
<p>A thing to notice here is that the <code>game_info/1</code> function uses only <code>curling_accumulator</code> as a handler id. In the cases where you have many versions of the same handler, the hint about using <code>make_ref()</code> (or any other means) to ensure you write to the right handler still holds. Also note that I made the <code>curling_accumulator</code> handler start automatically, much like the scoreboard. Now for the module itself. It should be able to hold state for the curling game: so far we have teams, score and rounds to track. This can all be held in a state record, changed on each event received. Then, we will only need to reply to the <code>game_data</code> call, as below:</p>
<pre class="brush:erl">
-module(curling_accumulator).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
terminate/2]).
-record(state, {teams=orddict:new(), round=0}).
init([]) ->
{ok, #state{}}.
handle_event({set_teams, TeamA, TeamB}, S=#state{teams=T}) ->
Teams = orddict:store(TeamA, 0, orddict:store(TeamB, 0, T)),
{ok, S#state{teams=Teams}};
handle_event({add_points, Team, N}, S=#state{teams=T}) ->
Teams = orddict:update_counter(Team, N, T),
{ok, S#state{teams=Teams}};
handle_event(next_round, S=#state{}) ->
{ok, S#state{round = S#state.round+1}};
handle_event(_Event, Pid) ->
{ok, Pid}.
handle_call(game_data, S=#state{teams=T, round=R}) ->
{ok, {orddict:to_list(T), {round, R}}, S};
handle_call(_, State) ->
{ok, ok, State}.
handle_info(_, State) ->
{ok, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
</pre>
<p>So you can see we basically just update the state until someone asks for game details, at which point we'll be sending them back. We did this in a very basic way. A perhaps smarter way to organize the code would have been to simply keep a list of all the events to ever happen in the game so we could send them back at once each time a new process subscribes to the feed. This won't be needed here to show how things work, so let's focus on using our new code:</p>
<pre class="brush:eshell">
1> c(curling), c(curling_accumulator).
{ok,curling_accumulator}
2> {ok, Pid} = curling:start_link("Pigeons", "Eagles").
Scoreboard: Team Pigeons vs. Team Eagles
{ok,<0.242.0>}
3> curling:add_points(Pid, "Pigeons", 2).
Scoreboard: increased score of team Pigeons by 1
ok
Scoreboard: increased score of team Pigeons by 1
4> curling:next_round(Pid).
Scoreboard: round over
ok
5> curling:add_points(Pid, "Eagles", 3).
Scoreboard: increased score of team Eagles by 1
ok
Scoreboard: increased score of team Eagles by 1
Scoreboard: increased score of team Eagles by 1
6> curling:next_round(Pid).
Scoreboard: round over
ok
7> curling:game_info(Pid).
{[{"Eagles",3},{"Pigeons",2}],{round,2}}
</pre>
<p>Enthralling! Surely the Olympic committee will love our code. We can pat ourselves on the back, cash in a fat check and go play videogames all night now.</p>
<p>We haven't seen all there is to do with gen_event as a module. In fact, we haven't seen the most common use of event handlers: logging and system alarms. I decided against showing them because pretty much any other source on Erlang out there uses <code>gen_event</code> strictly for that. If you're interested in going there, check out <a class="docs" href="http://www.erlang.org/doc/man/error_logger.html" title="Erlang's default logging application">error_logger</a> first.</p>
<p>Even if we've not seen the most common uses of <code>gen_event</code>, it's important to say that we've seen all the concepts necessary to understanding them, building our own and integrating them into our applications. More importantly, we've finally covered the three main OTP behaviours used in active code development. We still have a few behaviours left to visit—those that act as a bunch of glue between all of our worker processes—such as the supervisor.</p>
<ul class="navigation">
<li><a href="finite-state-machines.html" title="Previous chapter">< Previous</a></li>
<li><a href="contents.html" title="Index">Index</a></li>
<li><a href="supervisors.html" title="Next chapter">Next ></a></li>
</ul>
</div><!-- content -->
<div id="footer">
<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License Details"><img src="static/img/cc.png" width="88" height="31" alt="Creative Commons Attribution Non-Commercial No Derivative License" /></a>
<p>Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution Non-Commercial No Derivative License</p>
</div> <!-- footer -->
</div> <!-- wrapper -->
<div id="grass" />
<script type="text/javascript" src="static/js/shCore.js"></script>
<script type="text/javascript" src="static/js/shBrushErlang2.js%3F11"></script>
<script type="text/javascript">
SyntaxHighlighter.defaults.gutter = false;
SyntaxHighlighter.all();
</script>
</body>
</html>