ASynchronizer.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. #include "interface/Iface.h"
  16. #include "interface/ASynchronizer.h"
  17. #include "memory/Allocator.h"
  18. #include "util/Identity.h"
  19. #include "util/events/Timeout.h"
  20. #include "util/log/Log.h"
  21. #include "util/Hex.h"
  22. #define MAX_DRY_CYCLES 16
  23. #define ArrayList_TYPE struct Message
  24. #define ArrayList_NAME Messages
  25. #include "util/ArrayList.h"
  26. struct ASynchronizer_pvt
  27. {
  28. struct ASynchronizer pub;
  29. struct Allocator* alloc;
  30. struct EventBase* base;
  31. struct Log* log;
  32. struct Allocator* cycleAlloc;
  33. struct ArrayList_Messages* msgsToA;
  34. struct ArrayList_Messages* msgsToB;
  35. struct Allocator* timeoutAlloc;
  36. struct Timeout* intr;
  37. int dryCycles;
  38. Identity
  39. };
  40. static void timeoutTrigger(void* vASynchronizer)
  41. {
  42. struct ASynchronizer_pvt* as = Identity_check((struct ASynchronizer_pvt*) vASynchronizer);
  43. if (!as->cycleAlloc) {
  44. if (as->dryCycles++ < MAX_DRY_CYCLES || !as->timeoutAlloc) { return; }
  45. Allocator_free(as->timeoutAlloc);
  46. as->timeoutAlloc = NULL;
  47. as->dryCycles = 0;
  48. return;
  49. }
  50. struct ArrayList_Messages* msgsToA = as->msgsToA;
  51. struct ArrayList_Messages* msgsToB = as->msgsToB;
  52. struct Allocator* cycleAlloc = as->cycleAlloc;
  53. as->msgsToA = NULL;
  54. as->msgsToB = NULL;
  55. as->cycleAlloc = NULL;
  56. if (msgsToA) {
  57. for (int i = 0; i < msgsToA->length; i++) {
  58. struct Message* msg = ArrayList_Messages_get(msgsToA, i);
  59. Iface_send(&as->pub.ifA, msg);
  60. }
  61. }
  62. if (msgsToB) {
  63. for (int i = 0; i < msgsToB->length; i++) {
  64. struct Message* msg = ArrayList_Messages_get(msgsToB, i);
  65. Iface_send(&as->pub.ifB, msg);
  66. }
  67. }
  68. Allocator_free(cycleAlloc);
  69. }
  70. static void checkTimeout(struct ASynchronizer_pvt* as)
  71. {
  72. // The timeout might still be present but inactive because Timeout_clearAll() was called
  73. // to setup a test, in that case lets re-arm it in order to get the message to the other side.
  74. if (as->intr && !Timeout_isActive(as->intr)) {
  75. Allocator_free(as->timeoutAlloc);
  76. as->timeoutAlloc = NULL;
  77. }
  78. if (as->timeoutAlloc) { return; }
  79. as->timeoutAlloc = Allocator_child(as->alloc);
  80. as->intr = Timeout_setInterval(timeoutTrigger, as, 1, as->base, as->timeoutAlloc);
  81. }
  82. static Iface_DEFUN fromA(struct Message* msg, struct Iface* ifA)
  83. {
  84. struct ASynchronizer_pvt* as = Identity_containerOf(ifA, struct ASynchronizer_pvt, pub.ifA);
  85. if (!as->cycleAlloc) { as->cycleAlloc = Allocator_child(as->alloc); }
  86. if (!as->msgsToB) { as->msgsToB = ArrayList_Messages_new(as->cycleAlloc); }
  87. Allocator_adopt(as->cycleAlloc, msg->alloc);
  88. ArrayList_Messages_add(as->msgsToB, msg);
  89. checkTimeout(as);
  90. return NULL;
  91. }
  92. static Iface_DEFUN fromB(struct Message* msg, struct Iface* ifB)
  93. {
  94. struct ASynchronizer_pvt* as = Identity_containerOf(ifB, struct ASynchronizer_pvt, pub.ifB);
  95. if (!as->cycleAlloc) { as->cycleAlloc = Allocator_child(as->alloc); }
  96. if (!as->msgsToA) { as->msgsToA = ArrayList_Messages_new(as->cycleAlloc); }
  97. Allocator_adopt(as->cycleAlloc, msg->alloc);
  98. ArrayList_Messages_add(as->msgsToA, msg);
  99. checkTimeout(as);
  100. return NULL;
  101. }
  102. struct ASynchronizer* ASynchronizer_new(struct Allocator* alloc,
  103. struct EventBase* base,
  104. struct Log* log)
  105. {
  106. struct ASynchronizer_pvt* ctx = Allocator_calloc(alloc, sizeof(struct ASynchronizer_pvt), 1);
  107. Identity_set(ctx);
  108. ctx->alloc = alloc;
  109. ctx->base = base;
  110. ctx->log = log;
  111. ctx->pub.ifA.send = fromA;
  112. ctx->pub.ifB.send = fromB;
  113. return &ctx->pub;
  114. }