2 votes

Implémentation d'une file d'attente en C

J'essaie d'implémenter une file d'attente personnalisée pour les buffers Gstreamer. Le problème est que lorsque j'essaie de désempiler, il semble que je perde la tête de la file d'attente. Chaque fois que j'essaie de désempiler deux fois, j'obtiens un défaut de segmentation. J'ai également remarqué que head est toujours égal à head->next. Maintenant, je ne suis pas sûr qu'il y ait un problème avec enqueue ou dequeue. Veuillez m'aider. Merci.

typedef struct _GstBUFFERQUEUE GstBufferQueue;

struct _GstBUFFERQUEUE {
  GstBuffer *buf;
  guint buf_size;
  struct _GstBUFFERQUEUE *next;
};

void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf)
{
  if (*queue_size == 0)
  {
    *head = malloc(sizeof(GstBufferQueue));
    (*head)->buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE(buf));
    (*head)->buf = gst_buffer_copy(buf); 
    *tail = *head;
  }
  else
  {
    if ((*tail)->next = malloc(sizeof(GstBufferQueue))) {
        (*tail)->next->buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE(buf));
        (*tail)->next->buf = gst_buffer_copy(buf);
        (*tail) = (*tail)->next;
    }
    else {
        GST_WARNING("Error allocating memory for new buffer in queue");
    } 
  } 
  (*tail)->next = NULL; 
  (*queue_size)++;

}

void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf)
{
  GstBufferQueue **tmpPtr = head;
  GstBufferQueue **nextPtr;
  *nextPtr = (*head)->next; 
  *buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE((*tmpPtr)->buf));
  *buf = gst_buffer_copy((*tmpPtr)->buf);
  gst_buffer_unref((*tmpPtr)->buf);
  free((*tmpPtr));
  *head = *nextPtr;

  if ((*head) == NULL)
     (*tail) = NULL;

   (*queue_size)--;   
}

5voto

Jonathan Leffler Points 299946

Lorsqu'il est converti en code compilable en ajoutant suffisamment de pseudo-infrastructure pour simuler le système GST, GCC affiche un avertissement qui est presque certainement la source de votre problème :

gstq.c: In function ‘dequeue_gstbuffer’:
gstq.c:73:12: warning: ‘nextPtr’ is used uninitialized in this function [-Wuninitialized]

Les lignes sont :

72  GstBufferQueue **nextPtr;
73  *nextPtr = (*head)->next;

Sur ces lignes, vous avez besoin :

GstBufferQueue *nextPtr = (*head)->next;

Vous devez également utiliser :

(*head)->next = nextPtr;

Faites attention aux avertissements de votre compilateur. Si votre compilateur n'avertit pas, faites en sorte qu'il le fasse. Si vous ne pouvez pas le faire, trouvez un meilleur compilateur.


SSCCE

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#define GST_BUFFER_SIZE(x)  sizeof(x)
#define GST_WARNING(x)      fprintf(stderr, "%s\n", x)

typedef struct GstBuffer { int value; } GstBuffer;
typedef unsigned int guint;

static GstBuffer *gst_buffer_try_new_and_alloc(int size)
{
    GstBuffer *buf = malloc(sizeof(GstBuffer));
    assert(buf != 0);
    buf->value = size;
    return buf;
}

static GstBuffer *gst_buffer_copy(const GstBuffer *buf)
{
    GstBuffer *new_buf = malloc(sizeof(GstBuffer));
    assert(new_buf != 0);
    new_buf->value = buf->value;
    return new_buf;
}

static void gst_buffer_unref(GstBuffer *buf)
{
    buf->value = -1;
}

typedef struct _GstBUFFERQUEUE GstBufferQueue;

struct _GstBUFFERQUEUE {
  GstBuffer *buf;
  guint buf_size;
  struct _GstBUFFERQUEUE *next;
};

extern void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf);
extern void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf);

void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf)
{
  if (*queue_size == 0)
  {
    *head = malloc(sizeof(GstBufferQueue));
    (*head)->buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buf));
    (*head)->buf = gst_buffer_copy(buf); 
    *tail = *head;
  }
  else
  {
    if (((*tail)->next = malloc(sizeof(GstBufferQueue))) != 0)
    {
        (*tail)->next->buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buf));
        (*tail)->next->buf = gst_buffer_copy(buf);
        (*tail) = (*tail)->next;
    }
    else
    {
        GST_WARNING("Error allocating memory for new buffer in queue");
    } 
  } 
  (*tail)->next = NULL; 
  (*queue_size)++;
}

void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf)
{
  GstBufferQueue **tmpPtr = head;
  GstBufferQueue  *nextPtr;
  nextPtr = (*head)->next; 
  *buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE((*tmpPtr)->buf));
  *buf = gst_buffer_copy((*tmpPtr)->buf);
  gst_buffer_unref((*tmpPtr)->buf);
  free((*tmpPtr));
  *head = nextPtr;

  if ((*head) == NULL)
     (*tail) = NULL;

   (*queue_size)--;   
}

int main(void)
{
    GstBufferQueue *q_head = 0;
    GstBufferQueue *q_tail = 0;
    guint           q_size = 0;

    for (int i = 0; i < 10; i++)
    {
        GstBuffer *buf = gst_buffer_try_new_and_alloc(i + 100);
        enqueue_gstbuffer(&q_head, &q_tail, &q_size, buf);
        printf("EQ: %d\n", buf->value);
        free(buf);
        if (i % 2 == 1)
        {
            GstBuffer *buf;
            dequeue_gstbuffer(&q_head, &q_tail, &q_size, &buf);
            printf("DQ: %d\n", buf->value);
            free(buf);
        }
    }

    while (q_size > 0)
    {
        GstBuffer *buf;
        dequeue_gstbuffer(&q_head, &q_tail, &q_size, &buf);
        printf("DQ: %d\n", buf->value);
        free(buf);
    }

    printf("All done\n");
    return(0);
}

Sortie

EQ: 100
EQ: 101
DQ: 100
EQ: 102
EQ: 103
DQ: 101
EQ: 104
EQ: 105
DQ: 102
EQ: 106
EQ: 107
DQ: 103
EQ: 108
EQ: 109
DQ: 104
DQ: 105
DQ: 106
DQ: 107
DQ: 108
DQ: 109
All done

Notez que le code SSCCE ci-dessus fuit plus qu'un tamis. Je n'ai pas l'intention de corriger ces fuites car elles se trouvent dans le code qui simule la gestion des tampons de la TPS. Vérifiez bien que votre code ne souffre pas de ces fuites de mémoire.


Je pense que vous devriez emballer votre "file d'attente" différemment. Ce que vous appelez une GstBufferQueue devrait vraiment être un GstBufferQueueItem et votre réel GstBufferQueue doit contenir les pointeurs de tête et de queue, ainsi que la taille. Vous passeriez un pointeur vers le fichier (révisé) GstBufferQueue au enqueue_gstbuffer() y dequeue_gstbuffer() au lieu de passer 3 paramètres distincts.

typedef struct GstBufferQueueItem GstBufferQueueItem;

struct GstBufferQueueItem
{
  GstBuffer *buf;
  guint buf_size;
  GstBufferQueueItem *next;
};

typedef struct GstBufferQueue GstBufferQueue;

struct GstBufferQueue
{
    GstBufferQueueItem *head;
    GstBufferQueueItem *tail;
    guint               size;
};

// Uncompiled - but to give you an idea
void dequeue_gstbuffer(GstBufferQueue *q, GstBuffer **buf)
{
    GstBufferQueueItem *item = q->head;
    GstBufferQueueItem *next = item->next; 
    *buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(item->buf));
    *buf = gst_buffer_copy(item->buf);
    gst_buffer_unref(item->buf);
    free(item);
    q->head = next;

    if (q->head == NULL)
        q->tail = NULL;

    q->size--;   
}

Notez que ces noms ne comportent pas de trait de soulignement en tête. De tels noms sont dangereux. Les noms avec un trait de soulignement et une lettre majuscule sont réservés à l'implémentation pour n'importe quel usage. Les noms avec un trait de soulignement et une lettre minuscule sont réservés à des mots différents, mais l'utilisation de l'un ou l'autre est douteuse (et bien que les normes ne disent pas grand-chose sur le trait de soulignement et le chiffre, ne jouez pas avec eux - considérez le trait de soulignement principal comme "réservé au système" à moins que vous n'écriviez "le système").

ISO/IEC 9899:2011 §7.1.3 Identifiants réservés

  • Tous les identifiants qui commencent par un trait de soulignement et une lettre majuscule ou un autre caractère trait de soulignement sont toujours réservés à un usage quelconque.
  • Tous les identifiants qui commencent par un trait de soulignement sont toujours réservés pour être utilisés comme identifiants. avec une portée de fichier dans les espaces de noms ordinaires et de balises.

3voto

benjarobin Points 1456

Remplacer

GstBufferQueue **nextPtr;
*nextPtr = (*head)->next;
...
*head = *nextPtr;

Par

GstBufferQueue *nextPtr;
nextPtr = (*head)->next;
...
*head = nextPtr;

0voto

Jere Points 386

Une chose qui me frappe est que lorsque vous allouez la file d'attente pour la première fois (lorsque *queue_size == 0 ), vous ne mettez pas le pointeur 'next' du nœud nouvellement créé à NULL.

Il n'y a aucune garantie qu'elle sera NULL après l'allocation et l'assignation à (*head), donc quand vous faites le dequeue votre (*head)->next pourrait pointer vers une adresse poubelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X