1 /**
2 * Copyright (c) 2024 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 #include "plugins/ets/runtime/ets_vm.h"
16 #include "plugins/ets/runtime/types/ets_sync_primitives.h"
17
18 #include <atomic>
19
20 namespace ark::ets {
21
22 /*static*/
Create(EtsCoroutine * coro)23 EtsMutex *EtsMutex::Create(EtsCoroutine *coro)
24 {
25 auto *klass = coro->GetPandaVM()->GetClassLinker()->GetMutexClass();
26 auto hMutex = EtsHandle<EtsMutex>(coro, EtsMutex::FromEtsObject(EtsObject::Create(coro, klass)));
27 auto *waitersList = EtsWaitersList::Create(coro);
28 hMutex->SetWaitersList(coro, waitersList);
29 return hMutex.GetPtr();
30 }
31
Lock()32 void EtsMutex::Lock()
33 {
34 // Atomic with acq_rel order reason: sync Lock/Unlock in other threads
35 if (waiters_.fetch_add(1, std::memory_order_acq_rel) == 0) {
36 return;
37 }
38 auto *coroManager = EtsCoroutine::GetCurrent()->GetCoroutineManager();
39 auto awaitee = EtsWaitersList::Node(coroManager);
40 SuspendCoroutine(&awaitee);
41 }
42
Unlock()43 void EtsMutex::Unlock()
44 {
45 // Atomic with acq_rel order reason: sync Lock/Unlock in other threads
46 if (waiters_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
47 return;
48 }
49 ResumeCoroutine();
50 }
51
IsHeld()52 bool EtsMutex::IsHeld()
53 {
54 // Atomic with relaxed order reason: sync is not needed here
55 // because it is expected that method is not called concurrently with Lock/Unlock
56 return waiters_.load(std::memory_order_relaxed) != 0;
57 }
58
59 /*static*/
Create(EtsCoroutine * coro)60 EtsEvent *EtsEvent::Create(EtsCoroutine *coro)
61 {
62 auto *klass = coro->GetPandaVM()->GetClassLinker()->GetEventClass();
63 auto hEvent = EtsHandle<EtsEvent>(coro, EtsEvent::FromEtsObject(EtsObject::Create(coro, klass)));
64 auto *waitersList = EtsWaitersList::Create(coro);
65 hEvent->SetWaitersList(coro, waitersList);
66 return hEvent.GetPtr();
67 }
68
Wait()69 void EtsEvent::Wait()
70 {
71 // Atomic with acq_rel order reason: sync Wait/Fire in other threads
72 auto state = state_.fetch_add(ONE_WAITER, std::memory_order_acq_rel);
73 if (IsFireState(state)) {
74 return;
75 }
76 auto *coroManager = EtsCoroutine::GetCurrent()->GetCoroutineManager();
77 auto awaitee = EtsWaitersList::Node(coroManager);
78 SuspendCoroutine(&awaitee);
79 }
80
Fire()81 void EtsEvent::Fire()
82 {
83 // Atomic with acq_rel order reason: sync Wait/Fire in other threads
84 auto state = state_.exchange(FIRE_STATE, std::memory_order_acq_rel);
85 if (IsFireState(state)) {
86 return;
87 }
88 for (auto waiters = GetNumberOfWaiters(state); waiters > 0; --waiters) {
89 ResumeCoroutine();
90 }
91 }
92
93 /* static */
Create(EtsCoroutine * coro)94 EtsCondVar *EtsCondVar::Create(EtsCoroutine *coro)
95 {
96 auto *classLinker = coro->GetPandaVM()->GetClassLinker();
97 auto *klass = classLinker->GetCondVarClass();
98 auto hCondVar = EtsHandle<EtsCondVar>(coro, EtsCondVar::FromEtsObject(EtsObject::Create(klass)));
99 auto *waitersList = EtsWaitersList::Create(coro);
100 hCondVar->SetWaitersList(coro, waitersList);
101 return hCondVar.GetPtr();
102 }
103
Wait(EtsHandle<EtsMutex> & mutex)104 void EtsCondVar::Wait(EtsHandle<EtsMutex> &mutex)
105 {
106 ASSERT(mutex->IsHeld());
107 waiters_++;
108 mutex->Unlock();
109 auto *coroManager = EtsCoroutine::GetCurrent()->GetCoroutineManager();
110 auto awaitee = EtsWaitersList::Node(coroManager);
111 SuspendCoroutine(&awaitee);
112 mutex->Lock();
113 }
114
NotifyOne(EtsMutex * mutex)115 void EtsCondVar::NotifyOne([[maybe_unused]] EtsMutex *mutex)
116 {
117 ASSERT(mutex->IsHeld());
118 if (waiters_ != 0) {
119 ResumeCoroutine();
120 waiters_--;
121 }
122 }
123
NotifyAll(EtsMutex * mutex)124 void EtsCondVar::NotifyAll([[maybe_unused]] EtsMutex *mutex)
125 {
126 ASSERT(mutex->IsHeld());
127 while (waiters_ != 0) {
128 ResumeCoroutine();
129 waiters_--;
130 }
131 }
132
133 } // namespace ark::ets
134