LABSN / tdtpy

Python wrapper around the Tucker-Davis Technologies ActiveX library
BSD 3-Clause "New" or "Revised" License
7 stars 3 forks source link

Fix race condition when checking for buffer under and overflow #23

Closed ghost closed 1 year ago

ghost commented 1 year ago

I did my best to ensure this change is backwards compatible for anyone who may be using TDTPy (especially the DSPBuffer). I'm not sure how many people are actually using it, but there have been some gaps in TDTPy in terms of tracking the current "position" of the DSPBuffer when it's set up for continuous reads. Specifically, it acts as a ring buffer so we need to track both the index of the last sample written to the buffer as well as how many times the index has cycled around to the beginning. Before TDTPy 0.10.0 we did not check for potential under or overflows where data is lost. We now check by inspecting the current cycle and index (implemented in 0.10.0); however, 0.10.0 introduced the potential for a race condition. If we read the index when it is very close to the end of the buffer, it may wrap around to the beginning before we read the cycle number from the circuit. That makes it appear that the actual number of samples written to the buffer is much higher than it actually is, leading TDTPy to assume that some data was overwritten before it could be read.

Assume a buffer of size 100. At time T the index is 95 and the cycle is 1. That means the total number of samples written is 95 + (1 100) or 195. However, TDTPy reads the index first and sees that it is 95. Then, it reads the cycle tag but due to the fact this is not a concurrent read, the index has wrapped around to 0 and the cycle has incremented to 2 at the point the cycle tag is read. TDTPy then assumes that the total number of samples written is 95 + (2 100) or 295. The correct calculation at this point in time would be 5 + (2 * 100) or 205, but due to the fact that we have no way of simultaneously querying the cycle and index tag we have to account for this.

The trick is to put the index and cycle tag after a latch. Firing a soft trigger will "freeze" the value of each latched value at the specific sample the trigger was fired in the circuit. We can then read these values at our leisure and use that for computations without having to worry about race conditions.

ghost commented 1 year ago

@larsoner Don't merge this just yet. There's a few bugs that need to be ironed out.