1  /*
2   * Copyright (C) 2022 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  #include "hks_condition.h"
17  
18  #include "hks_mem.h"
19  #include "hks_template.h"
20  
21  #include <errno.h>
22  #include <pthread.h>
23  #include <stdatomic.h>
24  #include <stdbool.h>
25  #include <stdlib.h>
26  #include <string.h>
27  
28  #ifdef __cplusplus
29  extern "C" {
30  #endif
31  
32  struct HksCondition {
33      volatile atomic_bool notified;
34      pthread_mutex_t mutex;
35      pthread_cond_t cond;
36  };
37  
38  #define HKS_LOG_ERRNO(msg, ret) ({ int currentErrno = errno; \
39      HKS_LOG_E(msg " %" LOG_PUBLIC "d, errno %" LOG_PUBLIC "d, strerror %" LOG_PUBLIC "s", \
40          (ret), currentErrno, strerror(currentErrno)); })
41  
HksConditionWait(HksCondition * condition)42  int32_t HksConditionWait(HksCondition *condition)
43  {
44      HKS_IF_NULL_LOGE_RETURN(condition, -1, "HksConditionWait condition is NULL!")
45  
46      int32_t ret = pthread_mutex_lock(&condition->mutex);
47      if (ret != 0) {
48          HKS_LOG_ERRNO("HksConditionWait pthread_mutex_lock fail!", ret);
49          return ret;
50      }
51      if (atomic_load(&condition->notified)) {
52          int unlockRet = pthread_mutex_unlock(&condition->mutex);
53          if (unlockRet != 0) {
54              HKS_LOG_ERRNO("HksConditionWait notified pthread_mutex_unlock fail!", unlockRet);
55          }
56          return 0;
57      } else {
58          HKS_LOG_I("HksConditionWait begin wait...");
59          ret = pthread_cond_wait(&condition->cond, &condition->mutex);
60          if (ret != 0) {
61              HKS_LOG_ERRNO("HksConditionWait pthread_cond_wait fail!", ret);
62          }
63          int unlockRet = pthread_mutex_unlock(&condition->mutex);
64          if (unlockRet != 0) {
65              HKS_LOG_ERRNO("HksConditionWait waited pthread_mutex_unlock fail!", unlockRet);
66          }
67          return ret;
68      }
69  }
70  
HksConditionNotify(HksCondition * condition)71  int32_t HksConditionNotify(HksCondition *condition)
72  {
73      HKS_IF_NULL_LOGE_RETURN(condition, -1, "HksConditionNotify condition is NULL!")
74  
75      int32_t ret = pthread_mutex_lock(&condition->mutex);
76      if (ret != 0) {
77          HKS_LOG_ERRNO("HksConditionNotify pthread_mutex_lock fail!", ret);
78          return ret;
79      }
80  
81      bool flag = false;
82      if (atomic_compare_exchange_strong(&condition->notified, &flag, true)) {
83          HKS_LOG_I("never pthread_cond_signal before, first time notify!");
84      } else {
85          HKS_LOG_W("do pthread_cond_signal again!");
86      }
87  
88      ret = pthread_cond_signal(&condition->cond);
89      if (ret != 0) {
90          HKS_LOG_ERRNO("HksConditionNotify pthread_cond_signal fail!", ret);
91      }
92      int unlockRet = pthread_mutex_unlock(&condition->mutex);
93      if (unlockRet != 0) {
94          HKS_LOG_ERRNO("HksConditionNotify pthread_mutex_unlock fail!", unlockRet);
95      }
96      return ret;
97  }
98  
HksConditionNotifyAll(HksCondition * condition)99  int32_t HksConditionNotifyAll(HksCondition *condition)
100  {
101      HKS_IF_NULL_LOGE_RETURN(condition, -1, "HksConditionNotifyAll condition is NULL!")
102  
103      int32_t ret = pthread_mutex_lock(&condition->mutex);
104      if (ret != 0) {
105          HKS_LOG_ERRNO("HksConditionNotifyAll pthread_mutex_lock fail!", ret);
106          return ret;
107      }
108  
109      bool flag = false;
110      if (atomic_compare_exchange_strong(&condition->notified, &flag, true)) {
111          HKS_LOG_I("never pthread_cond_broadcast before, first time notify!");
112      } else {
113          HKS_LOG_W("do pthread_cond_broadcast again!");
114      }
115  
116      ret = pthread_cond_broadcast(&condition->cond);
117      if (ret != 0) {
118          HKS_LOG_ERRNO("HksConditionNotifyAll pthread_cond_broadcast fail!", ret);
119      }
120      int unlockRet = pthread_mutex_unlock(&condition->mutex);
121      if (unlockRet != 0) {
122          HKS_LOG_ERRNO("HksConditionNotifyAll pthread_mutex_unlock fail!", unlockRet);
123      }
124      return ret;
125  }
126  
HksConditionCreate(void)127  HksCondition *HksConditionCreate(void)
128  {
129      HksCondition *condition = (HksCondition *)HksMalloc(sizeof(HksCondition));
130      HKS_IF_NULL_RETURN(condition, NULL)
131      atomic_store(&condition->notified, false);
132      int32_t ret = pthread_mutex_init(&condition->mutex, NULL);
133      if (ret != 0) {
134          HKS_LOG_ERRNO("HksConditionCreate pthread_mutex_init fail!", ret);
135          HKS_FREE(condition);
136          return NULL;
137      }
138  
139      pthread_condattr_t attr;
140      int attrRet = pthread_condattr_init(&attr);
141      if (attrRet != 0) {
142          HKS_LOG_ERRNO("HksConditionCreate pthread_condattr_init fail!", attrRet);
143      }
144      ret = pthread_cond_init(&condition->cond, &attr);
145      attrRet = pthread_condattr_destroy(&attr);
146      if (attrRet != 0) {
147          HKS_LOG_ERRNO("HksConditionCreate pthread_condattr_destroy fail!", attrRet);
148      }
149      if (ret != 0) {
150          HKS_LOG_ERRNO("HksConditionCreate pthread_cond_init fail!", ret);
151          pthread_mutex_destroy(&condition->mutex);
152          HKS_FREE(condition);
153          return NULL;
154      }
155      return condition;
156  }
157  
HksConditionDestroy(HksCondition * condition)158  void HksConditionDestroy(HksCondition* condition)
159  {
160      if (condition == NULL) {
161          HKS_LOG_E("HksConditionDestroy condition is NULL!");
162          return;
163      }
164      int ret = pthread_mutex_destroy(&condition->mutex);
165      if (ret != 0) {
166          HKS_LOG_ERRNO("HksConditionDestroy pthread_mutex_destroy fail!", ret);
167      }
168      ret = pthread_cond_destroy(&condition->cond);
169      if (ret != 0) {
170          HKS_LOG_ERRNO("HksConditionDestroy pthread_cond_destroy fail!", ret);
171      }
172      HKS_FREE(condition);
173  }
174  
175  #ifdef __cplusplus
176  }
177  #endif