Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.0KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. MultiPseudoStack.cpp - Multiple-Writer Lock Free Datastructure
  4. Copyright (C) 2016 Mark McCurry
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. */
  10. #include "MultiPseudoStack.h"
  11. #include <cassert>
  12. #include <cstdio>
  13. #define INVALID ((int32_t)0xffffffff)
  14. #define MAX ((int32_t)0x7fffffff)
  15. namespace zyncarla {
  16. QueueListItem::QueueListItem(void)
  17. :memory(0), size(0)
  18. {
  19. }
  20. LockFreeQueue::LockFreeQueue(qli_t *data_, int n)
  21. :data(data_), elms(n), next_r(0), next_w(0), avail(0)
  22. {
  23. tag = new std::atomic<uint32_t>[n];
  24. for(int i=0; i<n; ++i)
  25. tag[i] = INVALID;
  26. }
  27. LockFreeQueue::~LockFreeQueue(void)
  28. {
  29. delete [] tag;
  30. }
  31. qli_t *LockFreeQueue::read(void) {
  32. retry:
  33. int8_t free_elms = avail.load();
  34. if(free_elms <= 0)
  35. return 0;
  36. int32_t next_tag = next_r.load();
  37. int32_t next_next_tag = (next_tag+1)&MAX;
  38. assert(next_tag != INVALID);
  39. for(int i=0; i<elms; ++i) {
  40. uint32_t elm_tag = tag[i].load();
  41. //attempt to remove tagged element
  42. //if and only if it's next
  43. if(((uint32_t)next_tag) == elm_tag) {
  44. if(!tag[i].compare_exchange_strong(elm_tag, INVALID))
  45. goto retry;
  46. //Ok, now there is no element that can be removed from the list
  47. //Effectively there's mutual exclusion over other readers here
  48. //Set the next element
  49. int sane_read = next_r.compare_exchange_strong(next_tag, next_next_tag);
  50. assert(sane_read && "No double read on a single tag");
  51. //Decrement available elements
  52. int32_t free_elms_next = avail.load();
  53. while(!avail.compare_exchange_strong(free_elms_next, free_elms_next-1));
  54. //printf("r%d ", free_elms_next-1);
  55. return &data[i];
  56. }
  57. }
  58. goto retry;
  59. }
  60. //Insert Node Q
  61. void LockFreeQueue::write(qli_t *Q) {
  62. retry:
  63. if(!Q)
  64. return;
  65. int32_t write_tag = next_w.load();
  66. int32_t next_write_tag = (write_tag+1)&MAX;
  67. if(!next_w.compare_exchange_strong(write_tag, next_write_tag))
  68. goto retry;
  69. uint32_t invalid_tag = INVALID;
  70. //Update tag
  71. int sane_write = tag[Q-data].compare_exchange_strong(invalid_tag, write_tag);
  72. assert(sane_write);
  73. //Increment available elements
  74. int32_t free_elms = avail.load();
  75. while(!avail.compare_exchange_strong(free_elms, free_elms+1))
  76. assert(free_elms <= 32);
  77. //printf("w%d ", free_elms+1);
  78. }
  79. MultiQueue::MultiQueue(void)
  80. :pool(new qli_t[32]), m_free(pool, 32), m_msgs(pool, 32)
  81. {
  82. //32 instances of 2kBi memory chunks
  83. for(int i=0; i<32; ++i) {
  84. qli_t &ptr = pool[i];
  85. ptr.size = 2048;
  86. ptr.memory = new char[2048];
  87. free(&ptr);
  88. }
  89. }
  90. MultiQueue::~MultiQueue(void)
  91. {
  92. for(int i=0; i<32; ++i)
  93. delete [] pool[i].memory;
  94. delete [] pool;
  95. }
  96. }