Browser docs

Event Queue

Intent

The intent of the event queue design pattern, also known as message queues, is to decouple the relationship between the sender and receiver of events within a system. By decoupling the two parties, they do not interact with the event queue simultaneously. Essentially, the event queue handles and processes requests in an asynchronous manner, therefore, this system can be described as a first in, first out design pattern model. Event Queue is a suitable pattern if there is a resource with limited accessibility (i.e. Audio or Database), however, you need to provide access to all the requests which seeks this resource. Upon accessing an event from the queue, the program also removes it from the queue.

alt text

Explanation

Real world example

The modern emailing system is an example of the fundamental process behind the event-queue design pattern. When an email is sent, the sender continues their daily tasks without the necessity of an immediate response from the receiver. Additionally, the receiver has the freedom to access and process the email at their leisure. Therefore, this process decouples the sender and receiver so that they are not required to engage with the queue at the same time.

In plain words

The buffer between sender and receiver improves maintainability and scalability of a system. Event queues are typically used to organise and carry out interprocess communication (IPC).

Wikipedia says

Message queues (also known as event queues) implement an asynchronous communication pattern between two or more processes/ threads whereby the sending and receiving party do not need to interact with the queue at the same time.

Key drawback

As the event queue model decouples the sender-receiver relationship - this means that the event-queue design pattern is unsuitable for scenarios in which the sender requires a response. For example, this is a prominent feature within online multiplayer games, therefore, this approach require thorough consideration.

Programmatic Example

Upon examining our event-queue example, here’s the app which utilised an event queue system.

 1import javax.sound.sampled.UnsupportedAudioFileException;
 2import java.io.IOException;
 3
 4public class App {
 5
 6    /**
 7     * Program entry point.
 8     *
 9     * @param args command line args
10     * @throws IOException                   when there is a problem with the audio file loading
11     * @throws UnsupportedAudioFileException when the loaded audio file is unsupported
12     */
13    public static void main(String[] args) throws UnsupportedAudioFileException, IOException,
14            InterruptedException {
15        var audio = Audio.getInstance();
16        audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f);
17        audio.playSound(audio.getAudioStream("./etc/Closed-Hi-Hat-1.wav"), -8.0f);
18
19        LOGGER.info("Press Enter key to stop the program...");
20        try (var br = new BufferedReader(new InputStreamReader(System.in))) {
21            br.read();
22        }
23        audio.stopService();
24    }
25}

Much of the design pattern is developed within the Audio class. Here we set instances, declare global variables and establish the key methods used in the above runnable class.

 1public class Audio {
 2    private static final Audio INSTANCE = new Audio();
 3
 4    private static final int MAX_PENDING = 16;
 5
 6    private int headIndex;
 7
 8    private int tailIndex;
 9
10    private volatile Thread updateThread = null;
11
12    private final PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
13
14    // Visible only for testing purposes
15    Audio() {
16
17    }
18
19    public static Audio getInstance() {
20        return INSTANCE;
21    }
22}

The Audio class is also responsible for handling and setting the states of the thread, this is shown in the code segments below.

 1/**
 2 * This method stops the Update Method's thread and waits till service stops.
 3 */
 4public synchronized void stopService() throws InterruptedException {
 5    if (updateThread != null) {updateThread.interrupt();}
 6    updateThread.join();
 7    updateThread = null;
 8}
 9
10/**
11 * This method check the Update Method's thread is started.
12 * @return boolean
13 */
14public synchronized boolean isServiceRunning() {
15    return updateThread != null && updateThread.isAlive();}
16
17/**
18 * Starts the thread for the Update Method pattern if it was not started previously. Also when the
19 * thread is ready it initializes the indexes of the queue
20 */
21public void init() {
22    if (updateThread == null) {
23        updateThread = new Thread(() -> {
24            while (!Thread.currentThread().isInterrupted()) {
25                update();
26            }});}
27    startThread();
28}
29
30/**
31 * This is a synchronized thread starter.
32 */
33private synchronized void startThread() {
34    if (!updateThread.isAlive()) {
35        updateThread.start();
36        headIndex = 0;
37        tailIndex = 0;
38    }
39}

New audio is added into our event queue in the playSound method found in the Audio class. The update method is then utilised to retrieve an audio item from the queue and play it to the user.

 1public void playSound(AudioInputStream stream, float volume) {
 2    init();
 3    // Walk the pending requests.
 4    for (var i = headIndex; i != tailIndex; i = (i + 1) % MAX_PENDING) {
 5      var playMessage = getPendingAudio()[i];
 6      if (playMessage.getStream() == stream) {
 7        // Use the larger of the two volumes.
 8        playMessage.setVolume(Math.max(volume, playMessage.getVolume()));
 9        // Don't need to enqueue.
10        return;
11      }
12    }
13    getPendingAudio()[tailIndex] = new PlayMessage(stream, volume);
14    tailIndex = (tailIndex + 1) % MAX_PENDING;
15}

Within the Audio class are some more methods with assist the construction of the event-queue design patterns, they are summarised below.

  • getAudioStream() = returns the input stream path of a file
  • getPendingAudio() = returns the current event queue item

Class diagram

alt text

Applicability

Use the Event Queue Pattern when

  • The sender does not require a response from the receiver.
  • You wish to decouple the sender & the receiver.
  • You want to process events asynchronously.
  • You have a limited accessibility resource and the asynchronous process is acceptable to reach that.

Credits