在基于事件或消息驱动的程序设计中,常常要用到队列这种数据结构。作者以此为背景,用C实现了一个Linux平台下的可动态增长的队列,并且可设置是否阻塞。只要将代码做简单的改变,就可以移植到其他平台上。
队列实现的并不完美,希望大家能不断修改,指正!
代码如下:
// Fifo.h ///
#ifndef __CM_FIFO_H__
#define __CM_FIFO_H__
#include "mutex.h"
#define bool int
struct MSGDATA
{
void* data;
struct MSGDATA* prev;
struct MSGDATA* next;
};
typedef struct MSGDATA msgdata_t;
struct FIFO
{
vmutex_t mutex;
vcondition_t cond;
msgdata_t* msg;
msgdata_t *head;
msgdata_t *tail;
int size;
int elem_size;
bool autosize;
int discard;
};
typedef struct FIFO fifo_t;
void fifo_init(fifo_t *fifo, int s, int es, bool as);
int fifo_free(fifo_t *fifo);
int fifo_add(fifo_t *fifo, void* msg);
int fifo_get_next(fifo_t *fifo, void* msg);
int fifo_get_discard(fifo_t *fifo);
#endif
/// Fifo.c ///#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <pthread.h>
#include "fifo.h"
static unsigned int max_size = 10;
static bool fwait(fifo_t *fifo, long millsec)
{
#if 0
if (millsec < 0)
#endif
return vcond_wait(&fifo->cond, &fifo->mutex);
struct timeval now;
struct timespec timeout;
int retcode;
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec + 100000;
timeout.tv_nsec = now.tv_usec * 1000 + millsec;
retcode = vcond_timedwait(&fifo->cond, &fifo->mutex, &timeout);
return 0;
}
static bool fwake_up(fifo_t *fifo)
{
vcond_signal(&fifo->cond);
return 0;
}
void fifo_init(fifo_t *fifo, int s, int es, bool as)
{
assert(s > 0);
assert(es > 0);
assert(fifo != NULL);
fifo->size = 0;
fifo->elem_size = es;
fifo->autosize = as;
fifo->discard = 0;
int i;
msgdata_t* temp = NULL;
#ifdef FIFO_DEBUG
char intbuf[256] = {0};
#endif
for (i = 0; i < max_size; ++i)
{
msgdata_t* item = (msgdata_t*) malloc(sizeof(msgdata_t));
item->data = malloc(fifo->elem_size);
#ifdef FIFO_DEBUG
sprintf(intbuf, "%x data-%d", item, i);
memcpy(item->data, intbuf, strlen(intbuf));
#endif
if (i == 0)
{
item->prev = NULL;
item->next = NULL;
fifo->msg = item;
temp = fifo->msg;
fifo->head = fifo->tail = fifo->msg;
}
else
{
temp->next = item;
item->prev = temp;
item->next = NULL;
temp = item;
}
}
// Loop link
temp->next = fifo->msg;
fifo->msg->prev = temp;
vmutex_init(&fifo->mutex);
vcond_init(&fifo->cond);
}
int fifo_add(fifo_t *fifo, void* msg)
{
// Queue is FULL
if ((fifo->tail->next == fifo->head) || (fifo->head->prev == fifo->tail))
{
++fifo->discard;
if (!fifo->autosize)
{
#ifdef FIFO_DEBUG
printf("Queue is FULL, Discard the message!/n");
#endif
vmutex_unlock(&fifo->mutex);
return -1;
}
// Resize this Queue
msgdata_t* item = (msgdata_t*) malloc(sizeof(msgdata_t));
item->data = malloc(fifo->elem_size);
item->next = fifo->head;
fifo->head->prev = item;
fifo->tail->next = item;
item->prev = fifo->tail;
}
// Add item to Queue
memcpy(fifo->tail->data, msg, fifo->elem_size);
//printf("Add==> %s, count = %d, new = %d/n", fifo->tail->data, fifo->size+1, fifo->discard);
fifo->tail = fifo->tail->next;
fifo->size++;
fwake_up(fifo);
vmutex_unlock(&fifo->mutex);
return fifo->size;
}
int fifo_get_next(fifo_t *fifo, void* msg)
{
assert(msg != NULL);
// Block the thread when Queue is EMPTY
vmutex_lock(&fifo->mutex);
while (fifo->head == fifo->tail)
{
fwait(fifo, 10);
}
memcpy(msg, fifo->head->data, fifo->elem_size);
fifo->head = fifo->head->next;
fifo->size--;
vmutex_unlock(&fifo->mutex);
return fifo->size;
}
int fifo_get_discard(fifo_t *fifo)
{
return fifo->discard;
}
int fifo_free(fifo_t *fifo)
{
static int count = 0;
msgdata_t* fprev,*fitem;
fprev = fifo->msg->prev;
fitem = fifo->msg->next;
fprev->next = NULL;
#ifdef FIFO_DEBUG
printf("FREE item: %d - %s/n", count, (char*)fifo->msg->data);
#endif
free(fifo->msg);
fifo->msg = fitem;
while (fifo->msg != NULL)
{
++count;
fitem = fifo->msg->next;
#ifdef FIFO_DEBUG
printf("Free item: %x, %d - %s/n", fifo->msg, count, (char*)fifo->msg->data);
#endif
free(fifo->msg);
fifo->msg = NULL;
fifo->msg = fitem;
}
return count+1;
}
/// Mutex.h /
#ifndef __CM_MUTEX_H__
#define __CM_MUTEX_H__
#include <pthread.h>
typedef pthread_mutex_t vmutex_t;
typedef pthread_cond_t vcondition_t;
#define vmutex_init(mutex) /
pthread_mutex_init((mutex),0)
#define vmutex_destroy(mutex) /
pthread_mutex_destroy((mutex))
#define vmutex_lock(mutex) /
pthread_mutex_lock((mutex))
#define vmutex_unlock(mutex) /
pthread_mutex_unlock((mutex))
#define vcond_init(cond) /
pthread_cond_init((cond),0)
#define vcond_destroy(cond) /
pthread_cond_destroy((cond))
#define vcond_wait(cond, mutex) /
pthread_cond_wait((cond),(mutex))
#define vcond_timedwait(cond, mutex, timeout) /
pthread_cond_timedwait((cond),(mutex),(timeout))
#define vcond_signal(cond) /
pthread_cond_signal((cond))
#define vcond_broadcast(cond) /
pthread_cond_broadcast((cond))
#endif
/ Test_fifo.c ///
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include "CMInterface.h"
#include "fifo.h"
fifo_t testfifo;
static int added1 = 0, added2 = 0;
static int readed = 0;
static int lock = 1;
void* thread_add1(void* p);
void* thread_add2(void* p);
void* thread_read(void* p);
int main()
{
fifo_init(&testfifo, 10, 256, 1);
pthread_t tadd1,tadd2,tread;
pthread_create(&tread, NULL, thread_read, "Read");
usleep(5);
pthread_create(&tadd1, NULL, thread_add1, "Add1");
//pthread_create(&tadd2, NULL, thread_add2, "Add2");
pthread_join(tread, NULL);
return 0;
}
void* thread_add1(void* p)
{
char* name = (char*)(p);
printf("Thread %s Starting.../n", name);
int count = 0;
for (;;)
{
char buffer[256] = {0};
sprintf(buffer, "Event %d From %s", ++count, name);
fifo_add(&testfifo, buffer);
++added1;
if (added1 == 1000) break;
if (added1 % 50 == 0)
usleep(1);
}
printf("add = %d, discard = %d, vaild = %d/n", count, fifo_get_discard(&testfifo), count - fifo_get_discard(&testfifo));
return p;
}
void* thread_add2(void* p)
{
char* name = (char*)(p);
printf("Thread %s Starting.../n", name);
int count = 0;
for (;;)
{
char buffer[256] = {0};
sprintf(buffer, "Event %d From %s", ++count, name);
if (-1 != fifo_add(&testfifo, buffer))
{
++added2;
printf("Add2==> %s/n", buffer);
if (added2 == 1000) break;
if (added2 % 10 == 0)
usleep(1);
}
}
return p;
}
void* thread_read(void* p)
{
char* name = (char*)(p);
printf("Thread %s Starting.../n", name);
int count = 0;
while(lock)
{
char buffer[256] = {0};
count = fifo_get_next(&testfifo,buffer);
if (1)
{
++readed;
printf("Get<=== %s/tcount = %d, discard = %d/n", buffer, readed, fifo_get_discard(&testfifo));
}
if (readed >= 1000)
{
printf("Break/n");
break;
}
}
printf("Get here/n");
int free_items = fifo_free(&testfifo);
printf("/r/n######## Free itemS: %d/n", free_items);
return p;
}
// Makefile.post //
all : $(target)
OBJS = $(SRCS:.c=.o)
SOBJS = $(SSRCS:.c=.o)
%.o : %.c
$(CC) $(C_FLAGS) $(INC_DIR) $?
$(target) : $(OBJS)
$(CC) $(LD_FLAGS) $@ $(OBJS) $(LD_PATH) $(LD_LIBS)
clean:
rm -rf *.o $(target)
// Makefile.prev ///
CC = gcc
C_FLAGS = -Wall -O2 -c
LD_FLAGS = -rdynamic -s -o
LD_LIBS =
LD_PATH =
SLD_FLAGS = -shared -o
INC_DIR = -I$(TOP_DIR)/include
MKDIR = mkdir -p
MV = mv
CP = cp -rf
/ Makefile for Fifo //
TOP_DIR = $(shell pwd)..
include ../Makefile.prev
LD_FLAGS = $(SLD_FLAGS)
C_FLAGS += -DFIFO_DEBUG
target := libutils.so
SRCS = fifo.c
include ../Makefile.post
Makefile for Test //
TOPDIR = $(shell pwd)
SUBDIRS += test_fifo ipcserver ipcclient
all:
$(foreach dir,$(SUBDIRS), cd $(dir) && $(MAKE) && cd $(TOPDIR); )
print:
$(foreach dir,$(SUBDIRS), cd $(dir) && $(MAKE) print && cd $(TOPDIR); )
clean:
$(foreach dir,$(SUBDIRS), cd $(dir) && $(MAKE) clean && cd $(TOPDIR); )
///
TOP_DIR = $(shell pwd)/..
include ../Makefile.prev
LD_LIBS = -ldl -lpthread -lutils
LD_PATH = -L../utils
INC_DIR += -I$(TOP_DIR)/utils
target := testfifo
SRCS = test_fifo.c
include ../Makefile.post