+ * An application-specific error state for unregistered thread keeps
+ * track of thread errors. A thread reporting a health error, normally
+ * unregisters and quits. This makes the TLS health state not available
+ * to the health_check_state() call so on unregister we update this
+ * global error array so we can keep track of which thread was on error
+ * if the TLS health state has been removed.
+ */
+struct health_app {
+ /* List of health state, for each application thread */
+ struct cds_list_head list;
+ /*
+ * This lock ensures that TLS memory used for the node and its
+ * container structure don't get reclaimed after the TLS owner
+ * thread exits until we have finished using it.
+ */
+ pthread_mutex_t lock;
+ int nr_types;
+ struct timespec time_delta;
+ /* Health flags containing thread type error state */
+ enum health_flags *flags;
+};
+
+/* Define TLS health state. */
+DEFINE_URCU_TLS(struct health_state, health_state);
+
+struct health_app *health_app_create(int nr_types)
+{
+ struct health_app *ha;
+
+ ha = zmalloc(sizeof(*ha));
+ if (!ha) {
+ return NULL;
+ }
+ ha->flags = zmalloc(sizeof(*ha->flags));
+ if (!ha->flags) {
+ goto error_flags;
+ }
+ CDS_INIT_LIST_HEAD(&ha->list);
+ pthread_mutex_init(&ha->lock, NULL);
+ ha->nr_types = nr_types;
+ ha->time_delta.tv_sec = DEFAULT_HEALTH_CHECK_DELTA_S;
+ ha->time_delta.tv_nsec = DEFAULT_HEALTH_CHECK_DELTA_NS;
+ return ha;
+
+error_flags:
+ free(ha);
+ return NULL;
+}
+
+void health_app_destroy(struct health_app *ha)
+{
+ free(ha->flags);
+ free(ha);
+}
+
+/*
+ * Lock health state global list mutex.
+ */
+static void state_lock(struct health_app *ha)
+{
+ pthread_mutex_lock(&ha->lock);
+}
+
+/*
+ * Unlock health state global list mutex.
+ */
+static void state_unlock(struct health_app *ha)
+{
+ pthread_mutex_unlock(&ha->lock);
+}
+
+/*
+ * Set time difference in res from time_a and time_b.
+ */
+static void time_diff(const struct timespec *time_a,
+ const struct timespec *time_b, struct timespec *res)
+{
+ if (time_a->tv_nsec - time_b->tv_nsec < 0) {
+ res->tv_sec = time_a->tv_sec - time_b->tv_sec - 1;
+ res->tv_nsec = 1000000000L + time_a->tv_sec - time_b->tv_sec;
+ } else {
+ res->tv_sec = time_a->tv_sec - time_b->tv_sec;
+ res->tv_nsec = time_a->tv_nsec - time_b->tv_nsec;
+ }
+}
+
+/*
+ * Return true if time_a - time_b > diff, else false.
+ */
+static int time_diff_gt(const struct timespec *time_a,
+ const struct timespec *time_b, const struct timespec *diff)
+{
+ struct timespec res;
+
+ time_diff(time_a, time_b, &res);
+ time_diff(&res, diff, &res);
+
+ if (res.tv_sec > 0) {
+ return 1;
+ } else if (res.tv_sec == 0 && res.tv_nsec > 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Validate health state. Checks for the error flag or health conditions.