1/*
2 * Copyright (c) 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#include "ecmascript/platform/mutex.h"
17
18#include <cstring>
19#include <ctime>
20
21namespace panda::ecmascript {
22
23Mutex::Mutex(bool is_init) : mutex_()
24{
25    if (is_init) {
26        Init(nullptr);
27    }
28}
29
30Mutex::~Mutex()
31{
32    [[maybe_unused]]int rc  = pthread_mutex_destroy(&mutex_);
33    FATAL_IF_ERROR("pthread_mutex_destroy", rc);
34}
35
36void Mutex::Init(pthread_mutexattr_t *attrs)
37{
38    [[maybe_unused]]int rc = pthread_mutex_init(&mutex_, attrs);
39    FATAL_IF_ERROR("pthread_mutex_init", rc);
40}
41
42void Mutex::Lock()
43{
44    [[maybe_unused]]int rc = pthread_mutex_lock(&mutex_);
45    FATAL_IF_ERROR("pthread_mutex_lock", rc);
46}
47
48bool Mutex::TryLock()
49{
50    int rc = pthread_mutex_trylock(&mutex_);
51    if (rc == EBUSY) {
52        return false;
53    }
54
55    FATAL_IF_ERROR("pthread_mutex_trylock", rc);
56
57    return true;
58}
59
60void Mutex::Unlock()
61{
62    [[maybe_unused]]int rc = pthread_mutex_unlock(&mutex_);
63    FATAL_IF_ERROR("pthread_mutex_unlock", rc);
64}
65
66RecursiveMutex::RecursiveMutex() : Mutex(false)
67{
68    pthread_mutexattr_t attrs;
69    pthread_mutexattr_init(&attrs);
70    pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
71    Init(&attrs);
72}
73
74RWLock::RWLock() : rwlock_()
75{
76    [[maybe_unused]]int rc = pthread_rwlock_init(&rwlock_, nullptr);
77    FATAL_IF_ERROR("pthread_rwlock_init", rc);
78}
79
80RWLock::~RWLock()
81{
82    [[maybe_unused]]int rc = pthread_rwlock_destroy(&rwlock_);
83    FATAL_IF_ERROR("pthread_rwlock_destroy", rc);
84}
85
86void RWLock::ReadLock()
87{
88    [[maybe_unused]]int rc = pthread_rwlock_rdlock(&rwlock_);
89    FATAL_IF_ERROR("pthread_rwlock_rdlock", rc);
90}
91
92void RWLock::WriteLock()
93{
94    [[maybe_unused]]int rc = pthread_rwlock_wrlock(&rwlock_);
95    FATAL_IF_ERROR("pthread_rwlock_wrlock", rc);
96}
97
98bool RWLock::TryReadLock()
99{
100    int rc = pthread_rwlock_tryrdlock(&rwlock_);
101    if (rc == EBUSY) {
102        return false;
103    }
104
105    FATAL_IF_ERROR("pthread_rwlock_tryrdlock", rc);
106
107    return true;
108}
109
110bool RWLock::TryWriteLock()
111{
112    int rc = pthread_rwlock_trywrlock(&rwlock_);
113    if (rc == EBUSY) {
114        return false;
115    }
116
117    FATAL_IF_ERROR("pthread_rwlock_trywrlock", rc);
118
119    return true;
120}
121
122void RWLock::Unlock()
123{
124    [[maybe_unused]]int rc = pthread_rwlock_unlock(&rwlock_);
125    FATAL_IF_ERROR("pthread_rwlock_unlock", rc);
126}
127
128ConditionVariable::ConditionVariable() : cond_()
129{
130    [[maybe_unused]]int rc = pthread_cond_init(&cond_, nullptr);
131    FATAL_IF_ERROR("pthread_cond_init", rc);
132}
133
134ConditionVariable::~ConditionVariable()
135{
136    [[maybe_unused]]int rc = pthread_cond_destroy(&cond_);
137    FATAL_IF_ERROR("pthread_cond_destroy", rc);
138}
139
140void ConditionVariable::Signal()
141{
142    [[maybe_unused]]int rc = pthread_cond_signal(&cond_);
143    FATAL_IF_ERROR("pthread_cond_signal", rc);
144}
145
146void ConditionVariable::SignalAll()
147{
148    [[maybe_unused]]int rc = pthread_cond_broadcast(&cond_);
149    FATAL_IF_ERROR("pthread_cond_broadcast", rc);
150}
151
152void ConditionVariable::Wait(Mutex *mutex)
153{
154    [[maybe_unused]]int rc = pthread_cond_wait(&cond_, &mutex->mutex_);
155    FATAL_IF_ERROR("pthread_cond_wait", rc);
156}
157
158struct timespec ConvertTime(uint64_t ms, uint64_t ns, bool is_absolute)
159{
160    struct timespec abs_time = {0, 0};
161    if (!is_absolute) {
162        clock_gettime(CLOCK_REALTIME, &abs_time);
163    }
164    const int64_t MILLISECONDS_PER_SEC = 1000;
165    const int64_t NANOSECONDS_PER_MILLISEC = 1000000;
166    const int64_t NANOSECONDS_PER_SEC = 1000000000;
167    time_t seconds = ms / MILLISECONDS_PER_SEC;
168    time_t nanoseconds = (ms % MILLISECONDS_PER_SEC) * NANOSECONDS_PER_MILLISEC + ns;
169    abs_time.tv_sec += seconds;
170    abs_time.tv_nsec += nanoseconds;
171    if (abs_time.tv_nsec >= NANOSECONDS_PER_SEC) {
172        abs_time.tv_nsec -= NANOSECONDS_PER_SEC;
173        abs_time.tv_sec++;
174    }
175    return abs_time;
176}
177
178bool ConditionVariable::TimedWait(Mutex *mutex, uint64_t ms, uint64_t ns, bool is_absolute)
179{
180    struct timespec abs_time = ConvertTime(ms, ns, is_absolute);
181    int rc = pthread_cond_timedwait(&cond_, &mutex->mutex_, &abs_time);
182    if (rc != 0) {
183        if (rc == ETIMEDOUT) {
184            // interrupted
185            return true;
186        }
187    }
188    FATAL_IF_ERROR("pthread_cond_timedwait", rc);
189    return false;
190}
191}  // namespace panda::ecmascript