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 "util/spmc_queue.h"
17 #include <cstdlib>
18 #include "dfx/log/ffrt_log_api.h"
19 namespace ffrt {
~SpmcQueue()20 SpmcQueue::~SpmcQueue()
21 {
22 if (buf_ != nullptr) {
23 free(buf_);
24 buf_ = nullptr;
25 }
26 }
27
Init(std::size_t capacity)28 int SpmcQueue::Init(std::size_t capacity)
29 {
30 if (capacity == 0) {
31 return -1;
32 }
33
34 buf_ = reinterpret_cast<void**>(malloc(capacity * sizeof(void*)));
35 if (buf_ == nullptr) {
36 FFRT_LOGE("Queue malloc failed, size: %u", capacity * sizeof(void*));
37 return -1;
38 }
39
40 capacity_ = capacity;
41 return 0;
42 }
43
GetLength() const44 std::size_t SpmcQueue::GetLength() const
45 {
46 return tail_.load() - head_.load();
47 }
48
GetCapacity() const49 std::size_t SpmcQueue::GetCapacity() const
50 {
51 return capacity_;
52 }
53
PopHead()54 void* SpmcQueue::PopHead()
55 {
56 if (buf_ == nullptr) {
57 return nullptr;
58 }
59
60 while (true) {
61 auto head = head_.load();
62 auto tail = tail_.load();
63 if (tail == head) {
64 return nullptr;
65 }
66 void* res = buf_[head % capacity_];
67 if (head_.compare_exchange_weak(head, head + 1)) {
68 return res;
69 }
70 }
71 }
72
PushTail(void * object)73 int SpmcQueue::PushTail(void* object)
74 {
75 if (buf_ == nullptr) {
76 return -1;
77 }
78
79 auto head = head_.load();
80 auto tail = tail_.load();
81 if ((tail - head) < capacity_) {
82 buf_[tail % capacity_] = object;
83 tail_.store(tail + 1);
84 return 0;
85 }
86
87 return -1;
88 }
89
PopHeadToAnotherQueue(SpmcQueue & dstQueue,unsigned int elementNum,PushFunc func)90 unsigned int SpmcQueue::PopHeadToAnotherQueue(SpmcQueue& dstQueue, unsigned int elementNum, PushFunc func)
91 {
92 if (elementNum == 0) {
93 return 0;
94 }
95
96 unsigned int pushCount = 0;
97 while ((dstQueue.GetLength() < dstQueue.GetCapacity()) && (head_.load() != tail_.load())) {
98 void* element = PopHead();
99 if (element == nullptr) {
100 break;
101 }
102
103 int ret = dstQueue.PushTail(element);
104 if (ret != 0) {
105 func(element);
106 return pushCount;
107 }
108
109 if (++pushCount == elementNum) {
110 break;
111 }
112 }
113
114 return pushCount;
115 }
116 }
117