1 /**
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ImsMediaCondition.h>
18 #include <errno.h>
19 #include <sys/time.h>
20 
ImsMediaCondition()21 ImsMediaCondition::ImsMediaCondition()
22 {
23     reset();
24     mMutex = new pthread_mutex_t;
25     mCondition = new pthread_cond_t;
26 
27     if (mMutex != nullptr)
28     {
29         pthread_mutex_init(mMutex, nullptr);
30     }
31 
32     if (mCondition != nullptr)
33     {
34         pthread_cond_init(mCondition, nullptr);
35     }
36 }
37 
~ImsMediaCondition()38 ImsMediaCondition::~ImsMediaCondition()
39 {
40     if (mCondition != nullptr)
41     {
42         pthread_cond_destroy(mCondition);
43     }
44 
45     if (mMutex != nullptr)
46     {
47         pthread_mutex_destroy(mMutex);
48     }
49 
50     if (mCondition != nullptr)
51     {
52         delete mCondition;
53         mCondition = nullptr;
54     }
55 
56     if (mMutex != nullptr)
57     {
58         delete mMutex;
59         mMutex = nullptr;
60     }
61 }
62 
wait()63 void ImsMediaCondition::wait()
64 {
65     while (pthread_mutex_lock(mMutex) == EINTR)
66         ;
67     if (mSignalFlag & (1 << mWaitCount))
68     {  // signal() had been reached before wait()
69         mSignalFlag = mSignalFlag ^ (1 << mWaitCount);
70     }
71     else
72     {
73         mWaitFlag = mWaitFlag | (1 << mWaitCount);
74         while (pthread_cond_wait(mCondition, mMutex) == EINTR)
75             ;
76     }
77 
78     IncCount(&mWaitCount);
79     pthread_mutex_unlock(mMutex);
80 }
81 
wait_timeout(int64_t nRelativeTime)82 bool ImsMediaCondition::wait_timeout(int64_t nRelativeTime)
83 {
84     // make abs time
85     struct timespec ts;
86     struct timeval tv;
87     gettimeofday(&tv, (struct timezone*)nullptr);
88     uint64_t nInitTime = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
89     ts.tv_sec = tv.tv_sec + (nRelativeTime / 1000);
90     long addedUSec = tv.tv_usec + (nRelativeTime % 1000) * 1000L;
91 
92     if (addedUSec >= 1000000)
93     {
94         ts.tv_sec++;
95         addedUSec -= 1000000;
96     }
97 
98     ts.tv_nsec = addedUSec * 1000L;
99     // wait
100     while (pthread_mutex_lock(mMutex) == EINTR)
101         ;
102     if (mSignalFlag & (1 << mWaitCount))
103     {  // signal() had been reached before wait()
104         mSignalFlag = mSignalFlag ^ (1 << mWaitCount);
105     }
106     else
107     {
108         mWaitFlag = mWaitFlag | (1 << mWaitCount);
109         while (pthread_cond_timedwait(mCondition, mMutex, &ts) == EINTR)
110             ;
111     }
112 
113     IncCount(&mWaitCount);
114     pthread_mutex_unlock(mMutex);
115     struct timeval tl;
116     gettimeofday(&tl, (struct timezone*)nullptr);
117     uint64_t nCurrTime = (tl.tv_sec * 1000) + (tl.tv_usec / 1000);
118 
119     if (nCurrTime - nInitTime >= nRelativeTime)
120     {
121         return true;  // timeout
122     }
123     else
124     {
125         return false;  // signal
126     }
127 }
128 
signal()129 void ImsMediaCondition::signal()
130 {
131     if (mCondition == nullptr || mMutex == nullptr)
132     {
133         return;
134     }
135 
136     while (pthread_mutex_lock(mMutex) == EINTR)
137         ;
138 
139     if (mWaitFlag & (1 << mSignalCount))
140     {
141         mWaitFlag = mWaitFlag ^ (1 << mSignalCount);
142     }
143     else
144     {
145         mSignalFlag = mSignalFlag | (1 << mSignalCount);
146     }
147 
148     pthread_cond_signal(mCondition);
149     IncCount(&mSignalCount);
150     pthread_mutex_unlock(mMutex);
151 }
152 
reset()153 void ImsMediaCondition::reset()
154 {
155     mWaitFlag = 0;
156     mSignalFlag = 0;
157     mWaitCount = 0;
158     mSignalCount = 0;
159 }
160 
IncCount(uint32_t * pnCount)161 void ImsMediaCondition::IncCount(uint32_t* pnCount)
162 {
163     uint32_t count = *pnCount;
164     count++;
165     if (count == 32)
166         count = 0;
167     *pnCount = count;
168 }
169