1/*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#define HST_LOG_TAG "ConditionVariable"
17
18#include "osal/task/condition_variable.h"
19#include <ratio>
20#include <ctime>
21#include "common/log.h"
22
23namespace {
24constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_FOUNDATION, "ConditionVariable" };
25}
26
27namespace {
28inline long long TmToNs(struct timespec tm)
29{
30    return tm.tv_sec * std::giga::num + tm.tv_nsec;
31}
32
33inline struct timespec NsToTm(long long ns)
34{
35    return {ns / std::giga::num, static_cast<long>(ns % std::giga::num)};
36}
37}
38
39namespace OHOS {
40namespace Media {
41ConditionVariable::ConditionVariable() noexcept : condInited_(true)
42{
43    pthread_condattr_t attr;
44    pthread_condattr_init(&attr);
45#ifndef __LITEOS_M__
46#ifdef USING_CLOCK_REALTIME
47    pthread_condattr_setclock(&attr, CLOCK_REALTIME);
48#else
49    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
50#endif
51#endif
52    int rtv = pthread_cond_init(&cond_, &attr);
53    if (rtv != 0) {
54        condInited_ = false;
55        MEDIA_LOG_E("failed to init pthread cond variable");
56    }
57    pthread_condattr_destroy(&attr);
58}
59
60ConditionVariable::~ConditionVariable() noexcept
61{
62    if (condInited_) {
63        pthread_cond_destroy(&cond_);
64    }
65}
66
67void ConditionVariable::NotifyOne() noexcept
68{
69    FALSE_RETURN_MSG(condInited_, "NotifyOne uninitialized pthread cond");
70    int ret = pthread_cond_signal(&cond_);
71    FALSE_LOG_MSG(ret == 0, "NotifyOne failed with errno = " PUBLIC_LOG_D32, ret);
72}
73
74void ConditionVariable::NotifyAll() noexcept
75{
76    FALSE_RETURN_MSG(condInited_, "NotifyAll uninitialized pthread cond");
77    int ret = pthread_cond_broadcast(&cond_);
78    FALSE_LOG_MSG(ret == 0, "NotifyAll failed with errno = " PUBLIC_LOG_D32, ret);
79}
80
81void ConditionVariable::Wait(AutoLock& lock) noexcept
82{
83    FALSE_RETURN_MSG(condInited_, "Wait uninitialized pthread cond");
84    pthread_cond_wait(&cond_, &(lock.mutex_->nativeHandle_));
85}
86
87void ConditionVariable::Wait(AutoLock& lock, std::function<bool()> pred) noexcept
88{
89    FALSE_RETURN_MSG(condInited_, "Wait uninitialized pthread cond");
90    while (!pred()) {
91        Wait(lock);
92    }
93}
94
95bool ConditionVariable::WaitFor(AutoLock& lock, int64_t timeoutMs)
96{
97    FALSE_RETURN_V_MSG_E(timeoutMs >= 0,
98        false, "ConditionVariable WaitUntil invalid timeoutMs: " PUBLIC_LOG_D64, timeoutMs);
99    FALSE_RETURN_V_MSG_E(condInited_,
100        false, "WaitFor uninitialized pthread cond");
101    struct timespec timeout = {0, 0};
102#ifdef USING_CLOCK_REALTIME
103    clock_gettime(CLOCK_REALTIME, &timeout);
104#else
105    clock_gettime(CLOCK_MONOTONIC, &timeout);
106#endif
107    timeout = NsToTm(TmToNs(timeout) + timeoutMs * std::mega::num);
108    return pthread_cond_timedwait(&cond_, &(lock.mutex_->nativeHandle_), &timeout) == 0;
109}
110
111bool ConditionVariable::WaitFor(AutoLock& lock, int64_t timeoutMs, std::function<bool()> pred)
112{
113    FALSE_RETURN_V_MSG_E(timeoutMs >= 0,
114        false, "ConditionVariable WaitUntil invalid timeoutMs: " PUBLIC_LOG_D64, timeoutMs);
115    FALSE_RETURN_V_MSG_E(condInited_,
116        false, "WaitFor uninitialized pthread cond");
117    struct timespec timeout = {0, 0};
118#ifdef USING_CLOCK_REALTIME
119    clock_gettime(CLOCK_REALTIME, &timeout);
120#else
121    clock_gettime(CLOCK_MONOTONIC, &timeout);
122#endif
123    timeout = NsToTm(TmToNs(timeout) + timeoutMs * std::mega::num);
124    int status = 0;
125    while (!pred() && (status == 0)) {
126        status = pthread_cond_timedwait(&cond_, &(lock.mutex_->nativeHandle_), &timeout);
127        if (status == ETIMEDOUT) {
128            return pred();
129        }
130    }
131    return status == 0;
132}
133} // namespace Media
134} // namespace OHOS
135