Skip to content

Commit

Permalink
#1802 Traffic channels stuck in TEARDOWN. Resolves: (#1829)
Browse files Browse the repository at this point in the history
1. Tuner channel processing runnables were being interruptibly shutdown, preventing the channel from clearing any owned locks (ie DMRTrafficChannelManager) causing thread deadlock and eventually causing out of memory situation or tying up all threads in the thread pool to the point that the application stopped processing any data.

2. DMR Traffic Channel Manager was erroneously processing non-owned traffic channel events.

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
  • Loading branch information
DSheirer and Dennis Sheirer committed Feb 9, 2024
1 parent 0491b08 commit 6b5a1fb
Show file tree
Hide file tree
Showing 21 changed files with 122 additions and 265 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/

package io.github.dsheirer.channel.state;
Expand Down Expand Up @@ -47,9 +44,37 @@ public abstract class AbstractDecoderState extends Module implements ActivitySum
protected Broadcaster<IDecodeEvent> mDecodeEventBroadcaster = new Broadcaster<>();
protected Listener<DecoderStateEvent> mDecoderStateListener;
private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener();
private boolean mRunning;

public abstract DecoderType getDecoderType();

/**
* Implements module start and sets the mRunning flag to true so that messages can be processed.
*/
@Override
public void start()
{
mRunning = true;
}

/**
* Implements the module stop and sets the mRunning flag to false to stop message processing
*/
@Override
public void stop()
{
mRunning = false;
}

/**
* Indicates if this module is running and can process/pass messages down to sub-class implementations.
* @return true if running
*/
public boolean isRunning()
{
return mRunning;
}

/**
* Provides subclass reference to the decode event broadcaster
*/
Expand Down Expand Up @@ -145,4 +170,22 @@ public void receive(DecoderStateEvent event)
}
}

/**
* Message listener that only passes messages while we're running. This is important because each of the decoders
* can process blocks of samples and that can result in additional messages being generated even after shutdown
* and so we shut off the processing of decoded messages when we're commanded to stop. The sample processing thread
* cannot be shutdown or forcefully interrupted because downstream decoder and channel states may have acquired
* locks that have to be properly released.
*/
private class MessageListener implements Listener<IMessage>
{
@Override
public void receive(IMessage message)
{
if(isRunning())
{
receive(message);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -54,6 +54,7 @@ public DecoderState()
@Override
public void start()
{
super.start();
//Broadcast the existing identifiers (as add events) so that they can be received by external listeners
mIdentifierCollection.broadcastIdentifiers();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,8 @@ private void stopProcessing(Channel channel) throws ChannelException
processingChain.removeFrequencyChangeListener(channel);
channel.resetFrequencyCorrection();

//Notify all processing chains that this channel is shutting down so that if this is a traffic channel,
//the owning parent channel's traffic channel manager can cleanup it's accounting.
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_STOP));
mChannelEventBroadcaster.removeListener(processingChain);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -145,11 +145,10 @@ private void endCallEvent()
@Override
public void start()
{
super.start();
getIdentifierCollection().update(getChannelNameIdentifier());
}

@Override
public void stop() {}
@Override
public void init() {}
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -115,9 +115,4 @@ public String getActivitySummary()
public void init()
{
}

@Override
public void stop()
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,8 @@ public void receiveDecoderStateEvent(DecoderStateEvent event)
@Override
public void start()
{
super.start();

//Change the default (45-second) traffic channel timeout to 1 second
if(mChannel.isTrafficChannel())
{
Expand All @@ -1483,9 +1485,4 @@ public void start()
public void init()
{
}

@Override
public void stop()
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ private void createTrafficChannels()
}

mAvailableTrafficChannels.addAll(trafficChannelList);

//Keep track of the complete list so that we can check channel events to determine if the channel event
//is for a traffic channel owned by this traffic channel manager.
mAllocatedTrafficChannels = Collections.unmodifiableList(trafficChannelList);
}
}
Expand Down Expand Up @@ -795,15 +798,19 @@ else if(!decodeEvent2.getDetails().endsWith(CHANNEL_START_REJECTED))
}

/**
* Process traffic channel processing stop or processing start rejected events.
/**
* Process channel events from the ChannelProcessingManager to account for owned child traffic channels.
* Note: this method sees events for ALL channels and not just DMR channels managed by this instance.
*
* @param channelEvent to process
*/
@Override
public synchronized void receive(ChannelEvent channelEvent)
{
Channel channel = channelEvent.getChannel();

if(channel.isTrafficChannel())
//Only process the event if it's one of the traffic channels managed by this instance.
if(mAllocatedTrafficChannels.contains(channel))
{
switch(channelEvent.getEvent())
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -53,20 +53,7 @@ public DecoderType getDecoderType()
}

@Override
public void start()
{
}

@Override
public void stop()
{
}

@Override
public void init()
{

}
public void init() {}

@Override
public void receive(IMessage message)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -150,20 +150,5 @@ public void reset()
}

@Override
public void start()
{

}

@Override
public void stop()
{

}

@Override
public void init()
{

}
public void init() {}
}

0 comments on commit 6b5a1fb

Please sign in to comment.