This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Dynamically allocate a RTX mailbox

Hello,

I'm using the Realview RTX and facing an issue of how to dynamically allocate a mailbox.

What I'm trying to create a reentrant function, which is called by some RTX tasks, and uses a mailbox to communicate with another task. I search through the RL-ARM manual and forum but all the examples show that the mailbox is declared globally. If so, the function is not reentrant anymore.

I have ask the Keil support but haven't received any response for this issue yet. So I put it here in hope that anyone could have the solution.

Thanks in advance for your answer.

Huy

Parents
  • Why do you need to dynamically create mailboxes? What is wrong with pre-allocated mailboxes?

    The sender must know if a mailbox exists, so you can't just create/destroy things without synchronization anyway.

    Please describe exactly what problem you are trying to solve. Your post do not mention why you need a reentrant function that allocates mailboxes. It isn't uncommon that you have a function that returns a mailbox ID - which (already existing) mailbox to use.

Reply
  • Why do you need to dynamically create mailboxes? What is wrong with pre-allocated mailboxes?

    The sender must know if a mailbox exists, so you can't just create/destroy things without synchronization anyway.

    Please describe exactly what problem you are trying to solve. Your post do not mention why you need a reentrant function that allocates mailboxes. It isn't uncommon that you have a function that returns a mailbox ID - which (already existing) mailbox to use.

Children
  • Hello,

    Sorry for not making it clear.

    I'm working on TcpNet with RTX and according to the TcpNet manual, all of the TcpNet related functions must be called in the networking task, which I assume that is the "tcp_poll" task.

    So for example I have 2 other tasks, let's say Task1 and Task2, want to call a TcpNet function, they can't call directly those but have to communicate to the "tcp_poll" task by using mailbox. The mailbox contains a data structure which comprises a TcpNet function that the task wants to call and the "Response mailbox" so that the "tcp_poll" task can return the result to the correct task requesting that function call. The "tcp_poll" task shall wait for the "Request mailbox" and determine which TcpNet function is requested, call it and return to result to the calling task by the "Response mailbox".

    So the data structure sent by the mailbox should be like this:

    struct TcpNetFuncObj
    {
       // This represents the TcpNet function to call
       U8 u8TcpNetFuncCode;
    
       // This is the "Response mailbox" or pointer to it
       ResponseMailbox;
    
       // Pointer to socket data structure
       void *ptSocketData;
    }
    

    I want to create a reentrant function for this communication so that Task1 and Task2 shall call the same function when they want to communicate with the "tcp_poll" task. Let's call it CommFunc and this is what it is supposed to do:

    
       void CommFunc (U8 func_code, void *socket_data)
       {
          struct TcpNetFuncObj *ptFuncObj;
    
          // Dynamically Allocate a Mailbox here.
          // I don't know how to do it but let's assume
          // that we can and I have a pointer to it
          // called ptResponseMailbox
    
    
          // Allocate the memory for TcpNetFuncObj
          ptFuncObj = malloc(sizeof(struct TcpNetFuncObj);
    
          // Setup the structure data
          ptFuncObj->u8TcpNetFuncCode = func_code;
          ptFuncObj->ResponseMailbox = ptResponseMailbox;
          ptFuncObj->ptSocketData = socket_data;
    
          // Send this to a Request Mailbox which is tcp_poll task is waiting.
          os_mbx_send(RequestMailbox, (void *)ptFuncObj, 10);
    
          // Now wait for the response from tcp_poll task
          os_mbx_wait(ptResponseMailbox, &(ptFuncObj), 10);
    
          // Process the response here
          ...
          // Deallocate ptFuncObj
          free(ptFuncObj);
    
          // Deallocate Response Mailbox here. I don't know this too.
          ...
    }
    

    As you can see, the Response Mailbox can't be declared globally, since Task1 and Task2 call the same CommFunc, but has two different Response Mailbox.

    And in the tcp_pool, it shall be like this:

    
          // Wait for the Request mailbox
          os_mbx_wait(RequestMailbox, &ptFuncObj, 10);
    
          // Call the TcpNet function here
          ...
    
          // Send the result back to Response Mailbox
          os_mbx_send(ptFuncObj->ResponseMailbox, ptFuncObj, 10);
    
          // Other processing here
          ...
    

    I hope this is clear enough so that anyone can give me the good solution for my situation.

    Thanks,

    Huy

  • If multiple tasks needs to send requests to a single receiving task, only one mailbox is needed for the transmission of the requests. But the receiving mailbox must have multiple slots so it can contain more than one mail.

    That means that several tasks can send their own messages to the single worker thread. It will pick up a message and perform an operation.

    In this case, you need to be able to send back a result too. So each requesting task must have its own mailbox for responses. And the worker thread must know which mailbox to use for the results. You might send the address of the response mailbox in the request. Or you may have an array of receiving mailboxes and in the request send the index of the receiving mailbox.

    In your case, your communication function could take a pointer to the receiving mailbox as a parameter. Or it could possibly use the ID of the currently running task to deduce what mailbox to use.

    In reality, you probably shouldn't even use a mailbox for the results, since each task is only allowed one single request. The request could contain a pointer to a global struct/union where the network task could pick up request parameters and where results should be stored. The network task can then (if it knows the task id of the requester) use os_evt_set() to inform the task that the answer is available.

    Most probably, there are even better ways to implement this but I'm much too hungry right now :)

  • Hello,

    That's what exactly what I'm doing with my code. Only one mailbox, Request Mailbox (with multiple item slots), is needed for the "tcp_poll" task, so that other tasks can send the multiple requests to.

    The problem here is with Response Mailboxes, which the request tasks waiting for the response from "tcp_poll" task. I think I shall use your idea to implement it by event, not mailbox, for the Response result since I'm in strict schedule right now and let me have no time to play around with it.

    But I think I'm still happy if know how to dynamically declare a mailbox in RTX. I have seen other RTOS implement this.

    Thanks again,

    Huy

  • Yes, you have one mailbox for the request.

    But the comment in your code - and text in your posts - says that you want to dynamically allocate a mailbox for each and every answer.

          // Dynamically Allocate a Mailbox here.
          // I don't know how to do it but let's assume
          // that we can and I have a pointer to it
          // called ptResponseMailbox
    

    These mailboxes should exsist for the full life of the requesting threads, so there will be no need to dynamically allocate them. You don't dynamically allocate a mail box outside your door whenever you expect that you may get a mail. You get your mailbox when moving to a new place, and keep it there.

    Event or mailbox for the answers depends a bit on if you need to wait for multiple things - if you have move tasks that may send data then you may need a mailbox that either gets the answer from the TCP task or from these other tasks.

  • void CommFunc (U8 func_code, void *socket_data)
       {
    
    // the following will dynamically declare AND initialize a mailbox called ptResponseMailbox.
    // Why did you not try this?  Is this not what ALL the examples do (save they all create the mailbox globally.)
    // The only difference is that the mailbox is declared locally, therefore "dynamically, on the stack"
    // bad things will happen if anyone tries to put something in or take something out of this mailbox after the instance of the mailbox goes away. (with a timeout on the mail_box_wait, this is sure to happen sometime)
    //If something is in the mailbox when this instance goes away, bad things may happen.
    // This really lends itself perfectly to a function that waits on an event and very poorly to a dynamic mailbox case.
    
    
          os_mbx_declare(ptResponseMailbox,1);
          os_mbx_init(ptRespnseMailbox,1);
    
    
          struct TcpNetFuncObj *ptFuncObj;
    
          // Dynamically Allocate a Mailbox here.
          // I don't know how to do it but let's assume
          // that we can and I have a pointer to it
          // called ptResponseMailbox
    
    
          // Allocate the memory for TcpNetFuncObj
          ptFuncObj = malloc(sizeof(struct TcpNetFuncObj);
    
          // Setup the structure data
          ptFuncObj->u8TcpNetFuncCode = func_code;
          ptFuncObj->ResponseMailbox = ptResponseMailbox;
          ptFuncObj->ptSocketData = socket_data;
    
          // Send this to a Request Mailbox which is
    



    tcp_poll task is waiting. os_mbx_send(RequestMailbox, (void *)ptFuncObj, 10);

    // Now wait for the response from tcp_poll task os_mbx_wait(ptResponseMailbox, &(ptFuncObj), 10);

    // Process the response here ... // Deallocate ptFuncObj free(ptFuncObj);

    // Deallocate Response Mailbox here. I don't know this too. ...
    }

  • Hello Robert,

    I thought about the way you suggest but I think there's a problem doing that way. When you declare the mailbox locally in the function, it shall be pushed to stack of the task when call other function, for example os_mbx_send(), or switch to another task, and the address use by the mailbox may be re-used for another variables in the new function/task. So there will be an issue here.

    I'm thinking about using _alloc_box() to dynamically allocate the mailbox. My idea is below:

    This is declare globally:

    // We declare this mailbox for getting size of it
    os_mbx_declare(ReponseMailbox, 1);
    
    // Declare a box for dynamically allocate mailbox
    _declare_box(ReponseMailboxMemPoll, sizeof(ResponseMailbox), 16);
    

    And in the CommFunc:

    void CommFunc (U8 func_code, void *socket_data)
    {
       ...
    
       // Dynamically allocate a mailbox
       ptResponseMailbox = _alloc_block(ResponseMailbox, sizeof(ResponseMailbox);
    
       // Allocate the memory for TcpNetFuncObj
       ptFuncObj = malloc(sizeof(struct TcpNetFuncObj);
    
       // Setup the structure data
       ptFuncObj->u8TcpNetFuncCode = func_code;
       ptFuncObj->ResponseMailbox = ptResponseMailbox;
       ptFuncObj->ptSocketData = socket_data;
    
       // Send this to a Request Mailbox which is
       // tcp_poll task is waiting.
       os_mbx_send(RequestMailbox, (void *)ptFuncObj, 10);
    
       // Now wait for the response from tcp_poll task
       os_mbx_wait(ptResponseMailbox, &(ptFuncObj), 10);
    
       // Process the response here ...
    
       // Deallocate ptFuncObj
       free(ptFuncObj);
    
       // Deallocate Response Mailbox here.
       _free_box(ResponseMailbox, ptResponseMailbox);
    }
    

    So do you think that my idea is correct?

    Huy

  • An object allocated on the stack is valid until the function in question returns.

    In this case, your function CommFunc() does not return until it has received the answer - so no problem that the memory gets reused while you are waiting for the answer.

  • No it is still is just as incorrect. The box allocated mailbox will be valid no longer than the stack allocated mailbox. If the os_mbx_wait times out after 10 OS_TMR_TICKS, you are hosed both ways.

    The only reason I showed how to dynamically allocate a mailbox was because that is what the OP asked, not because I would use the feature. It is VERY hard to make correct code that provides something usefull with a dynamic mailbox. It is better to use something that is actually helps you to create correct code. You would much better off using either a single static mailbox per task or a event. I would probably use the event, because it could be "hidden" (i.e the task calling the CommFunc does not need to have any prior knowledge of the Event use - which was the benefit you were trying to gain by using the dynamic mailbox in the first place)

  • I have posted to this thread as i think its of the same vain!

    I have a multiple (4of)task application passing messages via mailbox's (2of). I have one mailbox and its associated tasks works fine, i have a second task the call to the mail box works fine, however refuses to call the function within the task. See code below

    this task works fine
    
    void tcp_task (void) __task {
    
    U8 *ptr_tcp_task_msg;
    
    while (1) {
    
     if(os_mbx_wait(TCP_task_msg_box, (void**)&ptr_tcp_task_msg, 0x0000) == OS_R_OK ){
    /* if we have received a message decided what action to take */
       switch(*ptr_tcp_task_msg){
           case TCPTASK_DNS_ROUTINE:
           start_DNS_resolve((dns_struct*)tcp_task_msg);
           break;
    
        default:
           start_DNS_resolve((dns_struct*)tcp_task_msg);
           break;
        }
    }
    
                            /* DNS routine */
     if ( dns_resolve.DNS_active == __TRUE){
      get_host_by_name((U8*)dns_resolve.ptr_dns_task_data->name, dns_cbfunc);
      }
    
      main_TcpNet();
      os_tsk_pass();
      }
    }
    

    This mailbox functions fine however,

    The one below fails the message arrives will break on UARTSend function can see data in via message pointer but, fails to call function moves to os_tsk_pass.

    void communication_task (void) __task{
    
    global_msg_struct *ptr_global_msg;
    
       while (1) {
    
        if(os_mbx_wait(communication_task_msg_box,void**&ptr_global_msg, 0xFFFF) == OS_R_OK ){
    /* if we have received a message decided what action to take */
        //init_serial_port0();
        UARTSend((U8*) ptr_global_msg->tx_data );
    
        }
      os_tsk_pass();
      }
    }
    

    Any comments welcome on the topic, tried calling a simpler function init_serial_port() this fails in same way. Though when called from init_task at start functioned ok.

    The mailbox in the second case is external linked using OS_MBX as its in included file, but the mail box itself seems to work fine.

    Obviously issue with the boxes i guess any pointers? excuse the pun, or similar issues in the past.

    regards
    Darren