This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Using a queue to pass pointers to memory?

I'm trying to use a queue between two tasks (in separate files) for communication. In one task I have a few variables in memory that I want the other task to be able to write to, so I was trying to pass the pointers with a queue. However what I'm confused about is if I add the variables to the queue, will their direct address be available to the other task when it dequeues them or does the queue copy the values to a temporary location? I think the queue struct has confused me on this a bit.

  • The Queue module does not copy anything. When you call Queue_put, your structure is placed into a doubly linked list. Note: the first element of your structure should be a Queue_Elem. A Queue_Elem is a structure with next and prev fields. Here's an example of a structure that can be placed on a Queue instance

    struct myStruct {

        Queue_Elem elem; // should be first (not required, but it makes your life much easier!)

        void *buffer;

        unsigned int bufferSize;

        ...

    }

    The Mailbox module is another communication mechanism. It does copy the contents of the message into an internal buffer (and then copies them out when reading).

    Todd

  • Hi Todd,

    I'm still a little confused, say I have a variable uint32_t myvar that I want to add to the queue such that when someone reads the queue they get a pointer to that variable ( which is what a queue does). What I'm not sure about is if myvar needs to be defined via the struct or not? Does that make sense?
  • You cannot place a single variable in a Queue. Here's the code to Queue_put() (which places the elem at the end of the linked list).

    Void Queue_put(Queue_Object *obj, Queue_Elem *elem)
    {
        UInt key;

        key = Hwi_disable();

        elem->next = &(obj->elem);
        elem->prev = obj->elem.prev;
        obj->elem.prev->next = elem;
        obj->elem.prev = elem;

        Hwi_restore(key);
    }

    The second argument is a Queue_Elem. The Queue_Elem holds the next and prev pointers. As I noted earlier, you have to have a Queue_Elem at the start of the item you are passing into the queue. So you something like this:

    typedef struct MyStruct {

        Queue_Elem elem; // should be first (not required, but it makes your life much easier!)

        void *buffer;

        uint32_t myvar;

    } MyStruct;

    MyStruct foo;

    void main(void)
    {

        MyStruct *bar;

        Error_Block eb;

        Error_init(&eb)

        Queue_Handle queueHandle; 

        queueHandle = Queue_create(&eb);

        if (queueHandle == NULL) {

            //deal with this...

        }

        foo.myvar = 5;

        Queue_put(queueHandle, &foo);

        ...

        bar = Queue_get(queueHandle);

        bar.myvar = 7;  // bar is now pointing to foo.

      

     

  • Todd, I think that helped a lot, I did have one further clarification if you don't mind. Let's say MyStruct is:

    typedef struct MyStruct {

       Queue_Elem elem; // should be first (not required, but it makes your life much easier!)

       uint8_t buffer[100];

       uint8_t size;

    } MyStruct;

    MyStruct foo;

    Now suppose a writer task does say memcpy(&(foo.buffer[0]), &srcbuffer,50) and foo.size = 50 and does a Queue_put(queueHandle, &foo). However, before a reader task is able to read the queue, the writer task does another memcpy and queues up another message in the same way. Does this mean that the first data is overwritten? If so, what is a better solution? I understand that Mailbox's provide for a copy based solution but I don't see how its possible to read multiple messages like you can with a queue. I feel like i'm missing something since I was under the impression queues could grow to any size needed but that would seem useless under my current understanding..

  •  

    CM2015 said:
    Does this mean that the first data is overwritten?

    Not if you use a different MyStruct variable. For example, you make foo an array and manage them accordingly. A very common design would be to have an array of size N (where N is whatever you want). At the beginning, put all the elements on a "free" queue. Then TaskA would get one element off the "free" queue, act accordingly on it and then put it on a "full" queue and post a semaphore. TaskB, who is blocked on that semaphore, would unblock and get it off the "full" queue. When it is done with it, to would put it back on the "free" queue. Note: you'd probably use another semaphore to synchronize the "free" queue also (so TaskA can block until there is a free one).

    Note: this is very similar to the design of Mailbox, but Mailbox copies the contents into an internal buffer. You specify how big each message is and how many messages it can contain. Let's say you say that each message is M bytes and there are N of them. So the internal buffer will be N * M + N * sizeof(Queue_Elem) bytes (note: you don't need to add the Queue_Elem in your structure). Each of the internal messages are put onto a "free" queue during initialization. When you call Mailbox_post, a free element is popped from the "free" list. Your message is copied into the free message then placed on the "full" queue (and the "full" semaphore is posted). The Mailbox_pend checks the "full" semaphore. If it available (e.g. there is a message on the "full" queue), it pops from the "full" queue, copies the message into the supplied buffer and then places the spent element back onto the "free" list (and posts the "free" semaphore). So you can have multiple messages in a Mailbox. Note: the semaphores used in Mailbox are counting to allow for multiple Mailbox_posts before a Mailbox_pend.

    Note: since Mailbox is copied based, instead of having the buffer in the struct, many people have just a point to a buffer. During initialization, they set the buffer pointer in each structure to a unique buffer. That way the copies are faster.

    Todd