Menu

Using Q_NEW() and QF_gc() as malloc/free.

2015-08-14
2015-08-18
  • Harry Rostovtsev

    Pretty much what the title says. I'm trying to avoid using the stack for some rather large buffers that are used only shortly and temporarily (convert a hex array to a string so it can be printed, for example) and then garbage collecting the event. Is this ok as long as I remember to garbage collect and not do it too much?

    For example:

    SomeLargeEvt *evt = Q_NEW(SomeLargeEvt, SOME_SIG);
    .. do stuff with evt->dataBuf[100] ...
    ..
    ..
    .. Finish up ..
    QF_gc( evt );

    Is this safe or am I playing with fire?

     

    Last edit: Harry Rostovtsev 2015-08-14
  • Quantum Leaps

    Quantum Leaps - 2015-08-14

    I wouldn't use dynamic events.

    Instead I would use the QMPool facility directly. This is exactly why this facility is provided in the framework. It is less overhead than allocating dynamic events, but still, the memory-pool operations are thread-safe (i.e., are protected by critical sections).

    You can allocate raw memory block via QMPool_get() and release the block via QMPool_put(). Before using a pool, you need to initialize it with QMPool_init().

     
  • Harry Rostovtsev

    Fantastic! Thanks for your help, Miro.

     
  • Rémi Desgrange

    Rémi Desgrange - 2015-08-14

    Correct me if i'm wrong, but, Q_NEW aren't really dynamic. In main of program that use QP, I declare queue storage like this :

    //where MyClassEvt inherit from QP::QEvt
    static QF_MPOOL_EL(Evt::MyClassEvt) l_myClassEvtSto[10];
    

    So when i'm doing Q_NEW, it's not really dynamic, what's is the perk of QMPool ? I found the implentation in PSiCC2 book, but no app notes or something like that. I'm doing I lot of Q_NEW in my application, since it does not doing any malloc/free I can't see where it's dangerous. The only thing that are dangerous is the fact that when you do :

    Q_NEW(MyClassEvt, MY_EVT_SIG)
    

    and MyClassEvt is like that

    class MyClassEvt : public QP::QEvt {
        public:
        std::string m_attr;
     }
    

    then you don't execute the ctor of std::string class when Q_NEW is performed, it's perfectly normal but can be confusing for some people. (and yes std::string is a bad example).

     
  • Harry Rostovtsev

    Is there an example of initializing and using QMPool? I seem to be having a problem setting it up. My code is below:

    #define QM_MEMPOOL_SIZE               512
    static QMPool l_nativeMemPoolSto[QM_MEMPOOL_SIZE];
    int main(void)
    {
    
        QF_init();       /* initialize the framework and the underlying RT kernel */
        /* initialize the general memory pool */
       QMPool_init(
             l_nativeMemPoolSto,
             sizeof(l_nativeMemPoolSto),
             sizeof(l_nativeMemPoolSto[0]),
             QM_MEMPOOL_SIZE
       );
    
       .... etc ...
    
    }
    

    I get a warning:

    4: warning: passing argument 2 of 'QMPool_init' makes pointer from integer without a cast [enabled by default]
    ../Common/sys/qpc_5.3.1/include/qmpool.h:151:6: note: expected 'void * const' but argument is of type 'unsigned int'

    If I do the following, it compiles with no errors or warnings but this doesn't look quite
    right:

    #define QM_MEMPOOL_SIZE               512
    static QMPool l_nativeMemPoolSto[QM_MEMPOOL_SIZE];
    int main(void)
    {
    
        QF_init();       /* initialize the framework and the underlying RT kernel */
        /* initialize the general memory pool */
        QMPool_init(
             &l_nativeMemPoolSto[QM_MEMPOOL_SIZE],
             l_nativeMemPoolSto,
             QM_MEMPOOL_SIZE,
             sizeof(l_nativeMemPoolSto[0])
       );
    
       .... etc ...
    
    }
    

    Above code seems to run but before I get too far with this (start using put() and get() ), is above correct?

    Is there an example anywhere of how to correctly initialize the memory pool like this? The port example seems to take 2 storage arrays which makes no sense for my application:

    /* Package-scope objects ---------------------------------------------------*/
    QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL];          /* allocate the event pools */
    uint8_t QF_maxPool_;                   /* number of initialized event pools */
    
    /*..........................................................................*/
    void QF_poolInit(void * const poolSto, uint32_t const poolSize,
                     uint32_t const evtSize)
    {
                          /* cannot exceed the number of available memory pools */
        Q_REQUIRE(QF_maxPool_ < (uint8_t)Q_DIM(QF_pool_));
                /* please initialize event pools in ascending order of evtSize: */
        Q_REQUIRE((QF_maxPool_ == (uint8_t)0)
                 || (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - (uint8_t)1])
                     < (QEvtSize)evtSize));
                    /* perfom the platform-dependent initialization of the pool */
        QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize);
        ++QF_maxPool_;                                         /* one more pool */
    }
    
     

    Last edit: Harry Rostovtsev 2015-08-18
  • Quantum Leaps

    Quantum Leaps - 2015-08-18

    Hiere is an anotated example of initializing two memory pools:

    QMPool myMemPool1;             /* memory pool object #1 (global) */
    static uint8_t memPoolSto1[512]; /* storage for a memory pool #1 */
    
    QMPool myMemPool2;              /* memory pool object #2 (global) */
    static uint8_t memPoolSto2[1024]; /* storage for a memory pool #2 */
    
    
    QMPool_init(&myMemPool1,
                memPoolSto1,
                sizeof(memPoolSto1),
                10U);        /* blocks of 10 bytes each */
    
    QMPool_init(&myMemPool2,
                memPoolSto2,
                sizeof(memPoolSto2),
                25U);        /* blocks of 25 bytes each */
    

    As you can see, the object-oriented class pattern is used consistently here. The first argument to the QMPool_init() call is a pointer to a QMPool instance. The three remaining arguments are the storage for the pool, the size of this storage (in bytes), and the block-size of this pool (in bytes).

    And here are examples of using the pools:

    void *block1 = QMPool_get(&myMemPool1, 0U); /* asserts on empty pool */
    /* block1 is guaranteed to be not NULL */
    ~ ~ ~
    QMPool_put(&myMemPool1, block1);
    
    void *block2 = QMpool_get(&myMemPool2, 5U); /* non-asserting version */
    if (block2 != (void *)0) { /* allocation succeeded? */
       ~ ~ ~
    }
    
    ~ ~ ~
    QMPool_put(&myMemPool2, block2);
    

    I hope that these examples make sense to you.

     
  • Harry Rostovtsev

    Yes, these make sense. Thank you. I was WAY off :)

    Quick question about the sizes of the pools:
    Does the 10U and 25U mean that there are 10 pools of 512 bytes and 25 pools 1024 bytes or does that mean that there is a max of 512 bytes and 1024 bytes in the pool and I can allocate a max of 10 blocks and 25 blocks of whatever size as long as they are smaller than the total storage of the pools?

     

    Last edit: Harry Rostovtsev 2015-08-18
    • Quantum Leaps

      Quantum Leaps - 2015-08-18

      The memory pools managed by the QMPool class are fixed block-size pools. The block-size of each pool must be determined at initialization and cannot be changed later.

      So, for example, a pool initialized with block-size of 10U will contain blocks of at least 10-bytes each. I'm saying "at least", because the QMPool_init() function tries to align all blocks at the (void*) pointer boundary (so the block size must be a muliple of sizeof(void*)).

      Consequently, the number of blocks in the pool is the size of the provided storage divided by the (rounded-up) block-size.

      In the previous examples, it might work out that myMemPool1 with storage size 512 bytes will have block size of 12 bytes (10U rounded up to 12, assuming sizeof(void*)==4), so it will hold 85 blocks and will have 4 bytes unused.)

      Similarly, myMemPool2 with storage size 1024 bytes will have block size of 28 bytes (25U rounded up to 28), so it will hold 36 blocks and will have 16 bytes unused.)

      Again, I hope that this makes sense to you.

       

      Last edit: Quantum Leaps 2015-08-18
      • Harry Rostovtsev

        Yep. Makes sense. Thanks again.

         

Log in to post a comment.