Skip to content

Commit

Permalink
#1876 Unifies reentrant lock between tuners and frequency controller.
Browse files Browse the repository at this point in the history
  • Loading branch information
sheirerd committed Apr 29, 2024
1 parent aa8d449 commit 80d173b
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 99 deletions.
78 changes: 45 additions & 33 deletions src/main/java/io/github/dsheirer/source/tuner/TunerController.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Base tuner controller implementation.
*/
public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener
{
private final static Logger mLog = LoggerFactory.getLogger(TunerController.class);

//Protects access to the native buffer broadcaster for adding, removing or checking for listener count.
protected ReentrantLock mBufferListenerLock = new ReentrantLock();
private ReentrantLock mBufferListenerLock = new ReentrantLock();
private ReentrantLock mLock = new ReentrantLock();
protected Broadcaster<INativeBuffer> mNativeBufferBroadcaster = new Broadcaster();

protected FrequencyController mFrequencyController;
private int mMiddleUnusableHalfBandwidth;
private int mMeasuredFrequencyError;
Expand All @@ -74,6 +77,26 @@ public TunerController(ITunerErrorListener tunerErrorListener)
mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this);
}

/**
* Lock for controlling configuration access to this tuner controller and any embedded tuner to ensure that
* configuration changes happen atomically. There are at least two threads that can touch this controller:
*
* A: channel processing that changes the center tuned frequency and frequency correction.
* b: user ui thread that can touch any of the frequency correction and gain controls.
*
* Generally, we'll lock on each of the primary configuration change points - getter and setter for:
* Frequency
* Frequency Correction
* Sample Rate
* Gain Controls
*
* @return lock for controlling access to tuner controller and em
*/
public ReentrantLock getLock()
{
return mLock;
}

/**
* Updates the frequency controller with the new minimum and maximum values.
* @param minimum frequency Hertz
Expand All @@ -85,17 +108,6 @@ public void setFrequencyExtents(long minimum, long maximum)
mFrequencyController.setMaximumFrequency(maximum);
}

/**
* Lock for the frequency controller. This should only be used by the channel source manager to lock access to the
* frequency controller while creating a channel source, to block multi-threaded access to the frequency controller
* which might put the center tuned frequency value in an indeterminant state.
* @return frequency controller lock
*/
public ReentrantLock getFrequencyControllerLock()
{
return mFrequencyController.getFrequencyControllerLock();
}

/**
* Frequency correction manager for this tuner controller.
*/
Expand Down Expand Up @@ -292,12 +304,12 @@ public void setFrequency(long frequency) throws SourceException
{
try
{
getFrequencyControllerLock().lock();
getLock().lock();
mFrequencyController.setFrequency(frequency);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}
}

Expand All @@ -318,12 +330,12 @@ public boolean canTune(long frequency)

try
{
getFrequencyControllerLock().lock();
getLock().lock();
canTune = mFrequencyController.canTune(frequency);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}

return canTune;
Expand All @@ -341,12 +353,12 @@ public double getFrequencyCorrection()

try
{
getFrequencyControllerLock().lock();
getLock().lock();
correction = mFrequencyController.getFrequencyCorrection();
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}

return correction;
Expand All @@ -356,12 +368,12 @@ public void setFrequencyCorrection(double correction) throws SourceException
{
try
{
getFrequencyControllerLock().lock();
getLock().lock();
mFrequencyController.setFrequencyCorrection(correction);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}
}

Expand All @@ -375,12 +387,12 @@ public long getMinimumFrequency()

try
{
getFrequencyControllerLock().lock();
getLock().lock();
minimum = mFrequencyController.getMinimumFrequency();
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}

return minimum;
Expand All @@ -394,12 +406,12 @@ public void setMinimumFrequency(long minimum)
{
try
{
getFrequencyControllerLock().lock();
getLock().lock();
mFrequencyController.setMinimumFrequency(minimum);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}
}

Expand All @@ -413,12 +425,12 @@ public long getMaximumFrequency()

try
{
getFrequencyControllerLock().lock();
getLock().lock();
maximum = mFrequencyController.getMaximumFrequency();
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}

return maximum;
Expand All @@ -432,12 +444,12 @@ public void setMaximumFrequency(long maximum)
{
try
{
getFrequencyControllerLock().lock();
getLock().lock();
mFrequencyController.setMaximumFrequency(maximum);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}
}

Expand All @@ -447,12 +459,12 @@ public long getMinTunedFrequency() throws SourceException

try
{
getFrequencyControllerLock().lock();
getLock().lock();
minTuned = mFrequencyController.getFrequency() - (getUsableBandwidth() / 2);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}

return minTuned;
Expand All @@ -464,12 +476,12 @@ public long getMaxTunedFrequency() throws SourceException

try
{
getFrequencyControllerLock().lock();
getLock().lock();
maxTuned = mFrequencyController.getFrequency() + (getUsableBandwidth() / 2);
}
finally
{
getFrequencyControllerLock().unlock();
getLock().unlock();
}

return maxTuned;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ public abstract class FCDTunerController extends TunerController

private FCDConfiguration mConfiguration = new FCDConfiguration();
protected ComplexMixer mComplexMixer;
private ReentrantLock mListenerLock = new ReentrantLock();

/**
* Generic FCD tuner controller - contains functionality common across both
Expand Down Expand Up @@ -126,7 +125,7 @@ else if(getTunerType() == TunerType.FUNCUBE_DONGLE_PRO_PLUS)
@Override
public void addBufferListener(Listener<INativeBuffer> listener)
{
mListenerLock.lock();
getLock().lock();

try
{
Expand All @@ -139,7 +138,7 @@ public void addBufferListener(Listener<INativeBuffer> listener)
}
finally
{
mListenerLock.unlock();
getLock().unlock();
}
}

Expand All @@ -150,7 +149,7 @@ public void addBufferListener(Listener<INativeBuffer> listener)
@Override
public void removeBufferListener(Listener<INativeBuffer> listener)
{
mListenerLock.lock();
getLock().lock();

try
{
Expand All @@ -167,7 +166,7 @@ public void removeBufferListener(Listener<INativeBuffer> listener)
}
finally
{
mListenerLock.unlock();
getLock().unlock();
}
}

Expand Down Expand Up @@ -460,7 +459,7 @@ public void setFCDMode(Mode mode) throws UsbException, UsbClaimException
*/
public void setTunedFrequency(long frequency) throws SourceException
{
mListenerLock.lock();
getLock().lock();

try
{
Expand All @@ -473,7 +472,7 @@ public void setTunedFrequency(long frequency) throws SourceException
}
finally
{
mListenerLock.unlock();
getLock().unlock();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
import io.github.dsheirer.source.InvalidFrequencyException;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FrequencyController
{
Expand All @@ -42,11 +43,6 @@ public class FrequencyController
private boolean mSampleRateLocked = false;
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();

/**
* Lock protecting access to frequency control plane, source event processor subscriptions and event broadcasting
*/
private ReentrantLock mLock = new ReentrantLock();

/**
* Constructs an instance
* @param tunable that can be controlled by this frequency controller.
Expand All @@ -56,15 +52,6 @@ public FrequencyController(Tunable tunable)
mTunable = tunable;
}


/**
* Lock for controlling access to frequency control plane, event processor subscriptions and source event broadcasts.
*/
public ReentrantLock getFrequencyControllerLock()
{
return mLock;
}

/**
* Prepare for disposal of this instance.
*/
Expand Down Expand Up @@ -294,7 +281,7 @@ public void setFrequencyCorrection(double correction) throws SourceException
*/
public void addSourceEventProcessor(ISourceEventProcessor processor)
{
mLock.lock();
mTunable.getLock().lock();

try
{
Expand All @@ -305,7 +292,7 @@ public void addSourceEventProcessor(ISourceEventProcessor processor)
}
finally
{
mLock.unlock();
mTunable.getLock().unlock();
}
}

Expand All @@ -314,15 +301,15 @@ public void addSourceEventProcessor(ISourceEventProcessor processor)
*/
public void removeSourceEventProcessor(ISourceEventProcessor processor)
{
mLock.lock();
mTunable.getLock().lock();

try
{
mProcessors.remove(processor);
}
finally
{
mLock.unlock();
mTunable.getLock().unlock();
}
}

Expand Down Expand Up @@ -354,7 +341,7 @@ protected void broadcastSampleRateChange() throws SourceException

public void broadcast(SourceEvent event) throws SourceException
{
mLock.lock();
mTunable.getLock().lock();

try
{
Expand All @@ -365,13 +352,18 @@ public void broadcast(SourceEvent event) throws SourceException
}
finally
{
mLock.unlock();
mTunable.getLock().unlock();
}
}


public interface Tunable
{
/**
* Reentrant lock to synchronize threaded access to tuner controls.
*/
ReentrantLock getLock();

/**
* Gets the tuned frequency of the device
*/
Expand Down

0 comments on commit 80d173b

Please sign in to comment.