Skip to content

Commit f3bd541

Browse files
committed
Merge branch 'laurenbee-master'
2 parents ba51580 + 9eacb1d commit f3bd541

File tree

2 files changed

+116
-114
lines changed

2 files changed

+116
-114
lines changed

book/event-queue.markdown

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ to your application.
7272

7373
Most <span name="game-loop">games</span> aren't event-driven like this, but it
7474
is common for a game to have its own event queue as the backbone of its nervous
75-
system. You'll often hear "central" or "global" or "main" used to describe it.
75+
system. You'll often hear "central", "global", or "main" used to describe it.
7676
It's used for high level communication between game systems that want to stay
7777
decoupled.
7878

7979
<aside name="game-loop">
8080

81-
If you want to know *why* they aren't, crack open the <a href="game-loop.html"
81+
If you want to know *why* they aren't event-driven, crack open the <a href="game-loop.html"
8282
class="pattern">Game Loop</a> chapter.
8383

8484
</aside>
@@ -90,10 +90,10 @@ to grab the loot!"
9090

9191
<aside name="tutorial">
9292

93-
Tutorial systems are a pain to implement gracefully and most players will spend
94-
only a fraction of their time using it, so it feels like they aren't worth the
95-
effort. But that fraction where they *are* using the tutorial can be invaluable
96-
for easing the player into your game.
93+
Tutorial systems are a pain to implement gracefully, and most players will spend
94+
only a fraction of their time using in-game help, so it feels like they aren't
95+
worth the effort. But that fraction where they *are* using the tutorial can be
96+
invaluable for easing the player into your game.
9797

9898
</aside>
9999

@@ -120,7 +120,7 @@ the AI field.
120120
<img src="images/event-queue-central.png" alt="A central event queue is read from and written to by the Combat and Tutorial code." />
121121

122122
I thought about using this as the example for the rest of the chapter, but I'm
123-
not generally a fan of global systems. It is a common technique, but I don't
123+
not generally a fan of global systems. Using them is a common technique, but I don't
124124
want you to think that event queues *have* to be global.
125125

126126
### Say what?
@@ -140,7 +140,7 @@ volume:
140140

141141
While I almost always shy away from the <a href="singleton.html"
142142
class="gof-pattern">Singleton</a> pattern, this is one of the places where it
143-
may fit. I'm doing a simpler approach and just making the method static.
143+
may fit. I'm taking a simpler approach and just making the method static.
144144

145145
</aside>
146146

@@ -155,7 +155,7 @@ implemented elsewhere. Using it, we write our method like so:
155155

156156
We check that in, create a few sound files, and start sprinkling `playSound()`
157157
calls through our codebase like some magical audio fairy. For example, in our UI
158-
code, when the selected menu item changes, we play a little bloop:
158+
code, we play a little bloop when the selected menu item changes:
159159

160160
^code menu-bloop
161161

@@ -166,7 +166,7 @@ screen freezes for a few frames. We've hit our first issue:
166166
processed the request.**
167167

168168
Our `playSound()` method is *synchronous* -- it doesn't return back to the
169-
caller until bleeps are coming out of the speakers. If a sound file has to be
169+
caller until bloops are coming out of the speakers. If a sound file has to be
170170
loaded from disc first, that may take a while.
171171

172172
Ignoring that for now, we move on. In the AI code, we add a call to let out a
@@ -228,14 +228,14 @@ A **queue** stores a series of **notifications or requests** in first-in,
228228
first-out order. Sending a notification **enqueues the request and returns**.
229229
The request processor then **processes items from the queue** at a later time.
230230

231-
Requests can be **handled directly**, or **routed to interested parties**. This
231+
Requests can be **handled directly** or **routed to interested parties**. This
232232
**decouples the sender from the receiver** both **statically** and **in time**.
233233

234234
## When to Use It
235235

236-
If you just want to decouple *who* receives a message from its sender, patterns
236+
If you only want to decouple *who* receives a message from its sender, patterns
237237
like <a href="observer.html">Observer</a> and <a href="command.html">Command</a>
238-
will take care of you with less <span name="simple">complexity</span>. You only
238+
will take care of this with less <span name="simple">complexity</span>. You only
239239
need a queue when you want to decouple something *in time*.
240240

241241
<aside name="simple">
@@ -254,17 +254,17 @@ at a convenient time in *its* run cycle. When you have a push model on one end
254254
and a pull model on the other, you need a buffer between them. That's what a
255255
queue provides that simpler decoupling patterns don't.
256256

257-
Queues give control to the code that pulls from it: the receiver can delay
258-
processing, aggregate requests, or discard them entirely. But it does this by
259-
taking control *away* from the sender. All it can do is throw a request on the
257+
Queues give control to the code that pulls from it -- the receiver can delay
258+
processing, aggregate requests, or discard them entirely. But queues do this by
259+
taking control *away* from the sender. All the sender can do is throw a request on the
260260
queue and hope for the best. This makes queues a poor fit when the sender needs
261261
a response.
262262

263263
## Keep in Mind
264264

265265
Unlike some more modest patterns in this book, event queues are complex and tend
266266
to have a wide-reaching effect on the architecture of our games. That means
267-
you'll want to think hard about how -- or if -- you use it.
267+
you'll want to think hard about how -- or if -- you use one.
268268

269269
### A central event queue is a global variable
270270

@@ -286,8 +286,8 @@ how many frames until it eventually works its way to the front and gets
286286
processed.
287287

288288
Meanwhile, the experience system wants to track the heroine's body count and
289-
reward her for her grisly efficiency. It receives these "entity died" events
290-
then determines the kind of entity slain and the difficulty of the kill so it
289+
reward her for her grisly efficiency. It receives each "entity died" event
290+
and determines the kind of entity slain and the difficulty of the kill so it
291291
can dish out an appropriate reward.
292292

293293
That requires various pieces of state in the world. We need the entity that died
@@ -298,8 +298,8 @@ other nearby foes may have wandered off.
298298

299299
When you receive an event, you have to be careful not to assume the *current*
300300
state of the world reflects how the world was *when the event was raised*. This
301-
means queued events tend to be more data heavy than in synchronous systems. With
302-
the latter, the notification can just say "something happened" and the receiver
301+
means queued events tend to be more data heavy than events in synchronous systems. With
302+
the latter, the notification can say "something happened" and the receiver
303303
can look around for the details. With a queue, those ephemeral details must be
304304
captured when the event is sent so they can be used later.
305305

@@ -313,7 +313,7 @@ All event and message systems have to worry about cycles:
313313
response, it sends an event...
314314
5. Go to 2.
315315

316-
When your messaging system is *synchronous*, you find cycles quickly: they
316+
When your messaging system is *synchronous*, you find cycles quickly -- they
317317
overflow the stack and crash your game. With a queue, the asynchrony unwinds the
318318
stack, so the game may keep running even though spurious events are <span
319319
name="log">sloshing</span> back and forth in there. A common rule to avoid this
@@ -328,7 +328,7 @@ A little debug logging in your event system is probably a good idea too.
328328
## Sample Code
329329

330330
We've already seen some code. It's not perfect, but it has the right basic
331-
functionality: the public API we want, and the right low level audio calls. All
331+
functionality -- the public API we want and the right low-level audio calls. All
332332
that's left for us to do now is fix its problems.
333333

334334
The first is that our API *blocks*. When a piece of code plays a sound, it can't
@@ -347,7 +347,7 @@ play messages. Now, your <span name="prof">algorithms professor</span> would
347347
tell you to use some exciting data structure here like a [Fibonacci
348348
heap](http://en.wikipedia.org/wiki/Fibonacci_heap) or a [skip
349349
list](http://en.wikipedia.org/wiki/Skip_list), or, hell, at least a *linked*
350-
list. But, in practice, the best way to store a bunch of homogenous things is
350+
list. But in practice, the best way to store a bunch of homogenous things is
351351
almost always a plain old array:
352352

353353
<aside name="prof">
@@ -374,7 +374,7 @@ So let's do that:
374374

375375
^code pending-array
376376

377-
We can tune the array size to cover our worst case. To play a sound, we just
377+
We can tune the array size to cover our worst case. To play a sound, we simply
378378
slot a new message in there at the end:
379379

380380
^code array-play
@@ -394,9 +394,9 @@ class="pattern">Update Method</a> pattern.
394394

395395
</aside>
396396

397-
Now we just need to call that from somewhere convenient. What "convenient" means
397+
Now, we need to call that from somewhere convenient. What "convenient" means
398398
depends on your game. It may mean calling it from the main <a
399-
href="game-loop.html" class="pattern">Game Loop</a>, or from a dedicated audio
399+
href="game-loop.html" class="pattern">Game Loop</a> or from a dedicated audio
400400
thread.
401401

402402
This works fine, but it does presume we can process *every* sound request in a
@@ -409,23 +409,23 @@ actual queue.
409409
### A ring buffer
410410

411411
There are a bunch of ways to implement queues, but my favorite is called a *ring
412-
buffer*. It preserves everything that's great about arrays, but lets us
412+
buffer*. It preserves everything that's great about arrays while letting us
413413
incrementally remove items from the front of the queue.
414414

415415
Now, I know what you're thinking. If we remove items from the beginning of the
416416
array, don't we have to shift all of the remaining items over? Isn't that slow?
417417

418-
This is why they made us learn linked lists: you can remove nodes from them
418+
This is why they made us learn linked lists -- you can remove nodes from them
419419
without having to shift things around. Well, it turns out you can implement a
420420
queue without any shifting in an array too. I'll walk you through it, but first
421-
let's get precise on some terms.
421+
let's get precise on some terms:
422422

423423
* The **head** of the queue is where requests are *read* from. The head is the
424424
oldest pending request.
425425

426426
* The **tail** is the other end. It's the slot in the array where the next
427427
enqueued request will be *written*. Note that it's just *past* the end of
428-
the queue. You can think of it as a half-open range if that helps.
428+
the queue. You can think of it as a half-open range, if that helps.
429429

430430
Since `playSound()` appends new requests at the end of the array, the head
431431
starts at element zero and the tail grows to the right.
@@ -437,7 +437,7 @@ markers explicit in the class:
437437

438438
^code head-tail
439439

440-
In the implementation of `playSound()`, `numPending_` has been replaced by
440+
In the implementation of `playSound()`, `numPending_` has been replaced with
441441
`tail_`, but otherwise it's the same:
442442

443443
^code tail-play
@@ -459,8 +459,8 @@ will be empty if the head and tail are the same index.
459459

460460
Now we've got a queue -- we can add to the end and remove from the front.
461461
There's an obvious problem, though. As we run requests through the queue, the
462-
head and tail keep crawling to the right. Eventually, `tail_` will hit the end
463-
of the array and <span name="party">party time</span> is over. This is where it
462+
head and tail keep crawling to the right. Eventually, `tail_` hits the end
463+
of the array, and <span name="party">party time</span> is over. This is where it
464464
gets clever.
465465

466466
<aside name="party">
@@ -473,8 +473,8 @@ Do you want party time to be over? No. You do not.
473473

474474
Notice that while the tail is creeping forward, the *head* is too. That means
475475
we've got array elements at the *beginning* of the array that aren't being used
476-
any more. So what we do is wrap the tail back around to the beginning of the
477-
array when it runs off the end. That's why it's called a *ring* buffer: it acts
476+
anymore. So what we do is wrap the tail back around to the beginning of the
477+
array when it runs off the end. That's why it's called a *ring* buffer -- it acts
478478
like a circular array of cells.
479479

480480
<img src="images/event-queue-ring.png" alt="The array wraps around and now the head can circle back to the beginning." />
@@ -485,19 +485,19 @@ the end:
485485

486486
^code ring-play
487487

488-
Replacing `tail_++` with an increment modulo the array size wraps it back
488+
Replacing `tail_++` with an increment modulo the array size wraps the tail back
489489
around. The other change is the assertion. We need to ensure the queue doesn't
490490
overflow. As long as there are fewer than `MAX_PENDING` requests in the queue,
491-
there will be a little gap of unused cells between the head and tail. If the
491+
there will be a little gap of unused cells between the head and the tail. If the
492492
queue fills up, those will be gone and, like some weird backwards Ouroboros, the
493493
tail will collide with the head and start overwriting it. The assertion ensures
494-
that doesn't happen.
494+
that this doesn't happen.
495495

496496
In `update()`, we wrap the head around too:
497497

498498
^code ring-update
499499

500-
There you go: a queue with <span name="capacity">no dynamic allocation</span>,
500+
There you go -- a queue with <span name="capacity">no dynamic allocation</span>,
501501
no copying elements around, and the cache-friendliness of a simple array.
502502

503503
<aside name="capacity">
@@ -549,8 +549,8 @@ the queue gets full, we'll find more things to collapse.
549549

550550
This pattern insulates the requester from knowing when the request gets
551551
processed, but when you treat the entire queue as a live data structure to be
552-
played with, then lag between request and processing can visibly affect
553-
behavior. Make sure you're OK with that before doing this.
552+
played with, then lag between making a request and processing it can visibly
553+
affect behavior. Make sure you're OK with that before doing this.
554554

555555
### Spanning threads
556556

@@ -600,7 +600,7 @@ burn CPU cycles until there's a request to process.
600600

601601
Many games use event queues as a key part of their communication structure, and
602602
you can spend a ton of time designing all sorts of complex routing and filtering
603-
for messages. But, before you go off and build something like the Los Angeles
603+
for messages. But before you go off and build something like the Los Angeles
604604
telephone switchboard, I encourage you to start simple. Here's a few starter
605605
questions to consider:
606606

@@ -676,11 +676,11 @@ distinguish these, and both styles are useful.
676676

677677
* *You don't have to worry about contention between listeners.* With
678678
multiple listeners, you have to decide if they *all* get every item
679-
(broadcast) or if *each* item in the queue is parcelled out to *one*
679+
(broadcast) or if *each* item in the queue is parceled out to *one*
680680
listener (something more like a work queue).
681681

682682
In either case, the listeners may end up doing redundant work or
683-
interfere with each other, and you have to think carefully about the
683+
interfering with each other, and you have to think carefully about the
684684
behavior you want. With a single listener, that complexity disappears.
685685

686686
* **A broadcast queue:**
@@ -691,7 +691,7 @@ distinguish these, and both styles are useful.
691691
* *Events can get dropped on the floor.* A corollary to the previous point
692692
is that if you have *zero* listeners, all zero of them see the event. In
693693
most broadcast systems, if there are no listeners at the point in time
694-
that an event is processed, the event just gets discarded.
694+
that an event is processed, the event gets discarded.
695695

696696
* *You may need to filter events.* Broadcast queues are often widely
697697
visible to much of the program, and you can end up with a bunch of
@@ -700,18 +700,18 @@ distinguish these, and both styles are useful.
700700

701701
To cut that down to size, most broadcast event systems let a listener
702702
winnow down the set of events they receive. For example, they may say
703-
they only want to receive mouse events, or events within a certain
703+
they only want to receive mouse events or events within a certain
704704
region of the UI.
705705

706706
* **A work queue:**
707707

708708
Like a broadcast queue, here you have multiple listeners too. The difference
709709
is that each item in the queue only goes to *one* of them. This is a common
710-
pattern for parcelling out jobs to a pool of concurrently running threads.
710+
pattern for parceling out jobs to a pool of concurrently running threads.
711711

712712
* *You have to schedule.* Since an item only goes to one listener, the
713713
queue needs logic to figure out the best one to choose. This may be as
714-
simple as round robin or random choice, or some more complex
714+
simple as round robin or random choice, or it could be some more complex
715715
prioritizing system.
716716

717717
### Who can write to the queue?
@@ -722,14 +722,15 @@ one-to-one, one-to-many, many-to-one, or many-to-many.
722722

723723
<aside name="configs">
724724

725-
You sometimes hear "fan-in" used to describe many-to-one communication systems,
725+
You sometimes hear "fan-in" used to describe many-to-one communication systems
726726
and "fan-out" for one-to-many.
727727

728728
</aside>
729729

730730
* **With one writer:**
731731

732-
This style is most similar to the synchronous Observer pattern. You have one
732+
This style is most similar to the synchronous <a href="observer.html"
733+
class="gof-pattern">Observer</a> pattern. You have one
733734
privileged object that generates events that others can then receive.
734735

735736
* *You implicitly know where the event is coming from.* Since there's only
@@ -738,7 +739,7 @@ and "fan-out" for one-to-many.
738739

739740
* *You usually allow multiple readers.* You can have a
740741
one-sender-one-receiver queue, but that starts to feel less like the
741-
communication system this pattern is about and more a vanilla queue data
742+
communication system this pattern is about and more like a vanilla queue data
742743
structure.
743744

744745
* **With multiple writers:**
@@ -765,7 +766,7 @@ message itself can safely live in a local variable on the stack. With a queue,
765766
the message outlives the call that enqueues it.
766767

767768
If you're using a garbage collected language, you don't need to worry about this
768-
too much. Stuff the message in the queue and it will stick around in memory as
769+
too much. Stuff the message in the queue, and it will stick around in memory as
769770
long as it's needed. In C or C++, it's up to you to ensure the object lives long
770771
enough.
771772

@@ -801,7 +802,7 @@ enough.
801802
the queue. Instead of allocating the message itself, the sender requests a
802803
"fresh" one from the queue. The queue returns a reference to a message
803804
already in memory inside the queue, and the sender fills it in. When the
804-
message gets processed, the receiver just refers to the same message in the
805+
message gets processed, the receiver refers to the same message in the
805806
queue.
806807

807808
<aside name="pool">
@@ -818,12 +819,12 @@ enough.
818819
class="gof-pattern">Observer</a> pattern.
819820

820821
* Like many patterns, event queues go by a number of aliases. One established
821-
term is "message queue". It's usually referring to a higher level
822+
term is "message queue". It's usually referring to a higher-level
822823
manifestation. Where our event queues are *within* an application, message
823824
queues are usually used for communicating *between* them.
824825

825826
Another term is "publish/subscribe", sometimes abbreviated to "pubsub". Like
826-
"message queue", it usually refers to larger distributed systems and less
827+
"message queue", it usually refers to larger distributed systems unlike
827828
the humble coding pattern we're focused on.
828829

829830
* A [finite state machine](http://en.wikipedia.org/wiki/Finite-state_machine),
@@ -832,7 +833,7 @@ enough.
832833
want it to respond to those asynchronously, it makes sense to queue them.
833834

834835
When you have a bunch of state machines sending messages to each other, each
835-
with a little queue of pending inputs (called a *mailbox*) then you've
836+
with a little queue of pending inputs (called a *mailbox*), then you've
836837
re-invented the [actor model](http://en.wikipedia.org/wiki/Actor_model) of
837838
computation.
838839

0 commit comments

Comments
 (0)