impl.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #include "access.h"
  2. #include "variable.h"
  3. #include <library/cpp/yt/memory/leaky_singleton.h>
  4. #include <array>
  5. namespace NYT::NGlobal {
  6. ////////////////////////////////////////////////////////////////////////////////
  7. namespace NDetail {
  8. inline constexpr int MaxTrackedGlobalVariables = 32;
  9. ////////////////////////////////////////////////////////////////////////////////
  10. class TGlobalVariablesRegistry
  11. {
  12. public:
  13. static TGlobalVariablesRegistry* Get()
  14. {
  15. return LeakySingleton<TGlobalVariablesRegistry>();
  16. }
  17. void RegisterAccessor(const TVariableTag& tag, TAccessor accessor)
  18. {
  19. if (!tag.Initialized_.exchange(true, std::memory_order::relaxed)) { // (a)
  20. DoRegisterAccessor(tag, accessor);
  21. return;
  22. }
  23. TryVerifyingExistingAccessor(tag, accessor);
  24. }
  25. std::optional<TErasedStorage> GetVariable(const TVariableTag& tag)
  26. {
  27. auto key = tag.Key_.load(std::memory_order::acquire); // (e)
  28. if (key != -1) {
  29. return Accessors_[key](); // (f)
  30. }
  31. return std::nullopt;
  32. }
  33. private:
  34. std::atomic<int> KeyGenerator_ = 0;
  35. std::array<TAccessor, MaxTrackedGlobalVariables> Accessors_;
  36. void DoRegisterAccessor(const TVariableTag& tag, TAccessor accessor)
  37. {
  38. // Get id -> place accessor -> store id
  39. auto key = KeyGenerator_.fetch_add(1, std::memory_order::relaxed); // (b)
  40. YT_VERIFY(key < MaxTrackedGlobalVariables);
  41. Accessors_[key] = accessor; // (c)
  42. tag.Key_.store(key, std::memory_order::release); // (d)
  43. }
  44. void TryVerifyingExistingAccessor(const TVariableTag& tag, TAccessor accessor)
  45. {
  46. auto key = tag.Key_.load(std::memory_order::acquire); // (e')
  47. if (key == -1) {
  48. // Accessor is about to be set.
  49. // In order to avoid deadlock caused by forks
  50. // we just leave. We could try acquiring fork
  51. // locks here but this makes our check too expensive
  52. // to be bothered.
  53. return;
  54. }
  55. // Accessor has been already set -> safe to read it.
  56. YT_VERIFY(Accessors_[key] == accessor); // (f')
  57. }
  58. };
  59. // (arkady-e1ppa): Memory orders:
  60. /*
  61. We have two scenarios: 2 writes and write & read:
  62. 2 writes: Accessors_ is protected via Initialized_ flag
  63. and KeyGenerator_ counter.
  64. 1) RMW (a) reads the last value in modification order
  65. thus relaxed is enough to ensure <= 1 threads registering
  66. per Tag.
  67. 2) KeyGenerator_ uses the same logic (see (b))
  68. to ensure <= 1 threads registering per index in array.
  69. If there are two writes per tag, then there is a "losing"
  70. thread which read Initialized_ // true. For all intents
  71. and purposes TryVerifyingExistingAccessor call is identical
  72. to GetVariable call.
  73. write & read: Relevant execution is below
  74. W^na(Accessors_[id], 0x0) // Ctor
  75. T1(Register) T2(Read)
  76. W^na(Accessors_[id], 0x42) (c) R^acq(Key_, id) (e)
  77. W^rel(Key_, id) (d) R^na(Accessors_[id], 0x42) (f)
  78. (d) -rf-> (e) => (d) -SW-> (e). Since (c) -SB-> (d) and (e) -SB-> (f)
  79. we have (c) -strongly HB-> (f) (strongly happens before). Thus we must
  80. read 0x42 from Accessors_[id] (and not 0x0 which was written in ctor).
  81. */
  82. ////////////////////////////////////////////////////////////////////////////////
  83. void RegisterVariable(const TVariableTag& tag, TAccessor accessor)
  84. {
  85. TGlobalVariablesRegistry::Get()->RegisterAccessor(tag, accessor);
  86. }
  87. } // namespace NDetail
  88. ////////////////////////////////////////////////////////////////////////////////
  89. std::optional<TErasedStorage> GetErasedVariable(const TVariableTag& tag)
  90. {
  91. return NDetail::TGlobalVariablesRegistry::Get()->GetVariable(tag);
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////
  94. } // namespace NYT::NGlobal