邮箱原理和创建

邮箱原理

以上是邮箱的大致思路,使用了两个队列,一个用于作为信息缓存—当然是有长度的,一个用于存放等待任务,我们还需要理解这两个队列运行逻辑,两个任务任何一个为空的时候,另外一个必定为非空

对于消息的缓存,采用了读和写索引,有消息进入的时候,通过写索引写入,有消息读出的时候,通过读索引读出

可以看到是一个环形缓冲区,但如下我们创建的是一个数组,我们加入写索引和读索引来实现环形缓冲的功能,除此以外,我们还想在缓冲区中有优先级高消息写入,那实现方法大体是读索引往前移动,写入到读索引上,而写索引往后移动

邮箱创建

typedef struct _tMbox
{
	// 事件控制块
	// 该结构被特意放到起始处,以实现tSem同时是一个tEvent的目的
	tEvent event;
	// 当前的消息数量
  uint32_t count;
  // 读取消息的索引
  uint32_t read;
  // 写消息的索引
  uint32_t write;
  // 最大允许容纳的消息数量
  uint32_t maxCount;
  // 消息存储缓冲区
  void ** msgBuffer;
}tMbox;

这里的重点是msgBuffer,他是一个指向指针的指针,可以理解为数组指针

  • void * 可以表示任意类型的指针
  • void ** 则是一个指向指针的指针,表示一个可以存储多个指针的数组
void tMboxInit (tMbox * mbox, void ** msgBuffer, uint32_t maxCount) 
{
	tEventInit(&mbox->event, tEventTypeMbox);
	mbox->msgBuffer = msgBuffer;
	mbox->maxCount = maxCount;
	mbox->read = 0;
	mbox->write = 0;
	mbox->count = 0;
}

由于邮箱在功能上也有等待,所以底层也有计数信号量,其余无需赘述

邮箱的获取和释放

uint32_t tMboxWait (tMbox * mbox, void **msg, uint32_t waitTicks) 
{
    uint32_t status = tTaskEnterCritical();

    // 首先检查消息计数是否大于0
    if (mbox->count > 0)
    {
    	// 如果大于0的话,取出一个
        --mbox->count;
			// 取出消息
        *msg = mbox->msgBuffer[mbox->read++];
        // 同时读取索引前移,如果超出边界则回绕
        if (mbox->read >= mbox->maxCount) 
        {
        	mbox->read = 0;
        }
    	tTaskExitCritical(status); 
    	return tErrorNoError;
    }
    else                                                                
    { 
         // 然后将任务插入事件队列中
        tEventWait(&mbox->event, currentTask, (void *)0,  tEventTypeMbox, waitTicks);
        tTaskExitCritical(status);
        // 最后再执行一次事件调度,以便于切换到其它任务
        tTaskSched();
        // 当切换回来时,从tTask中取出获得的消息
        *msg = currentTask->eventMsg;
        // 取出等待结果
        return currentTask->waitEventResult;
    }
}
uint32_t tMboxNoWaitGet (tMbox * mbox, void **msg)
{
    uint32_t status = tTaskEnterCritical();
    // 首先检查消息计数是否大于0
    if (mbox->count > 0)
    {
    	// 如果大于0的话,取出一个
        --mbox->count;
        *msg = mbox->msgBuffer[mbox->read++];
        // 同时读取索引前移,如果超出边界则回绕
        if (mbox->read >= mbox->maxCount) 
        {
        	mbox->read = 0;
        }
    	tTaskExitCritical(status); 
    	return tErrorNoError;
    }
    else                                                                
    {
     	// 否则,返回资源不可用
        tTaskExitCritical(status);
    	return tErrorResourceUnavaliable;
    }    
}
uint32_t tMboxNotify (tMbox * mbox, void * msg, uint32_t notifyOption)
{
    uint32_t status = tTaskEnterCritical();        
    // 检查是否有任务等待
    if (tEventWaitCount(&mbox->event) > 0)
    {
    	// 如果有的话,则直接唤醒位于队列首部(最先等待)的任务
        tTask * task = tEventWakeUp(&mbox->event, (void *)msg, tErrorNoError ) 
        // 如果这个任务的优先级更高,就执行调度,切换过去
        if (task->prio < currentTask->prio)
        {
            tTaskSched(); 
				}
    }
    else
    {
    	// 如果没有任务等待的话,将消息插入到缓冲区中
    	if (mbox->count >= mbox->maxCount) 
    	{
		    tTaskExitCritical(status);
		    return tErrorResourceFull;
    	}
		// 可以选择将消息插入到头,这样后面任务获取的消息的时候,优先获取该消息
		if (notifyOption & tMBOXSendFront)
		{
			if (mbox->read <= 0) 
			{
				mbox->read = mbox->maxCount - 1;
			} 
			else 
			{
				--mbox->read;
			}
			mbox->msgBuffer[mbox->read] = msg;
		}
		else 
		{
			mbox->msgBuffer[mbox->write++] = msg;
			if (mbox->write >= mbox->maxCount)
			{
				mbox->write = 0;
			}
		}
		// 增加消息计数
		mbox->count++;
   	}
	tTaskExitCritical(status);
	return tErrorNoError;
}

tMboxWait中,首先检查消息计数是否大于0—邮箱里面是否有邮件,有的话就将消息取出来—赋值给msg,并对计数减减;否则将任务插入事件队列中,但也会对msg操作

tMboxNoWaitGet与tMboxWait大同小异,唯一区别就是不需要对任务进行操作

tMboxNotify中,对判断的对象就改为了任务,有的话就唤醒并调度;否则将消息插入缓冲区中

但是在tMboxNotify不同的是,消息可以选择插入到头还是尾,方便优先获得该消息

邮箱的清空和删除

void tMboxFlush (tMbox * mbox)
{
    uint32_t status = tTaskEnterCritical();        
    // 如果队列中有任务等待,说明邮箱已经是空的了,不需要再清空
    if (tEventWaitCount(&mbox->event) == 0) 
    {
        mbox->read = 0;
        mbox->write = 0;
        mbox->count = 0;
    }
    tTaskExitCritical(status);
}
uint32_t tMboxDestroy (tMbox * mbox)
{       
    uint32_t status = tTaskEnterCritical(); 
    // 清空事件控制块中的任务
    uint32_t count = tEventRemoveAll(&mbox->event, (void *)0, tErrorDel);  
    tTaskExitCritical(status);
    // 清空过程中可能有任务就绪,执行一次调度
    if (count > 0) 
    {
        tTaskSched();
    } 
    return count;  
}

tMboxFlush中,对于清空,我们只需要把邮箱中的消息清空

tMboxDestroy中,对于销毁,我们需要把邮箱等待队列中的任务取出,并调度

邮箱的状态查询

typedef struct _tMboxInfo {
	// 当前的消息数量
    uint32_t count;
    // 最大允许容纳的消息数量
    uint32_t maxCount;
    // 当前等待的任务计数
    uint32_t taskCount;
}tMboxInfo;

void tMboxGetInfo (tMbox * mbox, tMboxInfo * info)
{
    uint32_t status = tTaskEnterCritical();
    // 拷贝需要的信息
    info->count = mbox->count;
    info->maxCount = mbox->maxCount;
    info->taskCount = tEventWaitCount(&mbox->event);
    tTaskExitCritical(status);
}

新增tMboxInfo结构体,以及tMboxGetInfo ,用于状态获取