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 #ifndef __WAITQUEUE_H__
17 #define __WAITQUEUE_H__
18 #include "sync.h"
19 #include "cpp/mutex.h"
20 #include "sched/execute_ctx.h"
21 #include "util/IntrusiveList.h"
22 #include "sync/mutex_private.h"
23 #include "dfx/log/ffrt_log_api.h"
24 
25 namespace ffrt {
26 class CPUEUTask;
27 struct TaskWithNode;
28 using TaskListNode = ListNode;
29 using TaskList = List<TaskWithNode, TaskListNode>;
30 
31 struct TaskTimeOutStatus {
TaskTimeOutStatusTaskTimeOutStatus32     explicit TaskTimeOutStatus(TaskTimeoutState state) : status(state)
33     {
34     }
35     TaskTimeoutState status;
36     std::mutex lock;
37 };
38 
39 enum class TimeoutState {
40     IDLE,
41     WAITING,
42     TIMEOUTING,
43     DONE,
44 };
45 
46 struct TimeoutStatus {
TimeoutStatusTimeoutStatus47     explicit TimeoutStatus(TimeoutState state) : status(state)
48     {
49     }
50     TimeoutState status;
51     mutex lock;
52 };
53 
54 struct TaskWithNode : public TaskListNode {
55     TaskWithNode();
56     CPUEUTask* task = nullptr;
57     std::mutex lk;
58     std::condition_variable cv;
59 };
60 
61 class WaitQueue {
62 public:
63     using TimePoint = std::chrono::steady_clock::time_point;
64     void SuspendAndWait(mutexPrivate* lk);
65     bool SuspendAndWaitUntil(mutexPrivate* lk, const TimePoint& tp) noexcept;
NotifyAll()66     void NotifyAll() noexcept { Notify(false); }
NotifyOne()67     void NotifyOne() noexcept { Notify(true); }
68 
WaitQueue()69     WaitQueue()
70     {
71         whead = new WaitUntilEntry();
72         whead->next = whead;
73         whead->prev = whead;
74     }
75     WaitQueue(WaitQueue const&) = delete;
76     void operator=(WaitQueue const&) = delete;
77 
~WaitQueue()78     ~WaitQueue()
79     {
80         wqlock.lock();
81         ReleaseAll();
82         delete whead;
83         whead = nullptr;
84         wqlock.unlock();
85     }
86 
87 private:
88     spin_mutex wqlock;
89     WaitUntilEntry* whead;
90 
91 private:
92     bool WeNotifyProc(WaitUntilEntry* we);
93     void ThreadWait(WaitUntilEntry* wn, mutexPrivate* lk, bool legacyMode, CPUEUTask* task);
94     bool ThreadWaitUntil(WaitUntilEntry* wn, mutexPrivate* lk, const TimePoint& tp, bool legacyMode, CPUEUTask* task);
95     void Notify(bool one) noexcept;
96 
empty()97     inline bool empty() const
98     {
99         if (whead == nullptr) {
100             return true;
101         }
102         return (whead->next == whead);
103     }
104 
ReleaseAll()105     void ReleaseAll()
106     {
107         while (!empty()) {
108             FFRT_LOGE("There are still tasks in cv that have not been awakened");
109             WaitUntilEntry *wue = pop_front();
110             (void)WeNotifyProc(wue);
111         }
112     }
113 
push_back(WaitUntilEntry * we)114     inline void push_back(WaitUntilEntry* we)
115     {
116         if ((we == nullptr) || (whead == nullptr) || (whead->prev == nullptr)) {
117             FFRT_LOGE("we or whead or whead->prev is nullptr");
118             return;
119         }
120         we->next = whead;
121         we->prev = whead->prev;
122         whead->prev->next = we;
123         whead->prev = we;
124     }
125 
pop_front()126     inline WaitUntilEntry* pop_front()
127     {
128         if ((whead->next == nullptr) || (whead->next->next == nullptr)) {
129             FFRT_LOGE("whead->next or whead->next->next is nullptr");
130             return nullptr;
131         }
132         WaitEntry *we = whead->next;
133         whead->next = we->next;
134         we->next->prev = whead;
135         we->next = nullptr;
136         we->prev = nullptr;
137         return static_cast<WaitUntilEntry*>(we);
138     }
139 
remove(WaitUntilEntry * we)140     inline void remove(WaitUntilEntry* we)
141     {
142         if ((we->next == nullptr) || (we->prev == nullptr)) {
143             return;
144         }
145         we->prev->next = we->next;
146         we->next->prev = we->prev;
147         we->next = nullptr;
148         we->prev = nullptr;
149         return;
150     }
151     friend bool WeTimeoutProc(WaitQueue* wq, WaitUntilEntry* wue);
152 };
153 } // namespace ffrt
154 #endif // _WAITQUEUE_H_
155