{"id":94775,"date":"2016-11-24T07:00:00","date_gmt":"2016-11-24T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=94775"},"modified":"2019-03-13T10:34:11","modified_gmt":"2019-03-13T17:34:11","slug":"20161124-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20161124-00\/?p=94775","title":{"rendered":"Lock free many-producer\/single-consumer patterns: A work queue of distinct events, order not important"},"content":{"rendered":"<p>Almost done with our miniseries on lock-free many-producer\/single-consumer patterns. Today, we&#8217;ll look at the case of multiple producers generating distinct work items which cannot be coalesced, but for which the order of processing is not important. <\/p>\n<p>As I noted last time, you can view this as a scenario built on top of the previous one. In the previous scenario, there was a counter of the number of work items to be done, but the work itself was fixed. You can pair this with another data structure that contains a collection of things to do. In that case, the  <code>Do&shy;Some&shy;Work()<\/code> function pulls a work item out of the collection. <\/p>\n<p>We&#8217;re going to go one step further: We&#8217;re going to let the work item be its own counter. <\/p>\n<pre>\nSLIST_HEADER WorkQueue;\n\nstruct alignas(MEMORY_ALLOCATION_ALIGNMENT)\nWorkItem : SLIST_ENTRY\n{\n    ... other stuff ...\n};\n\nvoid Initialize()\n{\n InitializeSListHeader(&amp;WorkQueue);\n}\n\nvoid RequestWork(WorkItem* work)\n{\n if (InterlockedPushEntrySList(&amp;WorkQueue, work)\n                                               == nullptr) {\n  \/\/ You provide the WakeConsumer() function.\n  WakeConsumer();\n }\n}\n\n\/\/ You call this function when the consumer receives the\n\/\/ signal raised by WakeConsumer().\nvoid ConsumeWork()\n{\n PSLIST_ENTRY entry;\n while ((entry = InterlockedPopEntrySList(&amp;WorkQueue))\n                                               != nullptr) {\n  ProcessWorkItem(static_cast&lt;WorkItem*&gt;(entry));\n  delete entry;\n }\n}\n<\/pre>\n<p>We use the lock-free linked list data structure <code>LIST_HEADER<\/code> to manage our work queue. To request some work, we push an item onto the front of the list. The <code>Interlocked&shy;Push&shy;Entry&shy;SList<\/code> function returns the previous list head. If that returns <code>nullptr<\/code>, then it means that the list was empty, so we wake up the consumer. If the list was not empty, then it means that somebody else woke the consumer, so we won&#8217;t do it (to avoid a spurious wake). <\/p>\n<p>On the consumer side, we atomically pop work off the list and process them, and we stop when there is no more work. Since the order in which work items are processed is presumed to be unimportant, we can process them LIFO. <\/p>\n<p>Remember, there is only one consumer, so if <code>Wake&shy;Consumer<\/code> is called while <code>Consume&shy;Work<\/code> is still running, the wake will not start a new consumer immediately. It will wait for the existing <code>Consume&shy;Work<\/code> to complete before starting a new <code>Consume&shy;Work<\/code>. <\/p>\n<p>Next time, we&#8217;ll wrap up by looking at the case where work items must be processed FIFO. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Each one is different in its own special way, but we don&#8217;t care what order they are processed.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-94775","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Each one is different in its own special way, but we don&#8217;t care what order they are processed.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/94775","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=94775"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/94775\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=94775"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=94775"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=94775"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}