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.

112 lines
2.9KB

  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. QueueListItem::QueueListItem(void)
  16. :memory(0), size(0)
  17. {
  18. }
  19. LockFreeQueue::LockFreeQueue(qli_t *data_, int n)
  20. :data(data_), elms(n), next_r(0), next_w(0), avail(0)
  21. {
  22. tag = new std::atomic<uint32_t>[n];
  23. for(int i=0; i<n; ++i)
  24. tag[i] = INVALID;
  25. }
  26. qli_t *LockFreeQueue::read(void) {
  27. retry:
  28. int8_t free_elms = avail.load();
  29. if(free_elms <= 0)
  30. return 0;
  31. int32_t next_tag = next_r.load();
  32. int32_t next_next_tag = (next_tag+1)&MAX;
  33. assert(next_tag != INVALID);
  34. for(int i=0; i<elms; ++i) {
  35. uint32_t elm_tag = tag[i].load();
  36. //attempt to remove tagged element
  37. //if and only if it's next
  38. if(((uint32_t)next_tag) == elm_tag) {
  39. if(!tag[i].compare_exchange_strong(elm_tag, INVALID))
  40. goto retry;
  41. //Ok, now there is no element that can be removed from the list
  42. //Effectively there's mutual exclusion over other readers here
  43. //Set the next element
  44. int sane_read = next_r.compare_exchange_strong(next_tag, next_next_tag);
  45. assert(sane_read && "No double read on a single tag");
  46. //Decrement available elements
  47. int32_t free_elms_next = avail.load();
  48. while(!avail.compare_exchange_strong(free_elms_next, free_elms_next-1));
  49. //printf("r%d ", free_elms_next-1);
  50. return &data[i];
  51. }
  52. }
  53. goto retry;
  54. }
  55. //Insert Node Q
  56. void LockFreeQueue::write(qli_t *Q) {
  57. retry:
  58. if(!Q)
  59. return;
  60. int32_t write_tag = next_w.load();
  61. int32_t next_write_tag = (write_tag+1)&MAX;
  62. if(!next_w.compare_exchange_strong(write_tag, next_write_tag))
  63. goto retry;
  64. uint32_t invalid_tag = INVALID;
  65. //Update tag
  66. int sane_write = tag[Q-data].compare_exchange_strong(invalid_tag, write_tag);
  67. assert(sane_write);
  68. //Increment available elements
  69. int32_t free_elms = avail.load();
  70. while(!avail.compare_exchange_strong(free_elms, free_elms+1))
  71. assert(free_elms <= 32);
  72. //printf("w%d ", free_elms+1);
  73. }
  74. MultiQueue::MultiQueue(void)
  75. :pool(new qli_t[32]), m_free(pool, 32), m_msgs(pool, 32)
  76. {
  77. //32 instances of 2kBi memory chunks
  78. for(int i=0; i<32; ++i) {
  79. qli_t &ptr = pool[i];
  80. ptr.size = 2048;
  81. ptr.memory = new char[2048];
  82. free(&ptr);
  83. }
  84. }
  85. MultiQueue::~MultiQueue(void)
  86. {
  87. for(int i=0; i<32; ++i)
  88. delete [] pool[i].memory;
  89. delete [] pool;
  90. }