| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "completion.h" |
| 4 | |
| 5 | ALWAYS_INLINE void completion_reset(struct completion *p) |
| 6 | { |
| 7 | if (!p) |
| 8 | return; |
| 9 | p->completed = 0; |
| 10 | p->completed_jobs = 0; |
| 11 | } |
| 12 | |
| 13 | ALWAYS_INLINE void completion_init(struct completion *p) |
| 14 | { |
| 15 | p->completed = 0; |
| 16 | p->completed_jobs = 0; |
| 17 | fatal_assert(0 == netdata_cond_init(&p->cond)); |
| 18 | fatal_assert(0 == netdata_mutex_init(&p->mutex)); |
| 19 | } |
| 20 | |
| 21 | ALWAYS_INLINE void completion_destroy(struct completion *p) |
| 22 | { |
| 23 | netdata_cond_destroy(&p->cond); |
| 24 | netdata_mutex_destroy(&p->mutex); |
| 25 | } |
| 26 | |
| 27 | ALWAYS_INLINE void completion_wait_for(struct completion *p) |
| 28 | { |
| 29 | netdata_mutex_lock(&p->mutex); |
| 30 | while (0 == p->completed) { |
| 31 | netdata_cond_wait(&p->cond, &p->mutex); |
| 32 | } |
| 33 | fatal_assert(1 == p->completed); |
| 34 | netdata_mutex_unlock(&p->mutex); |
| 35 | } |
| 36 | |
| 37 | ALWAYS_INLINE bool completion_timedwait_for(struct completion *p, uint64_t timeout_s) |
| 38 | { |
| 39 | uint64_t timeout_ns = timeout_s * NSEC_PER_SEC; |
| 40 | if (timeout_ns == 0) timeout_ns = 1; |
| 41 | |
| 42 | uint64_t deadline_ns = uv_hrtime() + timeout_ns; |
| 43 | bool result = true; |
| 44 | |
| 45 | netdata_mutex_lock(&p->mutex); |
| 46 | while (!p->completed && result) { |
| 47 | uint64_t current_time_ns = uv_hrtime(); |
| 48 | |
| 49 | // Check if we've already exceeded the deadline |
| 50 | if (current_time_ns >= deadline_ns) { |
| 51 | result = false; |
| 52 | break; |
| 53 | } |
| 54 | |
| 55 | uint64_t remaining_timeout_ns = deadline_ns - current_time_ns; |
| 56 | |
| 57 | int rc = netdata_cond_timedwait(&p->cond, &p->mutex, remaining_timeout_ns); |
| 58 | |
| 59 | if (rc == UV_ETIMEDOUT) |
| 60 | result = false; |
| 61 | |
| 62 | // Condition was signaled (or spurious wakeup). |
| 63 | // The loop condition `!p->completed` will be re-evaluated. |
| 64 | // If p->completed is true, the loop exits. |
| 65 | // If p->completed is false (spurious wakeup), the loop continues with a new remaining_timeout_ns. |
| 66 | } |
| 67 | netdata_mutex_unlock(&p->mutex); |
| 68 | |
| 69 | return result; |
| 70 | } |
| 71 | |
| 72 | ALWAYS_INLINE void completion_mark_complete(struct completion *p) |
| 73 | { |
| 74 | netdata_mutex_lock(&p->mutex); |
| 75 | p->completed = 1; |
| 76 | netdata_cond_broadcast(&p->cond); |
| 77 | netdata_mutex_unlock(&p->mutex); |
| 78 | } |
| 79 | |
| 80 | ALWAYS_INLINE unsigned completion_wait_for_a_job(struct completion *p, unsigned completed_jobs) |
| 81 | { |
| 82 | netdata_mutex_lock(&p->mutex); |
| 83 | while (0 == p->completed && p->completed_jobs <= completed_jobs) { |
| 84 | netdata_cond_wait(&p->cond, &p->mutex); |
| 85 | } |
| 86 | completed_jobs = p->completed_jobs; |
| 87 | netdata_mutex_unlock(&p->mutex); |
| 88 | |
| 89 | return completed_jobs; |
| 90 | } |
| 91 | |
| 92 | ALWAYS_INLINE unsigned completion_wait_for_a_job_with_timeout(struct completion *p, unsigned completed_jobs, uint64_t timeout_ms) |
| 93 | { |
| 94 | uint64_t timeout_ns = timeout_ms * NSEC_PER_MSEC; |
| 95 | if (timeout_ns == 0) timeout_ns = 1; |
| 96 | |
| 97 | uint64_t deadline_ns = uv_hrtime() + timeout_ns; |
| 98 | |
| 99 | netdata_mutex_lock(&p->mutex); |
| 100 | |
| 101 | while (p->completed == 0 && p->completed_jobs <= completed_jobs) { |
| 102 | uint64_t current_time_ns = uv_hrtime(); |
| 103 | |
| 104 | // Check if we've already exceeded the deadline |
| 105 | if (current_time_ns >= deadline_ns) { |
| 106 | break; |
| 107 | } |
| 108 | |
| 109 | uint64_t remaining_timeout_ns = deadline_ns - current_time_ns; |
| 110 | |
| 111 | int rc = netdata_cond_timedwait(&p->cond, &p->mutex, remaining_timeout_ns); |
| 112 | if (rc == UV_ETIMEDOUT) |
| 113 | break; |
| 114 | } |
| 115 | |
| 116 | completed_jobs = p->completed_jobs; |
| 117 | netdata_mutex_unlock(&p->mutex); |
| 118 | |
| 119 | return completed_jobs; |
| 120 | } |
| 121 | |
| 122 | ALWAYS_INLINE void completion_mark_complete_a_job(struct completion *p) |
| 123 | { |
| 124 | netdata_mutex_lock(&p->mutex); |
| 125 | p->completed_jobs++; |
| 126 | netdata_cond_broadcast(&p->cond); |
| 127 | netdata_mutex_unlock(&p->mutex); |
| 128 | } |
| 129 | |
| 130 | ALWAYS_INLINE bool completion_is_done(struct completion *p) |
| 131 | { |
| 132 | bool ret; |
| 133 | netdata_mutex_lock(&p->mutex); |
| 134 | ret = p->completed; |
| 135 | netdata_mutex_unlock(&p->mutex); |
| 136 | return ret; |
| 137 | } |