Thread: Lockless Threaded Queue

  1. #1
    Registered User
    Join Date
    Jun 2008
    Posts
    93

    Lockless Threaded Queue

    I have written the code below for a lockles threaded queue I am trying to implement. I am almost done, but I have a few questions/issues. I have put comments into my code that relate to each issue to provide a better understanding of where I am in the code.

    AREA 1 (main.c): I get a seg fault when I uncomment the code that frees my nodes. If you comment these two lines the program runs just fine but with a very large memory leak because nothing is actually being freed.

    AREA 2 (queue.c): Am I calling the cleanup_handler and passing it the queue to unlock correctly? Also, did I implement the pthread_cleanup_push and pop correctly? I don't think the cleanup handler is popped off of the stack when a thread is cancelled. Therefore I only pushed it on one time for each worker thread, and I am assuming it never gets popped because the threads will not be terminating until the queue is destroyed.

    AREA 3 (queue.c): I am not sure how to free the data portion of each node that is stored into the queue. I believe the way I have it is correct but again it seg faults when I use this code. I guess this is essentially the same problem as AREA 1.

    AREA 4 (queue.c): I am very confused about spurious wakeups that arise when i call pthread_cond_signal. I understand what the spurious wakeups are, but I am not sure if the while() condition I am using will prevent them from occurring.

    If you notice anything else that appears to be incorrect please let me know. I think most of the code is OK but I am confused about the issues above. Thanks.

    main.c

    Code:
    #include "queue.h"
    #include <unistd.h>
    
    queue_t queue;
    
    #define DEBUG_PRINT_STATUS
    //#define DEBUG_PRINT_ADD
    //#define DEBUG_PRINT_REMOVE
    
    struct data
    {
    	int x;
    	int y;
    };
    
    void* task(void* item);
    void* writer();
    void* status();
    
    int main()
    {
    	queue_init(&queue, task, 10);
    	
    #ifdef DEBUG_PRINT_STATUS
    	pthread_t status_id;
    	pthread_create(&status_id, NULL, status, NULL);
    #endif
    	
    	pthread_t writer_id;
    	pthread_create(&writer_id, NULL, writer, NULL);
    	
    	pthread_join(writer_id, NULL);
    
    	queue_destroy(&queue);
    	
    	return 0;
    }
    
    void* status()
    {
    	char buffer[500];
    	while(1)
    	{
    		system("clear");
    		
    		sprintf(buffer, "Length:\t%d\nMemory Usage:\t%d\n", queue.length, queue.memory_allocated);
    		
    		printf("%s", buffer);
    		
    		usleep(100000);
    	}
    	
    	return NULL;
    }
    
    void* writer()
    {
    	int x;
    
    	for(;;)
    	{
    		struct data* information = malloc(sizeof(struct data));
    		information->x = x+1;
    		information->y = x+2;
    		
    #ifdef DEBUG_PRINT_ADD
    		printf("\nADD:  %lu has added an item with values x=%d y=%d\n", pthread_self(), ((struct data*)information)->x, ((struct data*)information)->y);
    #endif
    
    		queue_add(&queue, (void*)information, sizeof(struct data));
    	}
    	
    	return NULL;
    }
    
    void* task(void* item)
    {
    	
    #ifdef DEBUG_PRINT_ADD
    	printf("\nREMOVE:  %lu has retrieved an item with values x=%d y=%d\n", pthread_self(), ((struct data*)(((node_t*)item)->data))->x,  ((struct data*)(((node_t*)item)->data))->y);
    #endif
    
    	/****** AREA 1 ******/
    	free(((node_t*)item)->data);
    	free(item);
    	
    	return NULL;
    }
    main.h
    Code:
    #ifndef MAIN_H_
    #define MAIN_H_
    
    void* writer();
    int task();
    
    #endif
    queue.c

    Code:
    #include "queue.h"
    
    /***** AREA 2 *****/
    static void cleanup_handler(void *arg) 
    {
      	pthread_mutex_unlock(&((queue_t*)arg)->queue_mutex);
    }
    
    void queue_init(queue_t *queue, void *(*f)(void *), int num_worker_threads)
    {
    	node_t* dummy = malloc(sizeof(node_t));
    	queue->first = dummy;
    	queue->divider = dummy;
    	queue->last = dummy;
    	
    	queue->worker_task = f;
    	
    	queue->length = 0;
        
        queue->memory_allocated = 0;
        
        queue->workers = (pthread_t *)calloc((num_worker_threads), sizeof(pthread_t));
     	
     	queue->num_workers = num_worker_threads;
     	
        pthread_cond_init(&queue->ready, NULL);
        pthread_mutex_init(&queue->queue_mutex, NULL);
    	pthread_mutex_init(&queue->backup_mutex, NULL);
    
    	int x;
    	for(x =0; x<num_worker_threads; x++)
    	{
    		pthread_create(&queue->workers[x], NULL, queue_process, (void *)queue);
    	}
    }
    
    void queue_add(queue_t* queue, void* item, int size) 
    {
    	struct Node* new_node = malloc(sizeof(struct Node));
    	new_node->data = item;
    	new_node->size = size;
    	
    	pthread_mutex_lock(&queue->queue_mutex);
    	
        queue->last->next = new_node;
        queue->last = queue->last->next;
        
        while( queue->first != queue->divider ) 
        {
          	node_t* tmp = queue->first;
    		queue->first = queue->first->next;
         	free(tmp);
        }
        
        queue->memory_allocated += size + sizeof(node_t);
    	    
    	queue->length++;
    	
    	pthread_mutex_unlock(&queue->queue_mutex);
    	
    	pthread_cond_signal(&queue->ready);
    }
    
    void* queue_remove(queue_t* queue, int is_worker_thread) 
    {
    	void* item = NULL;
    	
    	if(is_worker_thread)
    	{
    		pthread_mutex_lock(&queue->queue_mutex);
    		
    		/***** AREA 4 *****/
    		while(queue->length == 0)
    		{	
    		    pthread_cond_wait(&queue->ready, &queue->queue_mutex);
    		}
    	}
    	
    	pthread_mutex_lock(&queue->backup_mutex);
    	
        if( queue->divider != queue->last ) 
        {
        	item = malloc(queue->divider->size);
          	item = queue->divider->next;
          	queue->memory_allocated -= (queue->divider->size + sizeof(node_t));
          	queue->divider = queue->divider->next;
        }
    
    	queue->length--;
        
    	pthread_mutex_unlock(&queue->backup_mutex);
    	
    	if(is_worker_thread)
    	{
    		pthread_mutex_unlock(&queue->queue_mutex);
    	}
    	
    	return item;
    }
    
    void queue_destroy(queue_t* queue)
    {	
    	int x;
    	for(x =0; x<queue->num_workers; x++)
    	{
    		pthread_cancel(queue->workers[x]);
    	}
    	
    	for(x =0; x<queue->num_workers; x++)
    	{
    		pthread_join(queue->workers[x], NULL);
    	}
    	
    	while(queue->length > 0)
    	{
    		/***** AREA 3 *****/
    		node_t* item = queue_remove(queue, 0);
    		free(item->data);
    		free(item);
    	}
    	
    	pthread_cond_destroy(&queue->ready);	
    	pthread_mutex_destroy(&queue->queue_mutex);
    }
    
    void *queue_process(void *ptr)
    {	
    	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    	
    	/***** AREA 2 *****/
    	pthread_cleanup_push(cleanup_handler,(void*)ptr);
    	
    	while(1)
    	{
    		void* item = queue_remove((queue_t *)ptr, 1);
    		((queue_t *)ptr)->worker_task(item);
    	}
    	
    	/***** AREA 2 *****/
    	pthread_cleanup_pop(0);
    }
    queue.h

    Code:
    #ifndef QUEUE_H_
    #define QUEUE_H_
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    typedef struct Node
    {
        void* data;
        int size;
        struct Node* next;
    } node_t;
    
    typedef struct queue 
    {
    	unsigned int length;
    	unsigned int memory_allocated;
    	pthread_t* workers;
    	int num_workers;
    	pthread_cond_t  ready;
    	pthread_mutex_t queue_mutex;
    	pthread_mutex_t backup_mutex;
    	node_t* first;
    	volatile node_t* divider;
    	volatile node_t* last;
    	void* (*worker_task)(void *arg);
    } queue_t;
    
    void queue_new(queue_t **q);
    void queue_init(queue_t *queue, void *(*worker_task)(void *), int num_worker_threads);
    void queue_destroy(queue_t *q);
    void* queue_process(void *ptr);
    void queue_clear(queue_t *q);
    void queue_add(queue_t* queue, void* item, int size);
    void* queue_remove(queue_t* queue, int is_worker_thread);
    
    #endif

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    There's no such thing as lockless synchronization under Posix. To be Posix compliant all shared memory accesses must be synchronized as specified by 4.10 - General Concepts

    volatile has nothing to do with lockless, or mutli-threaded programming. Lock-free and wait-free data-structures and algorithms can't be implemented in straight C. You at least need some "atomic primitives" (like CAS or DCAS), as well as any necessary memory barriers - both typically implemented in assembly.

    If you want roll your own multi-threaded Q, I would just take a regular single-threaded Q that you know works, and make it multi-threaded by adding a lock for mutual-exclusion, and a condition to signal "data available" and unblock any readers.

    gg

  3. #3
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Quote Originally Posted by Codeplug View Post
    There's no such thing as lockless synchronization under Posix. To be Posix compliant all shared memory accesses must be synchronized as specified by 4.10 - General Concepts

    volatile has nothing to do with lockless, or mutli-threaded programming. Lock-free and wait-free data-structures and algorithms can't be implemented in straight C. You at least need some "atomic primitives" (like CAS or DCAS), as well as any necessary memory barriers - both typically implemented in assembly.

    If you want roll your own multi-threaded Q, I would just take a regular single-threaded Q that you know works, and make it multi-threaded by adding a lock for mutual-exclusion, and a condition to signal "data available" and unblock any readers.

    gg
    I think this is just a naming issue. He doesn't have a "Lockless Threaded Queue". He is implementing a thread-safe queue, which as you can see, has multiple locks in it.
    bit∙hub [bit-huhb] n. A source and destination for information.

  4. #4
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    1. That's pretty simple. Check to be sure that 'item' isn't NULL.
    Code:
    if( item )
    {
        free( item->data );
        free( item );
    }
    3. This is the same as point 1.

    I'm not really sure why you're using a void pointer here, when all you use is a 'struct data'.


    Quzah.
    Hope is the first step on the road to disappointment.

  5. #5
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    I'll be performing between 10 and 50k additions to the queue per second so I can't afford to ever have the writer waiting. When I said "lockless" i just meant that the operations on the queue (add and remove) are atomic. A single thread can be adding and removing at the same time without corruption. The locks on the back end of the queue are simply to ensure that only one thread is removing form the queue at a time.

  6. #6
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    quzah the queue needs to be generic. I can't hard code it to use the data struct.

  7. #7
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    Does anyone know why i get a seg fault on the frees?

  8. #8
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by fguy817817 View Post
    Does anyone know why i get a seg fault on the frees?
    I already told you. You lost your pointer some place, so when you try to do:
    Code:
    free( foo->bar );
    It crashes when you try to access a member of a null pointer. If it's not NULL, then it's crashing because you pointer is already pointing some place you shouldn't be.


    Quzah.
    Hope is the first step on the road to disappointment.

  9. #9
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    main.c
    writer() and status() should both take a void* parameter.
    In status(), access to queue.length and queue.memory_allocated is unsynchronized.
    With multiple threads using stdout, consider synchronizing output with a global mutex, and fflush the stream.
    In writer() - x is uninitialized

    queue.h
    Remove workers, num_workers, and worker_task from the Q. That's just one of many possible ways a multi-threaded Q may be used. No reason to tightly couple this one usage pattern, which could be implemented in a separate API.
    Remove backup_mutex (explained below).
    size isn't needed in node_t (explained below).
    Remove volatile.

    queue.c
    queue_remove -
    Make queue_remove() an external-only interface and remove the is_worker_thread parameter. queue_destroy() can do a simple traversal to free the nodes.
    When you exit the "while(queue->length == 0)" loop, queue_mutex will be locked - which is why you don't need backup_mutex.
    >> item = malloc(queue->divider->size);
    There's no need to malloc anything. You only need to return the same pointer that the user gave you in the queue_add() call. This is why you don't need size in node_t. At this point you just have to un-link queue->first->next, free that node, and return the user provided pointer that was stored in it. This eliminates the need for a divider pointer.

    queue_add -
    You're complicating things by deferring node deletion until queue_add. Just malloc nodes in queue_add and free them in queue_remove.
    Posix recommends that you call cond_signal before calling mutex_unlock.

    1) The node pointer passed to task() is still participating in the queue's list - queue_remove should return the user's pointer, not node pointers.
    2) That's not really how to use them - http://www.opengroup.org/onlinepubs/...eanup_pop.html
    3) The destroy API can assume it has exclusive access to the Q. Just traverse the list and free the nodes. If the user left any data in the Q, just ignore it. The user is responsible for the pointers it adds to the Q.
    4) You don't prevent spurious wakeups, you just write code that's immune to them. Spinning on length will work just fine.

    gg

  10. #10
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    OK so I made almost all of the changes. I didnt take out the volatiles yet or remove those items from Q. However, I did change all of the major functional items you suggested. It appears to run stable but for some reason I keep getting a NULL item in AREA 2. I wouldn't think this would be possible because that means that the list was empty when queue_remove was called. This shouldn't be possible if we are only calling remove on pthread_cond_signal and handling the spurious wakeups correctly... shoud it? Perhaps I have a pointer off somewhere. I am going to continue to search but if you see something that is off please let me know.

    main.c

    Code:
    #include "queue.h"
    #include <unistd.h>
    
    queue_t queue;
    pthread_mutex_t stdout_mutex;
    
    #define DEBUG_PRINT_STATUS
    //#define DEBUG_PRINT_ADD
    //#define DEBUG_PRINT_REMOVE
    
    struct data
    {
    	int x;
    	int y;
    };
    
    void* task(void* item);
    void* writer(void* vp);
    void* status(void* vp);
    
    int main()
    {
    	pthread_mutex_init(&stdout_mutex, NULL);
    	
    	queue_init(&queue, task, 10);
    	
    #ifdef DEBUG_PRINT_STATUS
    	pthread_t status_id;
    	pthread_create(&status_id, NULL, status, NULL);
    #endif
    	
    	pthread_t writer_id;
    	pthread_create(&writer_id, NULL, writer, NULL);
    	
    	pthread_join(writer_id, NULL);
    
    	queue_destroy(&queue);
    	
    	return 0;
    }
    
    void* status(void* vp)
    {
    	char buffer[500];
    	while(1)
    	{
    		sprintf(buffer, "Length:\t%d\nMemory Usage:\t%d\n", queue_length(&queue), queue_mem(&queue));
    
    #ifdef DEBUG_PRINT_STATUS
    		pthread_mutex_lock(&stdout_mutex);
    		system("clear");
    		printf("%s", buffer);
    		fflush(stdout);
    		pthread_mutex_unlock(&stdout_mutex);
    #endif
    		
    		usleep(100000);
    	}
    	
    	return NULL;
    }
    
    void* writer(void* vp)
    {
    	int x = 0;
    	for(;;)
    	{
    		struct data* information = malloc(sizeof(struct data));
    		information->x = x+1;
    		information->y = x+2;
    		
    #ifdef DEBUG_PRINT_ADD
    		pthread_mutex_lock(&stdout_mutex);
    		printf("\nADD:  %lu has added an item with values x=%d y=%d\n", pthread_self(), ((struct data*)information)->x, ((struct data*)information)->y);
    		fflush(stdout);
    		pthread_mutex_unlock(&stdout_mutex);
    #endif
    
    		queue_add(&queue, (void*)information, sizeof(struct data));
    	}
    	
    	return NULL;
    }
    
    void* task(void* item)
    {
    	
    #ifdef DEBUG_PRINT_ADD
    	pthread_mutex_lock(&stdout_mutex);
    	printf("\nREMOVE:  %lu has retrieved an item with values x=%d y=%d\n", pthread_self(), ((struct data*)item->data)->x,   ((struct data*)item->data)->y;
    	fflush(stdout);
    	pthread_mutex_unlock(&stdout_mutex);
    #endif
    
    	/****** AREA 1 ******/
    	free(item);
    	
    	return NULL;
    }
    queue.h

    Code:
    #ifndef QUEUE_H_
    #define QUEUE_H_
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    typedef struct Node
    {
        void* data;
        int size;
        struct Node* next;
    } node_t;
    
    typedef struct queue 
    {
    	unsigned int length;
    	unsigned int memory_allocated;
    	pthread_t* workers;
    	int num_workers;
    	pthread_cond_t  ready;
    	pthread_mutex_t queue_mutex;;
    	volatile node_t* first;
    	volatile node_t* last;
    	void* (*worker_task)(void *arg);
    } queue_t;
    
    void queue_new(queue_t **q);
    void queue_init(queue_t *queue, void *(*worker_task)(void *), int num_worker_threads);
    void queue_destroy(queue_t *q);
    void* queue_process(void *ptr);
    void queue_clear(queue_t *q);
    void queue_add(queue_t* queue, void* item, int size);
    void* queue_remove(queue_t* queue);
    unsigned int queue_length(queue_t* queue);
    unsigned int queue_mem(queue_t* queue);
    
    #endif
    queue.c

    Code:
    #include "queue.h"
    
    static void cleanup_handler(void *arg) 
    {
      	pthread_mutex_unlock(&((queue_t*)arg)->queue_mutex);
    }
    
    void queue_init(queue_t *queue, void *(*f)(void *), int num_worker_threads)
    {
    	node_t* dummy = malloc(sizeof(node_t));
    	queue->first = dummy;
    	queue->last = dummy;
    	
    	queue->worker_task = f;
    	
    	queue->length = 0;
        
        queue->memory_allocated = 0;
        
        queue->workers = (pthread_t *)calloc((num_worker_threads), sizeof(pthread_t));
     	
     	queue->num_workers = num_worker_threads;
     	
        pthread_cond_init(&queue->ready, NULL);
        pthread_mutex_init(&queue->queue_mutex, NULL);
    
    	int x;
    	for(x =0; x<num_worker_threads; x++)
    	{
    		pthread_create(&queue->workers[x], NULL,  queue_process, (void *)queue);
    	}
    }
    
    void queue_add(queue_t* queue, void* item, int size) 
    {
    	struct Node* new_node = malloc(sizeof(struct Node));
    	new_node->data = item;
    	new_node->size = size;
    	
    	pthread_mutex_lock(&queue->queue_mutex);
    	
                   queue->last->next = new_node;
                   queue->last = queue->last->next;
        
                   queue->memory_allocated += size + sizeof(node_t);
    	    
    	queue->length++;
    	
    	pthread_cond_signal(&queue->ready);
    	
    	pthread_mutex_unlock(&queue->queue_mutex);
    }
    
    void* queue_remove(queue_t* queue) 
    {
    	void* item = NULL;
    	
    	pthread_mutex_lock(&queue->queue_mutex);
    	
    	pthread_cleanup_push(cleanup_handler,(void*)queue);
    	
    	/***** AREA 4 *****/
    	while(queue->length == 0)
    	{	
    	    pthread_cond_wait(&queue->ready, &queue->queue_mutex);
    	}
    
        if( queue->first != queue->last ) 
        {	
          	item = queue->first->next->data;	
          	queue->memory_allocated -= (queue->first->next->size + sizeof(node_t));
          	volatile node_t* tmp = queue->first;
          	queue->first = queue->first->next;
          	free((node_t*)tmp);
        }
    
    	queue->length--;
    
    	pthread_cleanup_pop(1);
    	
    	return item;
    }
    
    void queue_destroy(queue_t* queue)
    {	
    	int x;
    	for(x =0; x<queue->num_workers; x++)
    	{
    		pthread_cancel(queue->workers[x]);
    	}
    	
    	for(x =0; x<queue->num_workers; x++)
    	{
    		pthread_join(queue->workers[x], NULL);
    	}
    	
    	/***** AREA 3 *****/
    	while( queue->first != queue->last ) 
                    {	
          	   volatile node_t* tmp = queue->first;
          	   queue->first = queue->first->next;
          	
          	   queue->memory_allocated -= (tmp->size + sizeof(node_t));
          	   free(tmp->data);
          	   free((node_t*)tmp);
                    }
    	
    	pthread_cond_destroy(&queue->ready);	
    	pthread_mutex_destroy(&queue->queue_mutex);
    }
    
    void *queue_process(void *ptr)
    {	
    	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    	
    	/***** AREA 2 *****/
    	/* moved the push for cleanup handler to remove */
    	while(1)
    	{
    		void* item = queue_remove((queue_t *)ptr);
    		
    		if(item != NULL)
    		{
    			((queue_t *)ptr)->worker_task(item);
    		}
    	}
    	/* moved the pop for cleanup handler to remove */
    }
    
    unsigned int queue_length(queue_t* queue)
    {
    	unsigned int length = 0;
    	pthread_mutex_lock(&queue->queue_mutex);
    	length = queue->length;
    	pthread_mutex_unlock(&queue->queue_mutex);
    	
    	return length;
    }
    
    unsigned int queue_mem(queue_t* queue)
    {
    	unsigned int mem = 0;
    	pthread_mutex_lock(&queue->queue_mutex);
    	mem = queue->memory_allocated;
    	pthread_mutex_unlock(&queue->queue_mutex);
    	
    	return mem;
    }

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Remove all instances of volatile. They do nothing but hinder the compiler's optimizer. What is it you believe the volatile's are doing for you?

    queue_remove() needs to unlink the "queue->first->next" node - possibly resetting queue->last. queue->first should always point to "dummy". I don't prefer the dummy-node concept myself - but when the Q is empty, first==last==dummy.

    >> queue_remove() - if( queue->first != queue->last )
    Should always be true since "length != 0" at that point.

    queue_destroy - don't free the user's data (or dummy->data), just ignore it. The while loop isn't quite right...to free everything including "dummy".

    >> queue_process() - if(item != NULL)
    Don't filter the user's data. What if I want put NULL in my Q?...

    The push/pop in remove looks good. Consider making add cancel-safe as well.

    And of course, adding error checking to all functions that can return errors is always good.

    gg

  12. #12
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    I read that the nodes needed to be volatile to keep from having to lock the front end when items are added. Currently there is no lock on the front end.

  13. #13
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> I read that the nodes needed to be volatile to ...
    You read bad information. volatile has nothing to do with mutithreading.

    Here is a C++ standards committee paper which considered whether or not volatile should actual "have something to do with" multithreading: http://www.open-std.org/JTC1/sc22/wg...006/n2016.html

    >> Currently there is no lock on the front end.
    The last code you posted is locking 'queue->queue_mutex' in both add and remove - as it should. Removing either lock'ing would break it.

    gg

  14. #14
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    Dr. Dobb's | Writing Lock-Free Code: A Corrected Queue | September 29, 2008

    So this is not correct? or did I misunderstand something?

  15. #15
    Registered User
    Join Date
    Jun 2008
    Posts
    93
    I originally had this working with no lock on the front end. I must have left the lock in when I changed it to be more generic in my first post. I used the approach from the artical above before I posted here and it seemed to be working very well. The lock on the front end is really gonna slow me down.... i was hoping to do away with it somehow. What is the best way to code this such that queue_add never has to wait? I am writing packet analysis software and if I have to wait for very long on the add I will start dropping packets. Is there a better approach than the one I am using to allow the front to wait for as little time as possible to perform an add?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help with FIFO QUEUE
    By jackfraust in forum C++ Programming
    Replies: 23
    Last Post: 04-03-2009, 08:17 AM
  2. Fixing my program
    By Mcwaffle in forum C Programming
    Replies: 5
    Last Post: 11-05-2008, 03:55 AM
  3. help with queues
    By Unregistered in forum C Programming
    Replies: 3
    Last Post: 05-21-2002, 09:09 PM
  4. help with queues
    By Unregistered in forum C Programming
    Replies: 3
    Last Post: 05-21-2002, 11:39 AM
  5. queue help
    By Unregistered in forum C Programming
    Replies: 2
    Last Post: 10-29-2001, 09:38 AM