1/*
2 * Copyright (c) 2021 Rockchip Electronics 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#ifndef __MPP_THREAD_H__
17#define __MPP_THREAD_H__
18
19#if defined(_WIN32) && !defined(__MINGW32CE__)
20
21/*
22 * NOTE: POSIX Threads for Win32
23 * Downloaded from http://www.sourceware.org/pthreads-win32/
24 */
25#include "semaphore.h"
26#include "pthread.h"
27#pragma comment(lib, "pthreadVC2.lib")
28
29/*
30 * add pthread_setname_np for windows
31 */
32int pthread_setname_np(pthread_t thread, const char *name);
33
34#else
35
36#include <unistd.h>
37#include <semaphore.h>
38#include <pthread.h>
39#include <sys/time.h>
40
41#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
42#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER
43#endif
44
45#endif
46
47#define THREAD_NAME_LEN 16
48
49typedef void *(*MppThreadFunc)(void *);
50
51typedef enum {
52    MPP_THREAD_UNINITED,
53    MPP_THREAD_RUNNING,
54    MPP_THREAD_WAITING,
55    MPP_THREAD_STOPPING,
56} MppThreadStatus;
57
58#ifdef __cplusplus
59
60#include "mpp_log.h"
61
62/*
63 * for shorter type name and function name
64 */
65class Mutex {
66public:
67    Mutex();
68    ~Mutex();
69
70    void lock();
71    void unlock();
72    int trylock();
73
74    class Autolock {
75    public:
76        inline Autolock(Mutex *mutex, unsigned int enable = 1) : mEnabled(enable), mLock(*mutex)
77        {
78            if (mEnabled) {
79                mLock.lock();
80            }
81        }
82        inline ~Autolock()
83        {
84            if (mEnabled) {
85                mLock.unlock();
86            }
87        }
88
89    private:
90        signed int mEnabled;
91        Mutex &mLock;
92    };
93
94private:
95    friend class Condition;
96
97    pthread_mutex_t mMutex;
98
99    Mutex(const Mutex &);
100    Mutex &operator=(const Mutex &);
101};
102
103inline Mutex::Mutex()
104{
105    pthread_mutexattr_t attr;
106    pthread_mutexattr_init(&attr);
107    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
108    pthread_mutex_init(&mMutex, &attr);
109    pthread_mutexattr_destroy(&attr);
110}
111inline Mutex::~Mutex()
112{
113    pthread_mutex_destroy(&mMutex);
114}
115inline void Mutex::lock()
116{
117    pthread_mutex_lock(&mMutex);
118}
119inline void Mutex::unlock()
120{
121    pthread_mutex_unlock(&mMutex);
122}
123inline int Mutex::trylock()
124{
125    return pthread_mutex_trylock(&mMutex);
126}
127
128typedef Mutex::Autolock AutoMutex;
129
130/*
131 * for shorter type name and function name
132 */
133class Condition {
134public:
135    Condition();
136    Condition(int type);
137    ~Condition();
138    signed int wait(Mutex &mutex);
139    signed int wait(Mutex *mutex);
140    signed int timedwait(Mutex &mutex, RK_S64 timeout);
141    signed int timedwait(Mutex *mutex, RK_S64 timeout);
142    signed int signal();
143    signed int broadcast();
144
145private:
146    pthread_cond_t mCond;
147};
148
149inline Condition::Condition()
150{
151    pthread_cond_init(&mCond, NULL);
152}
153inline Condition::~Condition()
154{
155    pthread_cond_destroy(&mCond);
156}
157inline signed int Condition::wait(Mutex &mutex)
158{
159    return pthread_cond_wait(&mCond, &mutex.mMutex);
160}
161inline signed int Condition::wait(Mutex *mutex)
162{
163    return pthread_cond_wait(&mCond, &mutex->mMutex);
164}
165inline signed int Condition::timedwait(Mutex &mutex, RK_S64 timeout)
166{
167    return timedwait(&mutex, timeout);
168}
169inline signed int Condition::timedwait(Mutex *mutex, RK_S64 timeout)
170{
171    struct timespec ts;
172
173    clock_gettime(CLOCK_REALTIME_COARSE, &ts);
174
175    ts.tv_sec += timeout / 1000;              // 1000:Get milliseconds
176    ts.tv_nsec += (timeout % 1000) * 1000000; // 1000:Get milliseconds // 1000000:Get seconds
177    /* Prevent the out of range at nanoseconds field */
178    ts.tv_sec += ts.tv_nsec / 1000000000; // 1000000000:Get seconds
179    ts.tv_nsec %= 1000000000;             // 1000000000:Get Get nanoseconds
180
181    return pthread_cond_timedwait(&mCond, &mutex->mMutex, &ts);
182}
183inline signed int Condition::signal()
184{
185    return pthread_cond_signal(&mCond);
186}
187inline signed int Condition::broadcast()
188{
189    return pthread_cond_broadcast(&mCond);
190}
191
192class MppMutexCond {
193public:
194    MppMutexCond() {};
195    ~MppMutexCond() {};
196
197    void lock()
198    {
199        mLock.lock();
200    }
201    void unlock()
202    {
203        mLock.unlock();
204    }
205    void trylock()
206    {
207        mLock.trylock();
208    }
209    void wait()
210    {
211        mCondition.wait(mLock);
212    }
213    signed int wait(RK_S64 timeout)
214    {
215        return mCondition.timedwait(mLock, timeout);
216    }
217    void signal()
218    {
219        mCondition.signal();
220    }
221    void broadcast()
222    {
223        mCondition.broadcast();
224    }
225    Mutex *mutex()
226    {
227        return &mLock;
228    }
229
230private:
231    Mutex mLock;
232    Condition mCondition;
233};
234
235// Thread lock / signal is distinguished by its source
236typedef enum MppThreadSignal_e {
237    THREAD_WORK,    // for working loop
238    THREAD_INPUT,   // for thread input
239    THREAD_OUTPUT,  // for thread output
240    THREAD_CONTROL, // for thread async control (reset)
241    THREAD_SIGNAL_BUTT,
242} MppThreadSignal;
243
244#define THREAD_NORMAL 0
245#define THRE 0
246
247class MppThread {
248public:
249    MppThread(MppThreadFunc func, void *ctx, const char *name = NULL);
250    ~MppThread() {};
251
252    MppThreadStatus get_status(MppThreadSignal id = THREAD_WORK);
253    void set_status(MppThreadStatus status, MppThreadSignal id = THREAD_WORK);
254    void dump_status();
255
256    void start();
257    void stop();
258
259    void lock(MppThreadSignal id = THREAD_WORK)
260    {
261        mpp_assert(id < THREAD_SIGNAL_BUTT);
262        mMutexCond[id].lock();
263    }
264
265    void unlock(MppThreadSignal id = THREAD_WORK)
266    {
267        mpp_assert(id < THREAD_SIGNAL_BUTT);
268        mMutexCond[id].unlock();
269    }
270
271    void wait(MppThreadSignal id = THREAD_WORK)
272    {
273        mpp_assert(id < THREAD_SIGNAL_BUTT);
274        MppThreadStatus status = mStatus[id];
275
276        mStatus[id] = MPP_THREAD_WAITING;
277        mMutexCond[id].wait();
278
279        // check the status is not changed then restore status
280        if (mStatus[id] == MPP_THREAD_WAITING) {
281            mStatus[id] = status;
282        }
283    }
284
285    void signal(MppThreadSignal id = THREAD_WORK)
286    {
287        mpp_assert(id < THREAD_SIGNAL_BUTT);
288        mMutexCond[id].signal();
289    }
290
291    Mutex *mutex(MppThreadSignal id = THREAD_WORK)
292    {
293        mpp_assert(id < THREAD_SIGNAL_BUTT);
294        return mMutexCond[id].mutex();
295    }
296
297private:
298    pthread_t mThread;
299    MppMutexCond mMutexCond[THREAD_SIGNAL_BUTT];
300    MppThreadStatus mStatus[THREAD_SIGNAL_BUTT];
301
302    MppThreadFunc mFunction;
303    char mName[THREAD_NAME_LEN];
304    void *mContext;
305
306    MppThread();
307    MppThread(const MppThread &);
308    MppThread &operator=(const MppThread &);
309};
310
311#endif
312
313#endif /* __MPP_THREAD_H__ */
314