udpipmap.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifndef UDPIPMAP_INCL
  14. #define UDPIPMAP_INCL
  15. #include "jsocket.hpp"
  16. #include "udplib.hpp"
  17. #include <functional>
  18. #include <iterator>
  19. #include <algorithm>
  20. template<class T> class IpMapOf
  21. {
  22. private:
  23. class list
  24. {
  25. public:
  26. list(const ServerIdentifier &_ip, const list *_next, std::function<T *(const ServerIdentifier)> tfunc) : ip(_ip), next(_next)
  27. {
  28. entry = tfunc(ip);
  29. }
  30. ~list()
  31. {
  32. delete entry;
  33. }
  34. const ServerIdentifier ip;
  35. const list *next;
  36. T *entry;
  37. };
  38. class myIterator
  39. {
  40. private:
  41. const list * value = nullptr;
  42. int hash = 0;
  43. const std::atomic<const list *> *table = nullptr;
  44. public:
  45. typedef T value_type;
  46. typedef std::ptrdiff_t difference_type;
  47. typedef T* pointer;
  48. typedef T& reference;
  49. typedef std::input_iterator_tag iterator_category;
  50. explicit myIterator(const list *_value, int _hash, const std::atomic<const list *> *_table)
  51. : value(_value), hash(_hash), table(_table)
  52. {
  53. }
  54. reference operator*() const { return *value->entry; }
  55. bool operator==(const myIterator& other) const { return value == other.value && hash==other.hash; }
  56. bool operator!=(const myIterator& other) const { return !(*this == other); }
  57. myIterator operator++(int)
  58. {
  59. myIterator ret = *this;
  60. ++(*this);
  61. return ret;
  62. }
  63. myIterator& operator++()
  64. {
  65. value = value->next;
  66. while (!value)
  67. {
  68. hash += 1;
  69. if (hash==256)
  70. break;
  71. value = table[hash].load(std::memory_order_acquire);
  72. }
  73. return *this;
  74. }
  75. };
  76. public:
  77. IpMapOf<T>(std::function<T *(const ServerIdentifier)> _tfunc) : tfunc(_tfunc)
  78. {
  79. }
  80. ~IpMapOf<T>()
  81. {
  82. for (unsigned idx = 0; idx < 256; idx++)
  83. {
  84. const list *head = table[idx].load(std::memory_order_acquire);
  85. while (head)
  86. {
  87. const list *next = head->next;
  88. delete head;
  89. head = next;
  90. }
  91. }
  92. }
  93. T &lookup(const ServerIdentifier &) const;
  94. inline T &operator[](const ServerIdentifier &ip) const { return lookup(ip); }
  95. myIterator begin()
  96. {
  97. // Take care as it's possible for firstHash to be updated on another thread as we are running
  98. unsigned lfirstHash = firstHash;
  99. if (lfirstHash==256)
  100. return end();
  101. else
  102. return myIterator(table[lfirstHash].load(std::memory_order_acquire), lfirstHash, table);
  103. }
  104. myIterator end() { return myIterator(nullptr, 256, nullptr); }
  105. private:
  106. const std::function<T *(const ServerIdentifier)> tfunc;
  107. mutable std::atomic<const list *> table[256] = {};
  108. mutable CriticalSection lock;
  109. mutable std::atomic<unsigned> firstHash = { 256 };
  110. };
  111. template<class T> T &IpMapOf<T>::lookup(const ServerIdentifier &ip) const
  112. {
  113. unsigned hash = ip.fasthash() & 0xff;
  114. for (;;)
  115. {
  116. const list *head = table[hash].load(std::memory_order_acquire);
  117. const list *finger = head;
  118. while (finger)
  119. {
  120. if (finger->ip == ip)
  121. return *finger->entry;
  122. finger = finger->next;
  123. }
  124. // If we get here, we need to add a new entry. This should be rare, so ok to lock
  125. // Note that we only lock out other additions, not other lookups
  126. // I could have a lock per table-entry if I thought it was worthwhile
  127. CriticalBlock b(lock);
  128. if (table[hash].load(std::memory_order_acquire) != head)
  129. continue; // NOTE - an alternative implementation would be to rescan the list inside the critsec, but this is cleaner
  130. finger = new list(ip, head, tfunc);
  131. table[hash].store(finger, std::memory_order_release);
  132. if (hash <= firstHash)
  133. firstHash = hash;
  134. return *finger->entry;
  135. }
  136. }
  137. #endif