org.red5.server.net.rtmp.ReceivedMessageTaskQueue Maven / Gradle / Ivy
/*
* RED5 Open Source Media Server - https://github.com/Red5/
*
* Copyright 2006-2016 by respective authors (see below). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.red5.server.net.rtmp;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.red5.server.net.rtmp.message.Packet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contains queue of tasks for processing messages in the specified channel. Ensures that all messages which has got in channel will be processed sequentially.
*
* @author Maria Chabanets ([email protected])
*/
public class ReceivedMessageTaskQueue {
private final static Logger log = LoggerFactory.getLogger(ReceivedMessageTaskQueue.class);
/**
* Stream id.
*/
private final int streamId;
/**
* Task queue.
*/
private final Queue tasks = new ConcurrentLinkedQueue();
/**
* Listener which tries to process message from queue if queue has been changed.
*/
private final IReceivedMessageTaskQueueListener listener;
public ReceivedMessageTaskQueue(int streamId, IReceivedMessageTaskQueueListener listener) {
this.streamId = streamId;
this.listener = listener;
}
/**
* Adds new task to the end of the queue.
*
* @param task
* received message task
*/
public void addTask(ReceivedMessageTask task) {
tasks.add(task);
Packet packet = task.getPacket();
// don't run the deadlock guard if timeout is <= 0
if (packet.getExpirationTime() > 0L) {
// run a deadlock guard so hanging tasks will be interrupted
task.runDeadlockFuture(new DeadlockGuard(task));
}
if (listener != null) {
listener.onTaskAdded(this);
}
}
/**
* Removes the specified task from the queue.
*
* @param task
* received message task
*/
public void removeTask(ReceivedMessageTask task) {
if (tasks.remove(task)) {
task.cancelDeadlockFuture();
if (listener != null) {
listener.onTaskRemoved(this);
}
}
}
/**
* Gets first task from queue if it can be processed. If first task is already in process it returns null.
*
* @return task that can be processed or null otherwise
*/
public ReceivedMessageTask getTaskToProcess() {
ReceivedMessageTask task = tasks.peek();
if (task != null && task.setProcessing()) {
return task;
}
return null;
}
/**
* Removes all tasks from the queue.
*/
public void removeAllTasks() {
for (ReceivedMessageTask task : tasks) {
task.cancelDeadlockFuture();
}
tasks.clear();
}
public int getStreamId() {
return streamId;
}
/**
* Prevents deadlocked message handling.
*/
private class DeadlockGuard implements Runnable {
private final ReceivedMessageTask task;
/**
* Creates the deadlock guard to prevent a message task from taking too long to setProcessing.
*
* @param task
*/
private DeadlockGuard(ReceivedMessageTask task) {
this.task = task;
if (log.isTraceEnabled()) {
log.trace("DeadlockGuard is created for {}", task);
}
}
/**
* Save the reference to the task, and wait until the maxHandlingTimeout has elapsed. If it elapsed, remove task and stop its thread.
* */
public void run() {
Packet packet = task.getPacket();
if (log.isTraceEnabled()) {
log.trace("DeadlockGuard is started for {}", task);
}
// skip processed packet
if (packet.isProcessed()) {
log.debug("DeadlockGuard skipping task for processed packet {}", task);
} else if (packet.isExpired()) {
// try to interrupt thread
log.debug("DeadlockGuard skipping task for expired packet {}", task);
} else {
// if the message task is not yet done or is not expired interrupt
// if the task thread hasn't been interrupted check its live-ness
// if the task thread is alive, interrupt it
Thread taskThread = task.getTaskThread();
if (taskThread == null) {
log.debug("Task has not start yet {}", task);
} else if (!taskThread.isInterrupted() && taskThread.isAlive()) {
log.warn("Interrupting unfinished active task {}", task);
taskThread.interrupt();
} else {
log.debug("Unfinished task {} already interrupted", task);
}
}
// remove this task from the queue in any case
removeTask(task);
}
}
}