mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-11 08:07:24 -05:00
#1210 Resolves issue of tuner instances can't be garbage collected due to retained references. Updates several of the tuner controller and tuner editor classes to add/invoke dispose() methods to clear retained instance references. (#1216)
Co-authored-by: Denny Sheirer <sheirerd@ainfosec.com>
This commit is contained in:
@@ -39,7 +39,7 @@ public class AudioSegmentBroadcaster<T extends AudioSegment> extends Broadcaster
|
||||
@Override
|
||||
public void broadcast(T audioSegment)
|
||||
{
|
||||
for(Listener<T> listener : mListeners)
|
||||
for(Listener<T> listener : getListeners())
|
||||
{
|
||||
audioSegment.incrementConsumerCount();
|
||||
listener.receive(audioSegment);
|
||||
|
||||
@@ -194,7 +194,10 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor
|
||||
*/
|
||||
public void addListener(ISourceEventProcessor processor)
|
||||
{
|
||||
mProcessors.add(processor);
|
||||
if(!mProcessors.contains(processor))
|
||||
{
|
||||
mProcessors.add(processor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,8 +22,10 @@ package io.github.dsheirer.sample;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Broadcasts an item to multiple listeners
|
||||
@@ -32,8 +34,8 @@ public class Broadcaster<T> implements Listener<T>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(Broadcaster.class);
|
||||
private boolean mDebug;
|
||||
|
||||
protected List<Listener<T>> mListeners = new CopyOnWriteArrayList<>();
|
||||
private ReentrantLock mLock = new ReentrantLock();
|
||||
private List<Listener<T>> mListeners = new ArrayList<>();
|
||||
|
||||
public Broadcaster()
|
||||
{
|
||||
@@ -64,7 +66,16 @@ public class Broadcaster<T> implements Listener<T>
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mListeners.clear();
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mListeners.clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,14 +86,6 @@ public class Broadcaster<T> implements Listener<T>
|
||||
return !mListeners.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the listener is currently registered with this broadcaster
|
||||
*/
|
||||
public boolean hasListener(Listener<T> listener)
|
||||
{
|
||||
return listener != null && mListeners.contains(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* The count of listeners currently registered with this broadcaster
|
||||
*/
|
||||
@@ -96,7 +99,7 @@ public class Broadcaster<T> implements Listener<T>
|
||||
*/
|
||||
public List<Listener<T>> getListeners()
|
||||
{
|
||||
return mListeners;
|
||||
return Collections.unmodifiableList(mListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,9 +109,21 @@ public class Broadcaster<T> implements Listener<T>
|
||||
*/
|
||||
public void addListener(Listener<T> listener)
|
||||
{
|
||||
if(listener != null && !mListeners.contains(listener))
|
||||
if(listener != null)
|
||||
{
|
||||
mListeners.add(listener);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
if(!mListeners.contains(listener))
|
||||
{
|
||||
mListeners.add(listener);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +134,16 @@ public class Broadcaster<T> implements Listener<T>
|
||||
{
|
||||
if(listener != null)
|
||||
{
|
||||
mListeners.remove(listener);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +152,16 @@ public class Broadcaster<T> implements Listener<T>
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
mListeners.clear();
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mListeners.clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,21 +169,30 @@ public class Broadcaster<T> implements Listener<T>
|
||||
*/
|
||||
public void broadcast(T t)
|
||||
{
|
||||
if(mDebug)
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
for(Listener<T> listener : mListeners)
|
||||
if(mDebug)
|
||||
{
|
||||
mLog.debug("Sending [" + t + "] to listener [" + listener.getClass() + "]");
|
||||
listener.receive(t);
|
||||
mLog.debug("Finished sending to listener [" + listener.getClass() + "]");
|
||||
for(Listener<T> listener : mListeners)
|
||||
{
|
||||
mLog.debug("Sending [" + t + "] to listener [" + listener.getClass() + "]");
|
||||
listener.receive(t);
|
||||
mLog.debug("Finished sending to listener [" + listener.getClass() + "]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(Listener<T> listener : mListeners)
|
||||
{
|
||||
listener.receive(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
for(Listener<T> listener : mListeners)
|
||||
{
|
||||
listener.receive(t);
|
||||
}
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,10 +110,17 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene
|
||||
if(getChannelSourceManager() != null)
|
||||
{
|
||||
getChannelSourceManager().stopAllChannels();
|
||||
getChannelSourceManager().dispose();
|
||||
mChannelSourceManager = null;
|
||||
}
|
||||
|
||||
broadcast(new TunerEvent(this, Event.NOTIFICATION_SHUTTING_DOWN));
|
||||
getTunerController().stop();
|
||||
getTunerController().dispose();
|
||||
|
||||
mTunerEventBroadcaster.clear();
|
||||
mTunerFrequencyErrorMonitor = null;
|
||||
mTunerErrorListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,13 +49,13 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
protected Broadcaster<INativeBuffer> mNativeBufferBroadcaster = new Broadcaster();
|
||||
protected FrequencyController mFrequencyController;
|
||||
private int mMiddleUnusableHalfBandwidth;
|
||||
private double mUsableBandwidthPercentage;
|
||||
private Listener<SourceEvent> mSourceEventListener;
|
||||
private int mMeasuredFrequencyError;
|
||||
private double mUsableBandwidthPercentage;
|
||||
private SourceEventListenerToProcessorAdapter mSourceEventListener;
|
||||
private NativeBufferWaveRecorder mRecorder;
|
||||
private ITunerErrorListener mTunerErrorListener;
|
||||
public FrequencyErrorCorrectionManager mFrequencyErrorCorrectionManager;
|
||||
private DecimalFormat mFrequencyErrorPPMFormat = new DecimalFormat("0.0");
|
||||
private FrequencyErrorCorrectionManager mFrequencyErrorCorrectionManager;
|
||||
|
||||
/**
|
||||
* Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels
|
||||
@@ -78,6 +78,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
return mFrequencyErrorCorrectionManager;
|
||||
}
|
||||
|
||||
protected void dispose()
|
||||
{
|
||||
getFrequencyErrorCorrectionManager().dispose();
|
||||
mNativeBufferBroadcaster.clear();
|
||||
mFrequencyController.dispose();
|
||||
mSourceEventListener.dispose();
|
||||
mTunerErrorListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform startup operations
|
||||
* @throws SourceException if there is an error that makes this tuner controller unusable
|
||||
@@ -141,7 +150,6 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
setFrequency(config.getFrequency());
|
||||
setFrequencyCorrection(config.getFrequencyCorrection());
|
||||
getFrequencyErrorCorrectionManager().setEnabled(config.getAutoPPMCorrectionEnabled());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,8 +25,9 @@ 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.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class FrequencyController
|
||||
{
|
||||
@@ -40,8 +41,8 @@ public class FrequencyController
|
||||
private double mFrequencyCorrection = 0.0d;
|
||||
private double mSampleRate = 0.0d;
|
||||
private boolean mLocked = false;
|
||||
|
||||
private List<ISourceEventProcessor> mProcessors = new CopyOnWriteArrayList<>();
|
||||
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();
|
||||
private ReentrantLock mLock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -52,6 +53,15 @@ public class FrequencyController
|
||||
mTunable = tunable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for disposal of this instance.
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mProcessors.clear();
|
||||
mTunable = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this frequency controller is locked by a remote process. A locked state indicates that neither
|
||||
* the frequency nor the sample rate should be changed. The remote process will typically be a downstream
|
||||
@@ -276,9 +286,18 @@ public class FrequencyController
|
||||
*/
|
||||
public void addListener(ISourceEventProcessor processor)
|
||||
{
|
||||
if(!mProcessors.contains(processor))
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mProcessors.add(processor);
|
||||
if(!mProcessors.contains(processor))
|
||||
{
|
||||
mProcessors.add(processor);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +306,16 @@ public class FrequencyController
|
||||
*/
|
||||
public void removeFrequencyChangeProcessor(ISourceEventProcessor processor)
|
||||
{
|
||||
mProcessors.remove(processor);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
mProcessors.remove(processor);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -318,9 +346,18 @@ public class FrequencyController
|
||||
|
||||
public void broadcast(SourceEvent event) throws SourceException
|
||||
{
|
||||
for(ISourceEventProcessor processor : mProcessors)
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
processor.process(event);
|
||||
for(ISourceEventProcessor processor : mProcessors)
|
||||
{
|
||||
processor.process(event);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,14 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor
|
||||
{
|
||||
private Broadcaster<SourceEvent> mSourceEventBroadcaster = new Broadcaster<>();
|
||||
|
||||
/**
|
||||
* Prepare for disposal
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mSourceEventBroadcaster.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorted set of tuner channels being sourced by this source manager. Set is ordered by frequency lowest to highest
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,15 @@ public class FrequencyErrorCorrectionManager
|
||||
mTunerController = tunerController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of this manager instance and nullify references.
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
setEnabled(false);
|
||||
mTunerController = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables automatic frequency PPM correction.
|
||||
* @param enabled to true to turn on automatic PPM adjustments.
|
||||
@@ -80,7 +89,7 @@ public class FrequencyErrorCorrectionManager
|
||||
*/
|
||||
private void applyCorrection()
|
||||
{
|
||||
if(mEnabled && mTunerController.hasMeasuredFrequencyError())
|
||||
if(mEnabled && mTunerController != null && mTunerController.hasMeasuredFrequencyError())
|
||||
{
|
||||
double frequencyCorrection = mTunerController.getFrequencyCorrection();
|
||||
frequencyCorrection -= mTunerController.getPPMFrequencyError();
|
||||
|
||||
@@ -26,7 +26,6 @@ import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerFactory;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager;
|
||||
import io.github.dsheirer.source.tuner.usb.USBTunerController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -300,16 +299,6 @@ public class RTL2832TunerController extends USBTunerController
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manager for applying automatic frequency error PPM adjustments to the tuner controller based on
|
||||
* frequency error measurements received from certain downstream decoders (e.g. P25).
|
||||
* @return manager
|
||||
*/
|
||||
public FrequencyErrorCorrectionManager getFrequencyErrorCorrectionManager()
|
||||
{
|
||||
return mFrequencyErrorCorrectionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides updates for measured frequency error so that the updates can also be applied to the
|
||||
* frequency error correction manager for automatic PPM updating.
|
||||
@@ -348,11 +337,11 @@ public class RTL2832TunerController extends USBTunerController
|
||||
{
|
||||
if(hasEmbeddedTuner())
|
||||
{
|
||||
return TunerClass.RTL2832.toString() + "/" + getTunerType().getLabel() + " " + mDescriptor.getSerial();
|
||||
return TunerClass.RTL2832 + "/" + getTunerType().getLabel() + " " + mDescriptor.getSerial();
|
||||
}
|
||||
else
|
||||
{
|
||||
return TunerClass.RTL2832.toString() + " " + mDescriptor.getSerial();
|
||||
return TunerClass.RTL2832 + " " + mDescriptor.getSerial();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -99,6 +99,8 @@ public class DiscoveredTunerEditor extends Editor<DiscoveredTuner> implements ID
|
||||
|
||||
super.setItem(tuner);
|
||||
|
||||
mEditorScroller.remove(mEditor);
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
mEditor = TunerFactory.getEditor(mUserPreferences, getItem(), mTunerManager);
|
||||
|
||||
@@ -116,7 +116,7 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**r
|
||||
* Indicates if the controls are currently being loaded with values.
|
||||
*/
|
||||
protected boolean isLoading()
|
||||
@@ -337,7 +337,7 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
|
||||
if(mEnabledButton == null)
|
||||
{
|
||||
mEnabledButton = new JButton(BUTTON_STATUS_ENABLE);
|
||||
mEnabledButton.setToolTipText("Enable or disable the tuner for use by sdrtrunk, or attempt to reset a tuner error");
|
||||
mEnabledButton.setToolTipText("Enable or disable the tuner for use by sdrtrunk");
|
||||
mEnabledButton.addActionListener(e ->
|
||||
{
|
||||
switch(getEnabledButton().getText())
|
||||
@@ -493,6 +493,11 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
turnOffRecorder();
|
||||
|
||||
getFrequencyControl().clearListeners();
|
||||
getFrequencyCorrectionSpinner().removeChangeListener(mFrequencyAndCorrectionChangeListener);
|
||||
|
||||
if(mDiscoveredTuner != null)
|
||||
{
|
||||
mDiscoveredTuner.removeTunerStatusListener(this);
|
||||
@@ -501,9 +506,12 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
|
||||
{
|
||||
mDiscoveredTuner.getTuner().removeTunerEventListener(this);
|
||||
}
|
||||
|
||||
mDiscoveredTuner = null;
|
||||
}
|
||||
|
||||
turnOffRecorder();
|
||||
mUserPreferences = null;
|
||||
mTunerManager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user