Re: Changing head item on a (GCD) Queue
Thanks Jens and Quincey, I think the best solution is to do what Jens suggested which is to have a custom thread type arrangement and share an Array as the Queue and protect it with a Lock/Mutex. The only thing I’m not clear on, it how to signal to the Consumer thread that an item has been added (e.g. wake the thread up). The way I saw it working is that I’d have one thread that is created at startup and sleeps until an entry has been added to the queue, it would then wake up and process all items on the queue before sleeping again.toggle quoted messageShow quoted text
A bit of background on what I am trying to do.
I have some C code that works on am embedded system using its own “OS/Kernel” and I’m trying to do something similar in my App so they can use a Mac instead of the embedded system (which is very hard to maintain since its very old and not supported anymore).
This code works with two threads, in a producer/consumer model. The cool thing about this Kernel is that it supports what it calls Queue Semaphores. A Queue Semaphore automatically performs the Thread Switching and Queue Management in a Thread-Safe Manner.
The pseudo code for the task I trying to reproduce on the Mac allows it to be coded really elegantly in my opinion.
myData = GetQueueHeadAndWait(queue);
// Process Data
Code to Process the Data
// Free Data
// Never Reached
(int) taskProducer((data*) theData)
// Check “theData” against the Head Item of the Queue and either allocate a new block and add it to the Tail or overwrite the existing Data in place.
newBlock = CheckData(queue,theData);
if (newBlock == TRUE)
// Allocate Buffer
myData = alloc(sizeof(data));
// Code to Copy from theData into myData
// Add Data to Queue, this causes the task to Consumer Task to become “fire” if it is asleep
myData = GetQueueHeadNoWait(queue);
if (myData == NULL)
// Fatal Error
// Code to Copy from theData into myData, no need to add it to the queue, because its already there
The way this works is that:
The Queue Semaphore is created empty, then the two tasks are created. One of the two tasks will get control first, if its the taskConsumer, then the call GetQueueHeadAndWait will cause the task to suspend (since the queue is empty). At some point taskProducer will run, which takes the data from the source and Adds it to the queue. If there are tasks waiting on the queue then the oldest task is given the data and it is made ready so that when it gets control, the return from GetQueueHeadAndWait return with the Data received. If there are no tasks waiting, then the data is just added to the queue (or overwritten).
There are various other functions in the Kernel that allow it Lock the Queue, set Task Priorities and things like that. In this case, the two tasks have equal priority and the Queue Semaphore used is private to these two Tasks.
I think this solution is so elegant and I’m trying to see if it is reproducible on the Mac/Cocoa and not having much luck so far. The beauty of it is is that it is completely self-regulating and self-synchronizing. Also this mechanism can re-used for different scenarios with very little tweaking, for instance, if you use two Queue Semaphores you can do things like:
FreeBufferQueue; Set to N Buffers when Queue is created.
ProcessQueue; Set Empty.
// Get a Free Buffer, Suspend if there are none (e.g. stop producing).
// Fill Buffer
// Add Buffer to Process Queue
// Get a Buffer to Process, Suspend if there are none (e.g. stop producing).
// Process Buffer
// Add Buffer to Free Queue (allow Producer to start producing again, if it ever stopped)
This automatically throttles the Producer/Consumer to N buffers!
Any comments or suggestions greatly appreciated.
All the Best