EDIT:
SIMPLY: I suggest you do not need a head and tail for your turn. Just have a head. If head = NULL, the list is empty. Add items to the head. Remove items from your head. Simplification, less CAS operations.
HELPER: I suggested in the comments that you need to think about an auxiliary scheme to combat the race. In my version of what “blocking is free” means, it’s normal to have rare race conditions if they do not create problems. I like the extra performance, as well as the idle thread sleeping for a couple of ms for too long.
Helper ideas. When the consumer captures the work, he can check if there is a flow in the coma. When a producer adds work, he can search for streams in comas.
So watch out for the sleepers. Use the linked list of sleepers. When a thread decides that there is no work, it marks itself as! Awake and CAS himself to top the sleeping list. When a signal is received for awakening, the stream marks itself as awake. Then the newly awakened thread clears the sleeping list. To clear a parallel single linked list, you have to be careful. You can use only CAS. Thus, while the head of the sleeping list is awake, you can turn off the head. If the head is not awake, continue to scan the list and “lazy unlock” (I made this term) the remaining awake items. Lazy unlink is just ... just set the next ptr of the previous element over the active element. Parallel scanning will still reach the end of the list, even if it falls into elements that are awake. Subsequent checks see a shorter list. Finally, anytime you add work or take off work, scan the sleeping list for awake items. If work with consumer notifications remains after capturing some work (.next work! = NULL), the consumer can scan the sleep list and signal the first thread, which is awake. After the producer adds work, the producer can scan the sleeping list and do the same.
If you have a broadcast scenario and it is not possible to transmit a single stream, just save the number of sleeping streams. As long as this account is still> 0, the consumer who has noticed the remaining work, and the work of adding the consumer will broadcast a signal for waking up.
In our environment, we have 1 thread per SMT, so the sleep list can never be so big (well, if I don’t get one of these new 128 parallel thread machines!) We create work items at an early stage of the transaction. In the first second, we could generate 10,000 work items, and this production is rapidly declining. Threads work for several seconds on these work items. Thus, we rarely have a thread in the inaction pool.
YOU MAY ALSO USE LOCKS If you only have 1 thread and you rarely work, this does not work for you. In this case, the performance of the mutexes is not a concern, and you should just use them. In this scenario, use the sleep queue lock. Think of a lock as it is "no locks where it counts."
PREVIOUS MAIL: You say: There is a line of work. There are many consumer flows. The consumer needs to put work in and do it if there is any work. The consumer stream should sleep until it works.
If you are, we do this using only atomic operations as follows:
A work queue is a linked list. There is also a linked list of sleeping threads.
To add work: CAS will lead the list for a new job. When work is added, we check if there are any threads in the sleeping list. If there is, before adding work, we CAS sleep from the list of sleepers, set its work = new job, and then ask the sleeping one to wake up. We are adding work to the work queue.
To consume work: CAS tops the list for head-> next. If the head of the job list is NULL, we create a thread in the sleepers list.
As soon as the thread has a work item, the thread should indicate to CAS the state of the WORK_INPROGRESS work item or some of them. If this fails, it means that the work is done by another, so the consumer flow returns to the job search. If the thread wakes up and has a work item, it should still have a CAS state.
So, if the work is added, the sleeping consumer always wakes up and transfers the work. pthread_kill () always wakes up the thread in sigwait (), because even if the thread receives a signal after the signal, the signal is received. This solves the thread problem, which is placed on the sleeping list, but receives a signal before bedtime. All that happens is that the thread is trying to own its → work, if one exists. Failure to work or lack of work directs the flow back to consumption. If the stream does not fall into the CAS list in the sleeping list, this means that either the other stream beat it or the producer removed the sleeping one. For security, we have a stream as if it just woke up.
We do not get any race conditions that do this, and have several manufacturers and consumers. We were also able to expand this to allow threads to sleep on individual work items.