C

实现自定义队列

本主题解释了如何在Qt Quick Ultralite中实现自定义队列。

概览

队列在处理Qt Quick Ultralite中的事件时是一个重要部分。最突出的例子之一就是在输入处理中使用队列。通过使用EventQueue,事件可以在Qt Quick Ultralite执行周期中合适的时间传播给Qt Quick Ultralite,以便进行处理。

EventQueue的默认队列实现是DoubleQueue,它位于platform\common\baremetal\doublequeue.cpp中。只要只有一个写入者,它就是中断安全的。如果有多个写入者,必须确保EventQueue::postEvent()不会被另一个EventQueue::postEvent()中断。此外,使用DoubleQueue的线程安全性无法保证,这也限制了它的使用。

大多数实时操作系统(RTOSes)提供自己的队列,这些队列可能提供额外的优势,例如中断安全性和线程安全性。它们还支持在多个任务使用中的应用程序中所需的适当任务间通信。使用RTOS队列可以在通常由于DoubleQueue的限制而阻止其使用的情况下,使用EventQueue

本主题涵盖了如何将MessageQueueInterface API调整适配以使用RTOS提供的队列,例如。EventQueue的平台实现使得在Qt Quick Ultralite中使用自定义队列成为可能。

调整自定义队列

用于实现自定义队列的队列抽象称为MessageQueueInterface API。它可以通过包括platform\messagequeue.h头文件来访问。该API提供抽象函数和带默认实现的函数。这些函数必须重新实现。参见MessageQueueInterface类的文档,获取更多信息。

注意:这里显示的代码片段来自一个示例实现,您可以在 Qt Quick Ultralite 的安装目录下找到它(platform\boards\qt\example-baremetal\examplequeue.cpp)。出于演示目的,它使用了简单的环形缓冲区作为队列后端。

要获得一个有效实现,以下 MessageQueueInterface API 中的函数必须实现:

  • 构造函数 - 实现的构造函数必须至少接受一个表示队列可以容纳的项的最大数量的整数。它还必须调用 MessageQueueInterface::MessageQueueInterface()。

    以下是实现的一个示例构造函数

    MyMessageQueue(const uint32_t &capacity, const uint32_t &messageSize)
        : MessageQueueInterface()
        , mQueue(NULL)
        , mOverrunFlag(false)
    {
        void *memory = qul_malloc(sizeof(Private::CircularBuffer));
        mQueue = new (memory) Private::CircularBuffer(capacity, messageSize);
    }
  • MessageQueueInterface::discardSupported() 和 MessageQueueInterface::overwriteSupported() - 这些函数表示队列实现是否支持丢弃、覆盖或两者。它们必须返回 truefalse

    注意:其中一个函数必须返回 true,以便EventQueue 能够工作。

  • MessageQueueInterface::enqueueOrDiscard() - 这个函数将给定消息推送到队列的末尾。如果队列已满,则必须丢弃消息并设置越界状态。如果消息被推送到队列,则必须返回 MessageQueueStatus::Success,如果消息被丢弃,则返回 MessageQueueStatus::MessageDiscarded

    注意:应将 message 参数的内容复制到队列,因为原始消息可能会被删除。

    如果实现不支持丢弃,即使该函数永远不会被调用,也必须返回 MessageQueueStatus::DiscardingNotSupported

    以下是一个函数的示例实现

    MessageQueueStatus enqueueOrDiscard(const void *message) QUL_DECL_OVERRIDE
    {
        if (mQueue->isFull()) {
            // Discard message
            mOverrunFlag = true;
            return MessageQueueStatus::MessageDiscarded;
        }
    
        mQueue->pushBack(message);
        return MessageQueueStatus::Success;
    }
  • MessageQueueInterface::enqueueOrOverwrite() - 这个函数将给定消息推送到队列的末尾。如果队列已满,则必须用给定消息覆盖队列中的最老消息并设置越界状态。如果消息成功推送,则函数必须返回 MessageQueueStatus::Success,如果用给定消息覆盖了较老的消息,则返回 MessageQueueStatus::MessageOverwritten

    注意:与 enqueueOrDiscard() 类似,应将 message 的内容复制到队列,因为原始消息可能会被删除。

    如果实现不支持覆盖,即使该函数永远不会被调用,也必须返回 MessageQueueStatus::OverwritingNotSupported

    当事件类型是指针时,EventQueue 不支持覆盖。如果需要此功能,实现必须对被覆盖的指针进行适当的内存处理。

    以下是在不支持覆盖的情况下实现 MessageQueueInterface::enqueueOrOverwrite() 的示例

    MessageQueueStatus enqueueOrOverwrite(const void *message) QUL_DECL_OVERRIDE
    {
        return MessageQueueStatus::OverwriteNotSupported;
    }
  • MessageQueueInterface::receive() - 在给定的超时时间内从队列中弹出消息并返回它。超时以毫秒为单位指定。如果超时值为零,函数无限期地等待消息;如果超时值为负,则函数根本不等待。

    如果成功地从队列中检索到一条消息,则必须保证message参数包含检索到的消息(即,检索到的消息内容必须复制到message指向的地址),并且函数必须返回MessageQueueStatus::Success。如果队列是空的,则必须返回MessageQueueStatus::EmptyQueueMessageQueueStatus::Timeout

    以下是对MessageQueueInterface::receive()的示例实现。

    MessageQueueStatus receive(void *message, int32_t timeout = 0) QUL_DECL_OVERRIDE
    {
        (void) timeout; // This example does not implement timeout handling.
    
        if (mQueue->isEmpty())
            return MessageQueueStatus::EmptyQueue;
    
        mQueue->popFront(message);
        return MessageQueueStatus::Success;
    }
  • MessageQueueInterface::isEmpty() - 如果队列空,则返回代码true,否则返回false
  • MessageQueueInterface::isOverrun()和MessageQueueInterface::clearOverrun() - 这些函数返回并修改队列的溢出状态。
    bool isOverrun() const QUL_DECL_OVERRIDE { return mOverrunFlag; }
    
    void clearOverrun() QUL_DECL_OVERRIDE { mOverrunFlag = false; }

还有实现消息入队和接收中断安全版本的函数。这些方法具有默认实现,该方法调用Qt Quick Ultralite提供的对应函数。但是,建议重新实现以下函数以确保中断安全:

现在,应该有一个使用自定义队列的MessageQueueInterface API的有效实现。EventQueue使用MessageQueue便利API与队列实现接口。然而,仅凭MessageQueue,不知道有关自定义实现的任何东西, εκτός από το ότι implements MessageQueueInterface。相反,MessageQueue调用requestQueue()函数以获取它使用的正确队列。该函数接收capacitymessageSize参数,分别表示队列可以容纳的项目数量和队列将要使用消息的大小。如果该实现不支持请求的容量或消息大小,则此函数必须返回一个指向队列实现实例的指针或空指针。

requestQueue()的示例实现

MessageQueueInterface *requestQueue(size_t queueCapacity, size_t messageSize)
{
    void *queue = qul_malloc(sizeof(MyMessageQueue));

    if (queue == NULL) {
        return NULL;
    }

    MessageQueueInterface *interface = new (queue) MyMessageQueue(queueCapacity, messageSize);
    return interface;
}

当队列不再需要时,MessageQueue调用deleteQueue(),该函数负责删除和分配队列资源。

deleteQueue()的示例实现

void deleteQueue(MessageQueueInterface *queue)
{
    MyMessageQueue *mq = static_cast<MyMessageQueue *>(queue);
    mq->~MyMessageQueue();
    qul_free(mq);
}

消息队列必须知道允许的最大消息大小,以便它可以处理给定消息大小超过实现尺寸限制的情况。此信息可以通过实现maximumQueueMessageSize()提供。函数必须返回以字节为单位的最大支持大小,或者如果队列支持任意大小的消息,则返回SIZE_MAX。如果返回SIZE_MAX,则实现负责处理消息,无论其大小如何。

以下示例实现了maximumQueueMessageSize(),它返回SIZE_MAX

size_t maximumQueueMessageSize()
{
    return LONG_MAX;
}

在某些Qt许可证下可用。
了解更多。