├── Makefile ├── README.md ├── pkt_queue.c ├── pkt_queue.h └── test.c /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-ggdb -Wall -Werror 2 | LDFLAGS=-pthread 3 | CC=gcc 4 | OBJECTS=pkt_queue.o test.o 5 | TARGET=test 6 | 7 | all: $(TARGET) 8 | 9 | $(TARGET): $(OBJECTS) 10 | $(CC) $(OBJECTS) -o $@ $(LDFLAGS) 11 | 12 | include depends 13 | 14 | depends: 15 | $(CC) -MM $(OBJECTS:.o=.c) > depends 16 | 17 | clean: 18 | rm ./$(TARGET) *.o 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple lock-free queue implementation based on [Implementing Lock-Free Queues (by John D. Valois)][1]. 2 | 3 | It is supposed to be used on multi-threaded tcpdump project (not finished yet :(). 4 | 5 | [1]: http://people.cs.pitt.edu/~jacklange/teaching/cs2510-f12/papers/implementing_lock_free.pdf 6 | -------------------------------------------------------------------------------- /pkt_queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Yuzhong Wen 3 | * 4 | * Distributed under terms of the MIT license. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "pkt_queue.h" 12 | 13 | void init_queue(struct queue_root **root) 14 | { 15 | *root = (struct queue_root*) malloc(sizeof(struct queue_root)); 16 | if (root == NULL) { 17 | printf("malloc failed"); 18 | exit(1); 19 | } 20 | (*root)->head = (struct queue_node*) malloc(sizeof(struct queue_node)); /* Sentinel node */ 21 | (*root)->tail = (*root)->head; 22 | (*root)->head->n = NULL; 23 | (*root)->head->next = NULL; 24 | } 25 | 26 | int queue_add(struct queue_root *root, void *val) 27 | { 28 | struct queue_node *n; 29 | struct queue_node *node = (struct queue_node *)malloc(sizeof(struct queue_node)); 30 | node->n = val; 31 | node->next = NULL; 32 | while (1) { 33 | n = root->tail; 34 | if (__sync_bool_compare_and_swap(&(n->next), NULL, node)) { 35 | break; 36 | } else { 37 | __sync_bool_compare_and_swap(&(root->tail), n, n->next); 38 | } 39 | } 40 | __sync_bool_compare_and_swap(&(root->tail), n, node); 41 | 42 | return 1; 43 | } 44 | 45 | void *queue_get(struct queue_root *root) 46 | { 47 | struct queue_node *n; 48 | void *val; 49 | while (1) { 50 | n = root->head; 51 | if (n->next == NULL) { 52 | return NULL; 53 | } 54 | 55 | if (__sync_bool_compare_and_swap(&(root->head), n, n->next)) { 56 | break; 57 | } 58 | } 59 | val = (void *) n->next->n; 60 | free(n); 61 | return val; 62 | } 63 | -------------------------------------------------------------------------------- /pkt_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Yuzhong Wen 3 | * 4 | * Distributed under terms of the MIT license. 5 | */ 6 | 7 | #ifndef PKT_QUEUE_H 8 | #define PKT_QUEUE_H 9 | 10 | #include 11 | 12 | #ifdef HAVE_CONFIG_H 13 | #include "config.h" 14 | #endif 15 | 16 | /* 17 | * Here we implement a lock-free queue for buffering the 18 | * packets that come out from multiple filters. 19 | * 20 | * */ 21 | 22 | struct queue_root { 23 | struct queue_node *head; 24 | struct queue_node *tail; 25 | volatile unsigned int size; 26 | }; 27 | 28 | struct queue_node { 29 | void *n; 30 | struct queue_node *next; 31 | }; 32 | 33 | void init_queue(struct queue_root **root); 34 | int queue_add(struct queue_root *root, void *val); 35 | void *queue_get(struct queue_root *root); 36 | 37 | #endif /* !PKT_QUEUE_H */ 38 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "pkt_queue.h" 8 | #define CONSUMER 1 9 | #define PRODUCER 4 10 | #define TEST_NUM 10000000 11 | 12 | struct queue_root *queue; 13 | volatile int total = TEST_NUM; 14 | int complete; 15 | 16 | void *producer_thread(void *para) 17 | { 18 | int c = TEST_NUM / PRODUCER; 19 | for (; c > 0; c--) 20 | { 21 | int *val = (int *)malloc(sizeof(int)); 22 | *val = random(); 23 | queue_add(queue, val); 24 | /* 25 | *printf("Adding value %d\n", *val); 26 | */ 27 | } 28 | 29 | complete++; 30 | return NULL; 31 | } 32 | 33 | void *consumer_thread(void *para) 34 | { 35 | while (total) { 36 | int *val; 37 | val = (int *) queue_get(queue); 38 | if (val != NULL) { 39 | /* 40 | *printf("Getting value %d, %d left\n", *val, queue->size); 41 | */ 42 | __sync_fetch_and_sub(&total, 1); 43 | } 44 | } 45 | 46 | return NULL; 47 | } 48 | 49 | int main() 50 | { 51 | int p, c; 52 | complete = 0; 53 | pthread_attr_t attr; 54 | pthread_t threads[200]; 55 | struct timeval then, now; 56 | gettimeofday(&then, NULL); 57 | srandom((unsigned int)then.tv_usec); 58 | 59 | init_queue(&queue); 60 | /* Now create some threads */ 61 | pthread_attr_init(&attr); 62 | 63 | for (p = 0; p < PRODUCER; p++) { 64 | pthread_create(&threads[p], &attr, producer_thread, NULL); 65 | pthread_join(threads[p], NULL); 66 | } 67 | for (c = 0; c < CONSUMER; c++) { 68 | pthread_create(&threads[p + c], &attr, consumer_thread, NULL); 69 | pthread_join(threads[p + c], NULL); 70 | } 71 | 72 | gettimeofday(&now, NULL); 73 | 74 | printf("Executions in %.3g seconds\n", now.tv_sec - then.tv_sec + 1e-6 * (now.tv_usec - then.tv_usec)); 75 | 76 | return 0; 77 | } 78 | --------------------------------------------------------------------------------