Introduction

In this discussion, we will walk through a common problem that arises when working with asynchronous programming and explore how to solve this problem using the WorkQueue. This discussion assumes familiarity with asynchronous programming concepts.

Use Case

Consider an example where you want to assert or deassert a wire when a button is pressed or released.

Initial Approach

Here is a naive approach to handle button press and release without using the WorkQueue:

  async OnButtonDown() {
    await device.setWireInValue(epAddress, 1);
    await device.updateWireIns();
  }

  async OnButtonUp() {
    await device.setWireInValue(epAddress, 0);
    await device.updateWireIns();
  }Code language: JavaScript (javascript)

Problem

The problem with this approach is that if the button press and release happen close enough to each other, the instructions could interleave. This creates a race condition in the source code, where the sequence of operations may not execute as intended. For example, the interleaving could look like this:

await device.setWireInValue(epAddress, 1);
await device.setWireInValue(epAddress, 0);
await device.updateWireIns();
await device.updateWireIns();Code language: JavaScript (javascript)

The setWireInValue function stores the WireIn endpoint values internally on the Host PC API side. The updateWireIns function commits these changes to the XEM device. If the wire is initially at 0, and the code above sets the internal storage from 0 to 1 and then from 1 to 0, and then commits 0 to the device twice, nothing ever changes. This is the core issue: the intended change to the WireIn endpoint is effectively nullified due to the interleaving of operations.

This is the intended order of operations:

await device.setWireInValue(epAddress, 1);
await device.updateWireIns();
await device.setWireInValue(epAddress, 0);
await device.updateWireIns();Code language: JavaScript (javascript)

Solution: Using WorkQueue

To ensure the setWireIn and updateWireIns calls happen in the correct order, we can use the WorkQueue. The WorkQueue acts like a ticketing system, where each ticket represents a series of instructions to be executed by the device. These work tickets are processed one at a time, ensuring atomic execution and preventing interleaving of instructions.

WorkQueue Approach

Using the WorkQueue, we can perform the same assert/deassert operations but ensure they execute in the correct order. Here is the modified code:

  OnButtonDown() {
    this.props.WorkQueue.Post(async () => {
      await device.setWireInValue(epAddress, 1);
      await device.updateWireIns();
    });
  }
  
  OnButtonUp() {
    this.props.WorkQueue.Post(async () => {
      await device.setWireInValue(epAddress, 0);
      await device.updateWireIns();
    });
  }Code language: JavaScript (javascript)

Here we wrap the operations with a function to define the work ticket. Then we schedule the ticket by calling the Post method of the WorkQueue and passing it the function. If it is the next ticket in the queue, the function will execute immediately. Otherwise, it will wait until all the previously queued tickets have completed. By using the WorkQueue, we ensure that work tickets containing sequences of FrontPanel API calls are executed atomically, one ticket at a time. This prevents interleaving and maintains the correct execution order.

Potential Pitfalls

Overloading the WorkQueue

Issue: Placing too many tickets into the WorkQueue can overwhelm the device, leading to performance degradation.
Solution: Monitor and manage the number of tasks being queued to ensure the device can keep up.

Mixed Access Methods

Issue: Mixing WorkQueue-managed instructions with non-WorkQueue-managed device accesses can lead to instruction interleaving.
Solution: Ensure all device instructions are routed through the WorkQueue to maintain atomicity.

Use the Same Instance of WorkQueue

Issue: If you post to two separate WorkQueues, the tickets may still interleave.
Solution: Use the same instance of the WorkQueue to ensure serialized execution of tasks.