Звезда не активнаЗвезда не активнаЗвезда не активнаЗвезда не активнаЗвезда не активна
 

Understanding Function Queues in C

Function queues are a powerful programming construct, particularly useful in embedded systems and real-time applications. This article explains a set of macros implemented in C to handle various types of function queues, including standard queues, delayed queues, and parameterized queues.

1. Standard Function Queue

The fQ macro defines a basic circular queue to store and execute function pointers. It includes:

  • q##_last and q##_first for managing queue indices.
  • q##_Queue, an array of function pointers.
  • q##_Push, which adds a function pointer to the queue.
  • q##_Pull, which retrieves and executes a function from the queue.
#define fQ(q, Q_SIZE)                            \
    volatile int q##_last = 0;                   \
    int q##_first = 0;                           \
    void (*q##_Queue[Q_SIZE])(void);             \
    int q##_Push(void (*pointerQ)(void)) {       \
        if ((q##_last + 1) % Q_SIZE == q##_first)\
            return 1; /* Queue is full */        \
        q##_Queue[q##_last++] = pointerQ;        \
        q##_last %= Q_SIZE;                      \
        return 0; /* Success */                  \
    }                                            \
    int (*q##_Pull(void))(void) {                \
        if (q##_last == q##_first)               \
            return 1; /* Queue is empty */       \
        q##_Queue[q##_first++]();                \
        q##_first %= Q_SIZE;                     \
        return 0;  /* Success */ 		 \
    }

2. Delayed Function Queue

The del_fQ macro extends the standard queue to support delayed execution:

  • q##_del_fQueue stores delayed function pointers.
  • q##_execTime and q##_time track execution times.
  • q##_Push_delayed schedules a function for future execution.
  • q##_Tick processes delayed functions when their time arrives.
  • q##_Revoke cancels a scheduled function.
#define del_fQ(q, Q_SIZE)                                           \
    int q##_time = 0;                                               \
    void (*q##_del_fQueue[Q_SIZE])(void);                           \
    int q##_execArr[Q_SIZE] = { 0 };   	                            \
    int q##_execTime[Q_SIZE];                                       \
    fQ(q, Q_SIZE)                                                   \
    int q##_Push_delayed(void (*pointerF)(void), int delayTime){    \
        int q##_fullQ = 1;                                          \
        for (int i = 0; i < Q_SIZE; i++) {                          \
            if (!q##_execArr[i]) {                                  \
                q##_del_fQueue[i] = pointerF;                       \
                q##_execArr[i] = 1;                                 \
                q##_execTime[i] = q##_time + delayTime;             \
                q##_fullQ = 0;                                      \
                break;                                              \
            }                                                       \
        }                                                           \
        return q##_fullQ;                                           \
    }                                                               \
    void q##_Tick(void){                                            \
        for (int i = 0; i < Q_SIZE; i++) {                          \
            if (q##_execTime[i] == q##_time) {                      \
                if (q##_execArr[i]) {                               \
                    q##_Push(q##_del_fQueue[i]);                    \
                    q##_execArr[i] = 0;                             \
                }                                                   \
            }                                                       \
        }                                                           \
        q##_time++; /* Increment time */                            \
    }                                                               \
    int q##_Revoke(void (*pointerF)(void)){                         \
        int result = 1;                                             \
        for (int i = 0; i < Q_SIZE; i++) {                          \
            if (q##_del_fQueue[i] == pointerF) {                    \
                q##_execArr[i] = 0;                                 \
                result = 0;                                         \
            }                                                       \
        }                                                           \
        return result;                                              \
    }

3. Parameterized Function Queue

The fQP macro allows parameterized function execution:

  • q##_funcs stores function pointers.
  • q##_params stores associated parameters.
  • q##_Push and q##_Pull handle adding and executing functions with parameters.
#define fQP(q, Q_SIZE, param_type)                                      	\
    void (*q##_funcs[Q_SIZE])(param_type);                                      \
    param_type q##_params[Q_SIZE];                                              \
    volatile int q##_last = 0;                                                  \
    int q##_first = 0;                                                          \
    int q##_Push(void (*func)(param_type), param_type params) {                 \
        if ((q##_last + 1) % Q_SIZE == q##_first)                               \
            return 1; /* Queue is full */                                       \
        q##_funcs[q##_last] = func;                                             \
        q##_params[q##_last++] = params;                                        \
        q##_last %= Q_SIZE;                                                     \
        return 0; /* Success */                                                 \
    }                                                                           \
    int q##_Pull(void) {                                                        \
        if (q##_last == q##_first)                                              \
            return 1; /* Queue is empty */                                      \
        q##_funcs[q##_first](q##_params[q##_first++]);                          \
        q##_first %= Q_SIZE;                                                    \
        return 0; /* Success */                                                 \
    }

Key Features

These macros provide:

  • Circular Buffers: Efficient storage and retrieval with wrap-around indices.
  • Delayed Execution: Schedule functions to execute after a delay.
  • Parameterized Queues: Pass parameters to queued functions.

Use Cases

Function queues are particularly useful in:

  • Task scheduling in embedded systems or real-time applications.
  • Managing callbacks or event-driven programming workflows.
  • Efficient queuing and delayed execution in constrained environments.

find more here: ANTIRTOS#C

Conclusion

The macros presented here demonstrate a flexible and efficient way to manage function queues in C. By integrating these techniques into your projects, you can streamline task scheduling, improve resource management, and enhance the overall functionality of your software.