1. Related Data Structure
// Only one copy of its instance (sThread, which is shared by all
// TimeoutTask objects) existed in DSS, see TimeoutTask declare and its
// Initialize implementation.
// TimeoutTask::Initialize called in StartServer of RunServer.cpp as following
// TimeoutTask::Initialize()
// Therefore all members in this object, also only one of copy
// its own instance. e.g., fMutex, fQueue of TimeoutTaskThread
// TimeoutTaskThread is one task object rather than thread object
class TimeoutTaskThread : public IdleTask
{
public:
//All timeout tasks get timed out from this thread
TimeoutTaskThread() : IdleTask(), fMutex() {this->SetTaskName("TimeoutTask");}
virtual ~TimeoutTaskThread(){}
private:
//this thread runs every minute and checks for timeouts
enum
{
kIntervalSeconds = 60 //UInt32
};
virtual SInt64 Run();
OSMutex fMutex;
OSQueue fQueue; // All TimeoutTask objects are enqueued this queue
friend class TimeoutTask;
};
class TimeoutTask
{
//TimeoutTask is not a derived object off of Task, to add flexibility as
//to how this object can be utilitized
public:
//Call Initialize before using this class
static void Initialize();
//Pass in the task you'd like to send timeouts to.
//Also pass in the timeout you'd like to use. By default, the timeout is 0 (NEVER).
TimeoutTask(Task* inTask, SInt64 inTimeoutInMilSecs = 60);
~TimeoutTask();
//MODIFIERS
// Changes the timeout time, also refreshes the timeout
void SetTimeout(SInt64 inTimeoutInMilSecs);
// Specified task will get a Task::kTimeoutEvent if this
// function isn't called within the timeout period
void RefreshTimeout() { fTimeoutAtThisTime = OS::Milliseconds() + fTimeoutInMilSecs; Assert(fTimeoutAtThisTime > 0); }
void SetTask(Task* inTask) { fTask = inTask; }
private:
Task* fTask;
SInt64 fTimeoutAtThisTime;
SInt64 fTimeoutInMilSecs;
//for putting on our global queue of timeout tasks
OSQueueElem fQueueElem;
// static object instanced in Initialize that was called in StartServer of RunServer.cpp as following
// TimeoutTask::Initialize();
static TimeoutTaskThread* sThread;
friend class TimeoutTaskThread;
};
2. function implementation of above data structure
2.1 TimeoutTask::Initialize
void TimeoutTask::Initialize()
{
if (sThread == NULL)
{
// unqiue instanced object of TimeoutTaskThread
sThread = NEW TimeoutTaskThread();
// enqueue TimeoutTaskThread object (one task object only really) just created
// to task queue of some taskthread
sThread->Signal(Task::kStartEvent);
// because TimeoutTaskThread derived from IdleTask, which derived from Task.
// This TimeoutTaskThread(sThread) object is with fTaskQueueElem, and
// fTimerHeapElem members, and In Task Construct function, fTaskQueueElem and
// fTimeHeapElem instanced, and enclosed as followings
// fTaskQueueElem.SetEnclosingObject(this);
// fTimerHeapElem.SetEnclosingObject(this);
// then fTaskQueueElem is enqueued into task queue of TaskThread in task's Signal
// function, fUseThisThread->fTaskQueue.EnQueue(&fTaskQueueElem); or
// TaskThreadPool::sTaskThreadArray[theThreadIndex]->fTaskQueue.EnQueue(&fTaskQueueElem);
}
}
2.2 TimeoutTask::TimeoutTask(Task* inTask, SInt64 inTimeoutInMilSecs)
// TimeoutTask construct function.
// instance own fQueueElem, associate with fTask to inTask(from outer), noted later
// set timeout time
// enclosing own into own fQueueElem
// enqueue own fQueueElem into TimeoutTaskThread(sThread)'s fQueue,
// equiv to, enqueue own into TimeoutTaskThread(sThread)'s fQueue
TimeoutTask::TimeoutTask(Task* inTask, SInt64 inTimeoutInMilSecs)
: fTask(inTask), fQueueElem()
{
fQueueElem.SetEnclosingObject(this);
this->SetTimeout(inTimeoutInMilSecs);
if (NULL == inTask) fTask = (Task *) this;
Assert(sThread != NULL); // this can happen if RunServer intializes tasks in the wrong order
OSMutexLocker locker(&sThread->fMutex);
sThread->fQueue.EnQueue(&fQueueElem);
}
2.3 TimeoutTask::SetTimeout(SInt64 inTimeoutInMilSecs)
void TimeoutTask::SetTimeout(SInt64 inTimeoutInMilSecs)
{
fTimeoutInMilSecs = inTimeoutInMilSecs; // Timeout time milliseconds unit
if (inTimeoutInMilSecs == 0)
fTimeoutAtThisTime = 0;
else
fTimeoutAtThisTime = OS::Milliseconds() + fTimeoutInMilSecs;
}
2.4 TimeoutTask::RefreshTimeout()
TimeoutTask::RefreshTimeout()
{ // refresh timeout time
fTimeoutAtThisTime = OS::Milliseconds() + fTimeoutInMilSecs;
Assert(fTimeoutAtThisTime > 0);
}
2.5 TimeoutTask::~TimeoutTask()
TimeoutTask::~TimeoutTask()
{
OSMutexLocker locker(&sThread->fMutex);
// unenqueue from TimeoutTaskThread's fQueue
sThread->fQueue.Remove(&fQueueElem);
}
2.6 TimeoutTaskThread::Run()
// TimeoutTaskThread::Run run in some TaskThread's env
// by theTimeout = theTask->Run(), when some theTask is
// exactly TimeoutTaskThread object in TaskThread::Entry()
SInt64 TimeoutTaskThread::Run()
{
//ok, check for timeouts now. Go through the whole queue
OSMutexLocker locker(&fMutex);
SInt64 curTime = OS::Milliseconds();
//always default to 60 seconds but adjust to smallest interval > 0
SInt64 intervalMilli = kIntervalSeconds * 1000;
SInt64 taskInterval = intervalMilli;
// iterator all TimeoutTask object from TimeoutTask's fQueue
for (OSQueueIter iter(&fQueue); !iter.IsDone(); iter.Next()) {
TimeoutTask* theTimeoutTask = (TimeoutTask*)iter.GetCurrent()->GetEnclosingObject();
// if it's time to time this task out, signal it
if ((theTimeoutTask->fTimeoutAtThisTime > 0)
&& (curTime >= theTimeoutTask->fTimeoutAtThisTime)) {
// timeout occur here
#if TIMEOUT_DEBUGGING
qtss_printf("TimeoutTask %"_S32BITARG_" timed out.
Curtime = %"_64BITARG_"d, timeout time =
%"_64BITARG_"d\n",(SInt32)theTimeoutTask,
curTime, theTimeoutTask->fTimeoutAtThisTime);
#endif
// theTimeoutTask->fTask may be RTSPSession(Task), or
// RTPSession(Task) etc, forever, each RTSPSession, RTPSession
// such Task associates with one own TimeoutTask by its member
// fTimeoutTask pointer. They assocaite with each other.
// Therefore, by theTimeoutTask's fTask, find its related some real Task
// and enqueue this real Task into task queue of some TaskThread with
// TimeoutEvent
theTimeoutTask->fTask->Signal(Task::kTimeoutEvent);
} else {
taskInterval = theTimeoutTask->fTimeoutAtThisTime - curTime;
if ( (taskInterval > 0) && (theTimeoutTask->fTimeoutInMilSecs > 0)
&& (intervalMilli > taskInterval) ) {
// set timeout to 1 second past this task's timeout
intervalMilli = taskInterval + 1000;
}
#if TIMEOUT_DEBUGGING
qtss_printf("TimeoutTask %"_S32BITARG_" not being timed out.
Curtime = %"_64BITARG_"d. timeout time =
%"_64BITARG_"d\n", (SInt32)theTimeoutTask,
curTime, theTimeoutTask->fTimeoutAtThisTime);
#endif
}
}
(void)this->GetEvents(); // we must clear the event mask!
OSThread::ThreadYield(); // do nothing in Linux
#if TIMEOUT_DEBUGGING
qtss_printf ("TimeoutTaskThread::Run interval seconds=
%"_S32BITARG_"\n", (SInt32) intervalMilli/1000);
#endif
// this will be return value theTimeout = theTask->Run(), denote exec me in intervalMill
// milliseconds next time
return intervalMilli; // don't delete me!
}
3. TimeoutEvent Process by Real Task such as RTSPSession Task, RTPSession Task etc
TaskThread::Entry() ==> theTask->Run()
if theTask is RTSPSession Task or RTPSession
Task, following Run will be called
3.1 TimeoutEvent Process of RTSPSession::Run()
if ((events & Task::kTimeoutEvent) || (events & Task::kKillEvent))
fLiveSession = false; // mark session dead
.......
// Make absolutely sure there are no resources being occupied by the session
// at this point.
this->CleanupRequest();
// Only delete if it is ok to delete!
if (fObjectHolders == 0) return -1; // destructor will be called
// If we are here because of a timeout, but we can't delete because someone
// is holding onto a reference to this session, just reschedulethe timeout.
//
// At this point, however, the session is DEAD.
return 0;
// session can't be destructor because of reference num to this
// session not zero; maybe Run called next time, the fObjectHolders is zero.
3.2 TimeoutEvent Process of RTPSession::Run()
//if we have been instructed to go away, then let's delete ourselves
if ((events & Task::kKillEvent) || (events & Task::kTimeoutEvent)
|| (fModuleDoingAsyncStuff)) {
if (!fModuleDoingAsyncStuff) {
if (events & Task::kTimeoutEvent)
fClosingReason = qtssCliSesCloseTimeout;
//deletion is a bit complicated. For one thing, it must happen from within
//the Run function to ensure that we aren't getting events when we are deleting
//ourselves. We also need to make sure that we aren't getting RTSP requests
//(or, more accurately, that the stream object isn't being used by any other
//threads). We do this by first removing the session from the session map.
#if RTPSESSION_DEBUGGING
qtss_printf("RTPSession %"_S32BITARG_": about to be killed.
Eventmask = %"_S32BITARG_"\n",(SInt32)this, (SInt32)events);
#endif
// We cannot block waiting to UnRegister, because we have to
// give the RTSPSessionTask a chance to release the RTPSession.
OSRefTable* sessionTable = QTSServerInterface::GetServer()->GetRTPSessionMap();
Assert(sessionTable != NULL);
if (!sessionTable->TryUnRegister(&fRTPMapElem)) {
this->Signal(Task::kKillEvent);// So that we get back to this place in the code
return kCantGetMutexIdleTime;
}
// The ClientSessionClosing role is allowed to do async stuff
fModuleState.curTask = this;
fModuleDoingAsyncStuff = true; // So that we know to jump back to the
fCurrentModule = 0; // right place in the code
// Set the reason parameter
theParams.clientSessionClosingParams.inReason = fClosingReason;
// If RTCP packets are being generated internally for this stream,
// Send a BYE now.
RTPStream** theStream = NULL;
UInt32 theLen = 0;
if (this->GetPlayFlags() & qtssPlayFlagsSendRTCP) {
SInt64 byePacketTime = OS::Milliseconds();
for (int x = 0; this->GetValuePtr(qtssCliSesStreamObjects, x,
(void**)&theStream, &theLen) == QTSS_NoErr; x++) {
if (theStream && *theStream != NULL)
(*theStream)->SendRTCPSR(byePacketTime, true);
}
}
}
// KillEvent or TimeoutEvent, session cleanup
//at this point, we know no one is using this session, so invoke the
//session cleanup role. We don't need to grab the session mutex before
//invoking modules here, because the session is unregistered and
//therefore there's no way another thread could get involved anyway
UInt32 numModules = QTSServerInterface::GetNumModulesInRole(
QTSSModule::kClientSessionClosingRole);
{
for (; fCurrentModule < numModules; fCurrentModule++) {
fModuleState.eventRequested = false;
fModuleState.idleTime = 0;
QTSSModule* theModule = QTSServerInterface::GetModule(
QTSSModule::kClientSessionClosingRole, fCurrentModule);
(void)theModule->CallDispatch(QTSS_ClientSessionClosing_Role, &theParams);
// If this module has requested an event, return and wait for the event to transpire
// return 0
if (fModuleState.eventRequested) return fModuleState.idleTime;
// If the module has requested idle time...
}
}
return -1; // doing this will cause the destructor to get called.
}
4. TimeoutTask of Real Task(such as RTSPSession, RTPSession)
4.1 TimeoutTask Instance of RTSPSession
// when TimeoutTask constructor called, its fQueueElem enclosing TimeoutTask itself
// and fQueueElem was enqueued into TimeoutTaskThread(really one Task rather than thread)
// (sThread)'s fQueue. Notes: sThread of TimeoutTask is static variable, therefore only
// one copy of its instance is shared by all TimeoutTask objects
RTSPSessionInterface::RTSPSessionInterface()
: QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kRTSPSessionDictIndex)),
Task(),
fTimeoutTask(NULL, QTSServerInterface::GetServer()->GetPrefs()->GetRealRTSPTimeoutInSecs() * 1000),
.........
fRTSPSession3GPP(QTSServerInterface::GetServer()->GetPrefs()->Get3GPPEnabled() ),
fRTSPSession3GPPPtr(&fRTSPSession3GPP)
{
// TimeoutTask and RTSPSession(Task) associate with each other
fTimeoutTask.SetTask(this);
fSocket.SetTask(this);
}
4.2 TimeoutTask Instance of RTPSession
Similiar with RTSPSession
4.3 RefreshTimeout of RTSPSession
In RTSPSession::Run
case case kFilteringRequest:
{
// We received something so auto refresh
// The need to auto refresh is because the api doesn't allow a module to refresh at this point
// fTimeoutTask, TimeoutTask object of RTSPSession
fTimeoutTask.RefreshTimeout();
........
}
44 RefreshTimeout of RTPSession
In RTPStream, fSession saved RTPSession object, In some functions implementation
of RTPStream, fSession's RefreshTimeout is called.
fSession->RefreshTimeout();
5. Summary