handle_state.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. * Copyright (c) 2018-2019 SignalWire, Inc
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in all
  12. * copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. #pragma once
  23. /* Handle states provide hooks to do things when they change, and they
  24. * describe whether the handle is open for use or not */
  25. typedef enum {
  26. SWCLT_HSTATE_INVALID, /* this is the no state, only used to distinguish between set and not set */
  27. SWCLT_HSTATE_NORMAL, /* the default state, we assume everything is normal post allocation. */
  28. SWCLT_HSTATE_ONLINE, /* the handle is now 'online' in whatever definition they want to consider it to be */
  29. SWCLT_HSTATE_OFFLINE, /* the handle is now 'offline' in whatever definition they want to consider it to be */
  30. SWCLT_HSTATE_DEGRADED, /* the service provided by the handle has halted due to some kind of failure.
  31. * Note: The primary difference between DEGRADED and OFFLINE is that DEGRADED is
  32. * caused by some kind of error, wherease OFFLINE may just be a request from the client. */
  33. } SWCLT_HSTATE;
  34. /* swclt_hstate_str - Returns a string version for the state enumeration value */
  35. static inline const char *swclt_hstate_str(SWCLT_HSTATE state)
  36. {
  37. switch (state) {
  38. case SWCLT_HSTATE_INVALID:
  39. return "Invalid";
  40. case SWCLT_HSTATE_NORMAL:
  41. return "Normal";
  42. case SWCLT_HSTATE_ONLINE:
  43. return "Online";
  44. case SWCLT_HSTATE_OFFLINE:
  45. return "Offline";
  46. case SWCLT_HSTATE_DEGRADED:
  47. return "Degraded";
  48. default:
  49. ks_abort_fmt("Invalid handle state: %d", state);
  50. }
  51. }
  52. /* Forward declare our handle type as it is composed of inner types that reference it */
  53. typedef struct swclt_hstate_change_request swclt_hstate_change_t;
  54. typedef struct swclt_handle_base swclt_handle_base_t;
  55. /* A state change callback is available on any signalwire client handle */
  56. typedef void(*swclt_hstate_change_cb_t)(swclt_handle_base_t *ctx, swclt_hstate_change_t *state_change_request);
  57. /* State change handler for when a handle wants to transition state and execute code as part of the transition */
  58. typedef ks_status_t (*swclt_hstate_state_change_handler_cb_t)(swclt_handle_base_t *ctx, SWCLT_HSTATE requested_new_state);
  59. /* Service callback for all client handles */
  60. typedef void(*swclt_hstate_service_cb_t)(swclt_handle_base_t *ctx);
  61. /* Context structure used to describe a state change listener on a handle */
  62. typedef struct swclt_hstate_listener_s {
  63. swclt_hstate_change_cb_t cb; /* Called anytime state changes on this handle, by the service manager thread */
  64. ks_handle_t handle; /* We check this ptr out before calling the callers callback */
  65. } swclt_hstate_listener_t;
  66. /* The state change request structure gets allocated by the handle anytime it wants to start a state change
  67. * request. The manager will then take this state change and apply it in its thread */
  68. struct swclt_hstate_change_request {
  69. /* the requested state to change to */
  70. SWCLT_HSTATE new_state;
  71. /* the original state snapshotted at allocation */
  72. SWCLT_HSTATE old_state;
  73. /* Reason and status for the state change (reason must be non zero when
  74. * state is degraded, and must be zero when state is normal) */
  75. char *reason;
  76. ks_status_t status;
  77. #if defined(KS_BUILD_DEBUG)
  78. /* For debugging we stash the file/line/tag anytime state changes */
  79. const char *file;
  80. const char *tag;
  81. int line;
  82. #endif
  83. };
  84. /* Describes in detail the pending state change, including the reason and status code. If debug
  85. * is enabled, will include the file/line where the state change was initiated from. */
  86. static inline const char *swclt_hstate_describe_change(const swclt_hstate_change_t *pending_state_change)
  87. {
  88. static KS_THREAD_LOCAL char buf[1024] = { 0 };
  89. #if defined(KS_BUILD_DEBUG)
  90. snprintf(buf, sizeof(buf), "[%s=>%s] Status: %d Reason: %s\nLocation: %s:%d:%s",
  91. swclt_hstate_str(pending_state_change->old_state),
  92. swclt_hstate_str(pending_state_change->new_state),
  93. pending_state_change->status, pending_state_change->reason,
  94. pending_state_change->file, pending_state_change->line, pending_state_change->tag);
  95. #else
  96. snprintf(buf, sizeof(buf), "[%s=>%s] Status: %d Reason: %s",
  97. swclt_hstate_str(pending_state_change->old_state),
  98. swclt_hstate_str(pending_state_change->new_state),
  99. pending_state_change->status, pending_state_change->reason);
  100. #endif
  101. return buf;
  102. }
  103. /* Private handle state apis used internally, but still exposed for unit tests */
  104. SWCLT_DECLARE(ks_status_t) swclt_hstate_check_ctx(const swclt_handle_base_t *ctx, const char *log_message);
  105. SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_get_ctx(const swclt_handle_base_t *ctx);
  106. SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_get(ks_handle_t handle);
  107. SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_pending_get(ks_handle_t handle);
  108. SWCLT_DECLARE(SWCLT_HSTATE) swclt_hstate_current_get(ks_handle_t handle);
  109. SWCLT_DECLARE(ks_status_t) swclt_hstate_check(ks_handle_t handle, const char *log_message);
  110. SWCLT_DECLARE(void) __swclt_hstate_changed(swclt_handle_base_t *ctx,SWCLT_HSTATE new_state, ks_status_t status, const char *reason, const char *file, int line, const char *tag);
  111. SWCLT_DECLARE(void) __swclt_hstate_initiate_change_in(
  112. swclt_handle_base_t *base,
  113. SWCLT_HSTATE new_state,
  114. swclt_hstate_state_change_handler_cb_t cb,
  115. ks_time_t next_service_ms,
  116. ks_time_t retry_delay_ms);
  117. #define swclt_hstate_initiate_change_in(base, new_state, cb, next_service_ms, retry_delay_ms)\
  118. __swclt_hstate_initiate_change_in(base, new_state, (swclt_hstate_state_change_handler_cb_t)cb, next_service_ms, retry_delay_ms)
  119. SWCLT_DECLARE(void) __swclt_hstate_initiate_change_now(
  120. swclt_handle_base_t *base,
  121. SWCLT_HSTATE new_state,
  122. swclt_hstate_state_change_handler_cb_t cb,
  123. ks_time_t retry_delay_ms);
  124. #define swclt_hstate_initiate_change_now(base, new_state, cb, retry_delay_ms)\
  125. __swclt_hstate_initiate_change_now(base, new_state, (swclt_hstate_state_change_handler_cb_t)cb, retry_delay_ms)
  126. SWCLT_DECLARE(ks_status_t) __swclt_hstate_register_listener(
  127. swclt_handle_base_t *listening_ctx,
  128. swclt_hstate_change_cb_t state_change_cb,
  129. ks_handle_t state_change_source_handle,
  130. const char *file,
  131. int line,
  132. const char *tag);
  133. SWCLT_DECLARE(void) __swclt_hstate_register_service(swclt_handle_base_t *ctx, swclt_hstate_service_cb_t service_cb, const char *file, int line, const char *tag);
  134. /* Use these macros to forward the context to debug build structures while also providing a cast for
  135. * the callbacks. */
  136. #define swclt_hstate_changed(ctx, new_state, status, reason) __swclt_hstate_changed(ctx, new_state, status, reason, __FILE__, __LINE__, __PRETTY_FUNCTION__)
  137. #define swclt_hstate_register_listener(ctx, cb, handle) __swclt_hstate_register_listener((swclt_handle_base_t *)ctx, (swclt_hstate_change_cb_t)cb, handle, __FILE__, __LINE__, __PRETTY_FUNCTION__)
  138. #define swclt_hstate_register_service(ctx, service_cb) __swclt_hstate_register_service(ctx, (swclt_hstate_service_cb_t)service_cb, __FILE__, __LINE__, __PRETTY_FUNCTION__)