blob: 5d2e56b9f909119afac7fe5a6f10cdfc82506ecb [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
#include "ExSMTask.h"
#include "ExSMTaskList.h"
#include "ExSMReadyList.h"
#include "ExSMGlobals.h"
#include "ExSMQueue.h"
#include "Ipc.h"
ExSMTask::ExSMTask(const sm_target_t &receiver,
uint32_t queueSize,
int32_t *scheduledAddr,
NAMemory *heap,
ex_tcb *tcb,
SMConnection *smConnection)
: inQueue_(NULL)
, outQueue_(NULL)
, receiver_(receiver)
, scheduledAddr_(scheduledAddr)
, heap_(heap)
, tcb_(tcb)
, receiveCount_(0)
, smConnection_(smConnection)
, hashBucketNext_(NULL)
, readyListNext_(NULL)
, readyListPrev_(NULL)
, recvChunk_buffer_(NULL)
, recvChunk_msgSize_(0)
, recvChunk_chunkSize_(0)
, recvChunk_bytesSoFar_(0)
, sendChunk_ackArrived_(false)
{
exsm_assert(heap_, "Invalid NAMemory pointer");
// In the queue constructors we add 1 to the passed in size because
// the queue class only guarantees storage for (size - 1)
// entries. The queue class also raises its size to a power of
// 2. See ExSMQueue.h for details. One effect is, if you want
// storage for N entries and N is a power of 2 (say 4) the queue
// will allocate storage for 2N (8) entries which wastes some
// memory.
inQueue_ = new ExSMQueue(queueSize + 1, heap);
exsm_assert(inQueue_, "Failed to allocate input task queue");
outQueue_ = new ExSMQueue(queueSize + 1, heap);
exsm_assert(outQueue_, "Failed to allocate output task queue");
}
ExSMTask::~ExSMTask()
{
if (recvChunk_buffer_)
{
// If recvChunk_buffer_ is not NULL, the task was waiting for an
// arrival that never came. The arrival never made its way to the
// task output queue. No one else has a pointer to this buffer so
// the task needs to release the buffer before going away.
IpcMessageBuffer *msgBuf = (IpcMessageBuffer *) recvChunk_buffer_;
msgBuf->decrRefCount();
recvChunk_buffer_ = NULL;
}
if (inQueue_)
{
while (!inQueue_->isEmpty())
{
ExSMQueue::Entry &entry = inQueue_->getHeadEntry();
IpcMessageBuffer *msgBuf = (IpcMessageBuffer *) entry.getData();
exsm_assert(msgBuf, "Invalid msgBuf pointer in SM queue entry");
exsm_assert(msgBuf->getRefCount() == 1,
"Ref count should be 1 for buffer in SM task queue");
msgBuf->decrRefCount();
inQueue_->removeHead();
}
}
if (outQueue_)
{
ExSMTaskList *taskList = ExSMGlobals::GetExSMGlobals()->getSMTaskList();
bool taskListLocked = false;
// This task has already been removed from the globals task list
// by ExSMGlobals::removeTask(). Therefore it is not necessary to
// obtain the task list lock before emptying the output
// queue. However, for now, the output queue and completed task
// queue will be locked as a pair for consistency with other code
// locations. In other words, in any code path where the output
// queue can become empty, we lock the task list and remove the
// task from the completed task queue.
if (!outQueue_->isEmpty())
{
taskList->lock();
taskListLocked = true;
}
while (!outQueue_->isEmpty())
{
ExSMQueue::Entry &entry = outQueue_->getHeadEntry();
IpcMessageBuffer *msgBuf = (IpcMessageBuffer *) entry.getData();
exsm_assert(msgBuf, "Invalid msgBuf pointer in SM queue entry");
exsm_assert(msgBuf->getRefCount() == 1,
"Ref count should be 1 for buffer in SM task queue");
msgBuf->decrRefCount();
outQueue_->removeHead();
}
if (taskListLocked)
{
// NOTE: The SM ready list is accessed by both threads (main and
// reader) but does not have its own lock. By convention,
// modifications to the ready list are always performed while
// holding a lock on the SM task list (a global collection of
// all SM tasks in this process).
ExSMReadyList *readyList = ExSMGlobals::GetExSMGlobals()->getReadyList();
readyList->remove(this);
taskList->unlock();
}
} // if (outQueue_)
delete inQueue_;
delete outQueue_;
}
// Methods for RECEIVE chunk protocol
void ExSMTask::recvChunk_Enter(void *buffer, uint32_t msgSize,
uint32_t chunkSize)
{
recvChunk_buffer_ = buffer;
recvChunk_bytesSoFar_ = 0;
recvChunk_msgSize_ = msgSize;
recvChunk_chunkSize_ = chunkSize;
}
void ExSMTask::recvChunk_Exit()
{
recvChunk_buffer_ = NULL;
recvChunk_bytesSoFar_ = 0;
recvChunk_msgSize_ = 0;
recvChunk_chunkSize_ = 0;
}
void ExSMTask::recvChunk_Receive(uint32_t bytesReceived)
{
recvChunk_bytesSoFar_ += bytesReceived;
}
void *ExSMTask::recvChunk_GetPrepostAddr() const
{
void *result = NULL;
IpcMessageBuffer *msgBuf = (IpcMessageBuffer *) recvChunk_buffer_;
if (msgBuf)
result = msgBuf->data(recvChunk_bytesSoFar_);
return result;
}
bool ExSMTask::recvChunk_MoreExpected() const
{
return (recvChunk_bytesSoFar_ < recvChunk_msgSize_);
}
// Methods for SEND chunk protocol
void ExSMTask::sendChunk_SetAckArrived(bool b)
{
bool notify = false;
if (sendChunk_ackArrived_ == false && b == true)
notify = true;
sendChunk_ackArrived_ = b;
if (notify)
{
exsm_assert(scheduledAddr_, "Invalid scheduledAddr_ pointer");
*scheduledAddr_ = 1;
}
}