1 /*
2 * Copyright (c) 2022-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 /**
17 * @addtogroup DriverHdi
18 * @{
19 *
20 * @brief Provides APIs for a system ability to obtain hardware device interface (HDI) services,
21 * load or unload a device, and listen for service status, and capabilities for the hdi-gen tool to
22 * automatically generate code in interface description language (IDL).
23 *
24 * The HDF and IDL code generated allow the system ability to accesses HDI driver services.
25 *
26 * @since 1.0
27 */
28
29 /**
30 * @file hdi_smq.h
31 *
32 * @brief Provides APIs for the shared memory queue (SMQ).
33 * The SMQ is a common mechanism for inter-process communication. The SMQ must comply with the IDL syntax.
34 * You only need to define the SMQ struct in IDL for the service module.
35 * The HDI module provides common operations for reading and writing the SMQ.
36 *
37 * @since 1.0
38 */
39
40 #ifndef HDI_SHARED_MEM_QUEUE_INF_H
41 #define HDI_SHARED_MEM_QUEUE_INF_H
42
43 #include <ashmem.h>
44 #include <atomic>
45 #include <cerrno>
46 #include <datetime_ex.h>
47 #include <hdf_base.h>
48 #include <hdf_log.h>
49 #include <base/hdi_smq_meta.h>
50 #include <base/hdi_smq_syncer.h>
51 #include <memory>
52 #include <securec.h>
53 #include <cstdint>
54 #include <cstring>
55 #include <sys/mman.h>
56
57 #ifndef PAGE_SIZE
58 #define PAGE_SIZE 4096
59 #endif
60
61 #ifndef HDF_LOG_TAG
62 #define HDF_LOG_TAG smq
63 #endif
64
65 namespace OHOS {
66 namespace HDI {
67 namespace Base {
68 /**
69 * @brief Defines the <b>SharedMemQueue</b> class.
70 *
71 * The SMQ is a message queue used for simplex communication between processes.
72 * It allows data write from one end and read at the other end, in either blocking or non-blocking mode.
73 */
74 template <typename T>
75 class SharedMemQueue {
76 public:
77 /**
78 * @brief A constructor used to create a <b>SharedMemQueue</b> object.
79 *
80 * @param elementCount Indicates the queue size, that is, the maximum number of elements allowed in the queue.
81 * @param type Indicates whether the SMQ is synchronous (<b>SYNCED_SMQ</b>) or asynchronous (<b>UNSYNC_SMQ</b>).
82 */
83 SharedMemQueue(uint32_t elementCount, SmqType type);
84
85 /**
86 * @brief A function used to copy the <b>SharedMemQueue</b> object.
87 */
88 explicit SharedMemQueue(const SharedMemQueueMeta<T> &meta);
89 ~SharedMemQueue();
90
91 /**
92 * @brief Writes an array of elements to the SMQ in blocking mode.
93 *
94 * When the SMQ is full, this API is blocked until the queue is writeable.
95 *
96 * @param data Indicates the pointer to the array of elements to write.
97 * @param count Indicates the number of elements to write.
98 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
99 */
100 int Write(const T *data, size_t count);
101
102 /**
103 * @brief Reads an array of elements from the SMQ in blocking mode.
104 *
105 * When the SMQ is empty, this API is blocked until the queue is readable.
106 *
107 * @param data Indicates the pointer to the buffer for storing the elements read.
108 * @param count Indicates the number of elements to read.
109 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
110 */
111 int Read(T *data, size_t count);
112
113 /**
114 * @brief Writes a single element to the SMQ in blocking mode.
115 *
116 * When the SMQ is full, this API is blocked until the queue is writeable.
117 *
118 * @param data Indicates the pointer to the single element to write.
119 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
120 */
121 int Write(const T *data);
122
123 /**
124 * @brief Reads a single element from the SMQ in blocking mode.
125 *
126 * When the SMQ is empty, this API is blocked until the queue is readable.
127 *
128 * @param data Indicates the pointer to the buffer for storing the single element read.
129 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
130 */
131 int Read(T *data);
132
133 /**
134 * @brief Writes a fixed number of elements to the SMQ in blocking mode.
135 *
136 * When the SMQ is full, this API is blocked until the queue is writeable or the write operation times out.
137 *
138 * @param data Indicates the pointer to the array of elements to write.
139 * @param count Indicates the number of elements to write.
140 * @param waitTimeNanoSec Indicates the write operation timeout period, in nanoseconds.
141 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
142 */
143 int Write(const T *data, size_t count, int64_t waitTimeNanoSec);
144
145 /**
146 * @brief Reads a fixed number of elements from the SMQ in blocking mode.
147 *
148 * When the number of elements in the SMQ is less than the number of elements to read,
149 * this API is blocked until the queue is readable or the read operation times out.
150 *
151 * @param data Indicates the pointer to the buffer for storing the elements read.
152 * @param count Indicates the number of elements to read.
153 * @param waitTimeNanoSec Indicates the read operation timeout period, in nanoseconds.
154 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
155 */
156 int Read(T *data, size_t count, int64_t waitTimeNanoSec);
157
158 /**
159 * @brief Writes a single element to the SMQ in non-blocking mode.
160 *
161 * When the SMQ queue is full, the SMPQ overflows. The overflowed element will be overwritten.
162 *
163 * @param data Indicates the pointer to the single element to write.
164 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
165 */
166 int WriteNonBlocking(const T *data);
167
168 /**
169 * @brief Reads a single element from the SMQ in non-blocking mode.
170 *
171 * @param data Indicates the pointer to the buffer for storing the element read.
172 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
173 */
174 int ReadNonBlocking(T *data);
175
176 /**
177 * @brief Writes a fixed number of elements to the SMQ in non-blocking mode.
178 *
179 * When the SMQ is full, the SMQ overflows. The overflowed elements will be overwritten.
180 *
181 * @param data Indicates the pointer to the elements to write.
182 * @param count Indicates the number of elements to write.
183 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
184 */
185 int WriteNonBlocking(const T *data, size_t count);
186
187 /**
188 * @brief Reads a fixed number of elements from the SMQ in non-blocking mode.
189 *
190 * If the SMQ queue does not have sufficient elements to read, a failure is returned immediately.
191 *
192 * @param data Indicates the pointer to the buffer for storing the data read.
193 * The number of elements that can be held in the buffer must be greater than the number of elements read.
194 * @param count Indicates the number of elements to read.
195 * @return Returns <b>0</b> if the operation is successful; returns a non-zero value otherwise.
196 */
197 int ReadNonBlocking(T *data, size_t count);
198
199 /**
200 * @brief Obtains the number of elements that can be written to the SMQ.
201 *
202 * @return Returns the number of elements that can be written to the SMQ.
203 */
204 size_t GetAvalidWriteSize();
205
206 /**
207 * @brief Obtains the number of elements that can be read from the SMQ.
208 *
209 * @return Returns the number of elements that can be read from the SMQ.
210 */
211 size_t GetAvalidReadSize();
212
213 /**
214 * @brief Obtains the size of the SMQ, in bytes.
215 *
216 * @return Returns the number of bytes occupied by the SMQ.
217 */
218 size_t GetSize();
219
220 /**
221 * @brief Obtains the metadata object.
222 *
223 * @return Returns the metadata object obtained.
224 */
225 std::shared_ptr<SharedMemQueueMeta<T>> GetMeta();
226
227 /**
228 * @brief Checks whether the SMQ object is valid.
229 *
230 * @return Returns <b>true</b> if the object is valid; returns <b>false</b> otherwise.
231 */
232 bool IsGood();
233
234 /**
235 * @brief Obtains the current timestamp, in nanoseconds.
236 *
237 * @return Returns the timestamp obtained.
238 */
GetNanoTime()239 static inline int64_t GetNanoTime()
240 {
241 struct timespec ts;
242 clock_gettime(CLOCK_MONOTONIC, &ts);
243 return (ts.tv_sec * SEC_TO_NANOSEC + ts.tv_nsec);
244 }
245
246 private:
247 void Init(bool resetWriteOffset);
248 uintptr_t MapMemZone(uint32_t zoneType);
249 void UnMapMemZone(void *addr, uint32_t zoneType);
250 size_t Align(size_t num, size_t alignSize);
251
252 int32_t status = HDF_FAILURE;
253 uint8_t *queueBuffer_ = nullptr;
254 std::atomic<uint64_t> *readOffset_ = nullptr;
255 std::atomic<uint64_t> *writeOffset_ = nullptr;
256 std::atomic<uint32_t> *syncerPtr_ = nullptr;
257 std::unique_ptr<SharedMemQueueSyncer> syncer_ = nullptr;
258 std::shared_ptr<SharedMemQueueMeta<T>> meta_ = nullptr;
259 };
260
261 template <typename T>
SharedMemQueue(uint32_t elementCount,SmqType type)262 SharedMemQueue<T>::SharedMemQueue(uint32_t elementCount, SmqType type)
263 {
264 if (elementCount == 0 || elementCount > UINT16_MAX) {
265 HDF_LOGE("invalid elementCount for smq:%{public}u", elementCount);
266 return;
267 }
268
269 meta_ = std::make_shared<SharedMemQueueMeta<T>>(elementCount, type);
270 HDF_LOGI("create SharedMemQueue, count=%{public}u, size=%{public}zu", elementCount, meta_->GetSize());
271 int ashmemFd = AshmemCreate("hdi_smq", Align(meta_->GetSize(), PAGE_SIZE));
272 if (ashmemFd < 0) {
273 HDF_LOGE("failed to create ashmem");
274 return;
275 }
276 meta_->SetFd(ashmemFd);
277 Init(true);
278 }
279
280 template <typename T>
SharedMemQueue(const SharedMemQueueMeta<T> & meta)281 SharedMemQueue<T>::SharedMemQueue(const SharedMemQueueMeta<T> &meta)
282 {
283 meta_ = std::make_shared<SharedMemQueueMeta<T>>(meta);
284 Init(false);
285 }
286
287 template <typename T>
~SharedMemQueue()288 SharedMemQueue<T>::~SharedMemQueue()
289 {
290 if (meta_ != nullptr && meta_->GetType() == SYNCED_SMQ && readOffset_ != nullptr) {
291 UnMapMemZone(readOffset_, SharedMemQueueMeta<T>::MemZoneType::MEMZONE_RPTR);
292 } else {
293 delete readOffset_;
294 readOffset_ = nullptr;
295 }
296
297 if (writeOffset_ != nullptr) {
298 UnMapMemZone(writeOffset_, SharedMemQueueMeta<T>::MEMZONE_WPTR);
299 }
300
301 if (syncerPtr_ != nullptr) {
302 UnMapMemZone(syncerPtr_, SharedMemQueueMeta<T>::MEMZONE_SYNCER);
303 }
304
305 if (queueBuffer_ != nullptr) {
306 UnMapMemZone(queueBuffer_, SharedMemQueueMeta<T>::MEMZONE_DATA);
307 }
308 }
309
310 template <typename T>
Init(bool resetWriteOffset)311 void SharedMemQueue<T>::Init(bool resetWriteOffset)
312 {
313 if (meta_ == nullptr) {
314 HDF_LOGE("invalid smq meta for init");
315 return;
316 }
317
318 if (meta_->GetType() == SYNCED_SMQ) {
319 readOffset_ = reinterpret_cast<std::atomic<uint64_t> *>(MapMemZone(SharedMemQueueMeta<T>::MEMZONE_RPTR));
320 } else {
321 readOffset_ = new std::atomic<uint64_t>;
322 }
323
324 if (readOffset_ == nullptr) {
325 HDF_LOGE("failed to map read offset");
326 return;
327 }
328
329 writeOffset_ = reinterpret_cast<std::atomic<uint64_t> *>(MapMemZone(SharedMemQueueMeta<T>::MEMZONE_WPTR));
330 if (writeOffset_ == nullptr) {
331 HDF_LOGE("failed to map write offset");
332 return;
333 }
334
335 syncerPtr_ = reinterpret_cast<std::atomic<uint32_t> *>(MapMemZone(SharedMemQueueMeta<T>::MEMZONE_SYNCER));
336 if (syncerPtr_ == nullptr) {
337 HDF_LOGE("failed to map sync ptr");
338 return;
339 }
340
341 queueBuffer_ = reinterpret_cast<uint8_t *>(MapMemZone(SharedMemQueueMeta<T>::MEMZONE_DATA));
342 if (queueBuffer_ == nullptr) {
343 HDF_LOGE("failed to map queue buffer");
344 return;
345 }
346
347 syncer_ = std::make_unique<SharedMemQueueSyncer>(syncerPtr_);
348
349 if (resetWriteOffset) {
350 writeOffset_->store(0, std::memory_order_release);
351 }
352 readOffset_->store(0, std::memory_order_release);
353 HDF_LOGI("smq init succ");
354 status = HDF_SUCCESS;
355 }
356
357 template <typename T>
MapMemZone(uint32_t zoneType)358 uintptr_t SharedMemQueue<T>::MapMemZone(uint32_t zoneType)
359 {
360 auto memzone = meta_->GetMemZone(zoneType);
361 if (memzone == nullptr) {
362 HDF_LOGE("invalid smq mem zone type %{public}u", zoneType);
363 return reinterpret_cast<uintptr_t>(nullptr);
364 }
365
366 int offset = (static_cast<int>(memzone->offset) / PAGE_SIZE) * PAGE_SIZE;
367 int length = static_cast<int>(memzone->offset) - offset + static_cast<int>(memzone->size);
368
369 void *ptr = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, meta_->GetFd(), offset);
370 if (ptr == MAP_FAILED) {
371 HDF_LOGE(
372 "failed to map memzone %{public}u, size %{public}u, offset %{public}u , fd %{public}d, errnor=%{public}d",
373 zoneType, length, offset, meta_->GetFd(), errno);
374 return reinterpret_cast<uintptr_t>(nullptr);
375 }
376 return (reinterpret_cast<uintptr_t>(ptr) + (static_cast<int>(memzone->offset) - offset));
377 }
378
379 template <typename T>
UnMapMemZone(void * addr,uint32_t zoneType)380 void SharedMemQueue<T>::UnMapMemZone(void *addr, uint32_t zoneType)
381 {
382 auto memzone = meta_->GetMemZone(zoneType);
383 if (memzone == nullptr) {
384 return;
385 }
386 int offset = (static_cast<int>(memzone->offset) / PAGE_SIZE) * PAGE_SIZE;
387 int length = static_cast<int>(memzone->offset) - offset + static_cast<int>(memzone->size);
388 uint8_t *ptr = reinterpret_cast<uint8_t *>(addr) - (static_cast<int>(memzone->offset) - offset);
389 if (ptr == nullptr) {
390 return;
391 }
392 munmap(ptr, length);
393 }
394
395 template <typename T>
IsGood()396 bool SharedMemQueue<T>::IsGood()
397 {
398 return status == HDF_SUCCESS;
399 }
400
401 template <typename T>
Align(size_t num,size_t alignSize)402 size_t SharedMemQueue<T>::Align(size_t num, size_t alignSize)
403 {
404 if (alignSize < 1 || num > (SIZE_MAX - alignSize)) {
405 HDF_LOGE("Invalid parameter num or alignSize");
406 return 0;
407 }
408 return (num + alignSize - 1) & ~(alignSize - 1);
409 }
410
411 template <typename T>
Write(const T * data,size_t count)412 int SharedMemQueue<T>::Write(const T *data, size_t count)
413 {
414 return Write(data, count, 0);
415 }
416
417 template <typename T>
Read(T * data,size_t count)418 int SharedMemQueue<T>::Read(T *data, size_t count)
419 {
420 return Read(data, count, 0);
421 }
422
423 template <typename T>
Write(const T * data)424 int SharedMemQueue<T>::Write(const T *data)
425 {
426 return Write(data, 1, 0);
427 }
428
429 template <typename T>
Read(T * data)430 int SharedMemQueue<T>::Read(T *data)
431 {
432 return Read(data, 1, 0);
433 }
434
435 template <typename T>
Write(const T * data,size_t count,int64_t waitTimeNanoSec)436 int SharedMemQueue<T>::Write(const T *data, size_t count, int64_t waitTimeNanoSec)
437 {
438 if (meta_->GetType() != SmqType::SYNCED_SMQ) {
439 HDF_LOGE("unsynecd smq not support blocking write");
440 return HDF_ERR_NOT_SUPPORT;
441 }
442
443 if (WriteNonBlocking(data, count) == 0) {
444 return syncer_->Wake(SharedMemQueueSyncer::SYNC_WORD_READ);
445 }
446
447 int ret = 0;
448 auto startTime = GetNanoTime();
449 int64_t currentTime = startTime;
450 while (true) {
451 if (waitTimeNanoSec != 0) {
452 currentTime = GetNanoTime();
453 waitTimeNanoSec -= (currentTime - startTime);
454 startTime = currentTime;
455 if (waitTimeNanoSec <= 0) {
456 ret = WriteNonBlocking(data, count);
457 break;
458 }
459 }
460 ret = syncer_->Wait(SharedMemQueueSyncer::SYNC_WORD_WRITE, waitTimeNanoSec);
461 if (ret != 0 && ret != -ETIMEDOUT) {
462 break;
463 }
464
465 ret = WriteNonBlocking(data, count);
466 if (ret == 0) {
467 break;
468 }
469 }
470
471 if (ret == 0) {
472 ret = syncer_->Wake(SharedMemQueueSyncer::SYNC_WORD_READ);
473 } else {
474 HDF_LOGE("failed to write %{public}zu, ret=%{public}d", count, ret);
475 }
476
477 return ret;
478 }
479
480 template <typename T>
Read(T * data,size_t count,int64_t waitTimeNanoSec)481 int SharedMemQueue<T>::Read(T *data, size_t count, int64_t waitTimeNanoSec)
482 {
483 if (meta_->GetType() != SmqType::SYNCED_SMQ) {
484 HDF_LOGE("unsynecd smq not support blocking read");
485 return HDF_ERR_NOT_SUPPORT;
486 }
487
488 if (ReadNonBlocking(data, count) == 0) {
489 return syncer_->Wake(SharedMemQueueSyncer::SYNC_WORD_WRITE);
490 }
491
492 int ret = -ENODATA;
493 auto startTime = GetNanoTime();
494 int64_t currentTime;
495 while (true) {
496 if (waitTimeNanoSec != 0) {
497 currentTime = GetNanoTime();
498 waitTimeNanoSec -= (currentTime - startTime);
499 startTime = currentTime;
500 if (waitTimeNanoSec <= 0) {
501 ret = ReadNonBlocking(data, count);
502 break;
503 }
504 }
505 ret = syncer_->Wait(SharedMemQueueSyncer::SYNC_WORD_READ, waitTimeNanoSec);
506 if (ret != 0 && ret != -ETIMEDOUT) {
507 break;
508 }
509
510 ret = ReadNonBlocking(data, count);
511 if (ret == 0) {
512 break;
513 }
514 }
515 if (ret == 0) {
516 ret = syncer_->Wake(SharedMemQueueSyncer::SYNC_WORD_WRITE);
517 } else {
518 HDF_LOGE("failed to read %{public}zu, ret=%{public}d", count, ret);
519 }
520
521 return ret;
522 }
523
524 template <typename T>
WriteNonBlocking(const T * data)525 int SharedMemQueue<T>::WriteNonBlocking(const T *data)
526 {
527 return WriteNonBlocking(data, 1);
528 }
529
530 template <typename T>
ReadNonBlocking(T * data)531 int SharedMemQueue<T>::ReadNonBlocking(T *data)
532 {
533 return ReadNonBlocking(data, 1);
534 }
535
536 template <typename T>
WriteNonBlocking(const T * data,size_t count)537 int SharedMemQueue<T>::WriteNonBlocking(const T *data, size_t count)
538 {
539 auto avalidWrite = GetAvalidWriteSize();
540 if (count > avalidWrite && meta_->GetType() == SmqType::SYNCED_SMQ) {
541 // synced smq can not overflow write
542 return -E2BIG;
543 }
544
545 auto wOffset = writeOffset_->load(std::memory_order_acquire);
546 auto rOffset = readOffset_->load(std::memory_order_acquire);
547 uint64_t newWriteOffset;
548 auto qCount = meta_->GetElementCount();
549 if (wOffset + count <= qCount) {
550 if (memcpy_s(queueBuffer_ + (wOffset * sizeof(T)), (qCount - wOffset) * sizeof(T),
551 data, count * sizeof(T)) != EOK) {
552 return HDF_FAILURE;
553 };
554 newWriteOffset = (wOffset + count) % qCount;
555 } else {
556 size_t firstPartSize = qCount - wOffset;
557 size_t secParcSize = count - firstPartSize;
558 if (memcpy_s(queueBuffer_ + (wOffset * sizeof(T)), (qCount - wOffset) * sizeof(T),
559 data, firstPartSize * sizeof(T)) != EOK) {
560 return HDF_FAILURE;
561 }
562 if (memcpy_s(queueBuffer_, qCount * sizeof(T), data + firstPartSize, secParcSize * sizeof(T)) != EOK) {
563 return HDF_FAILURE;
564 }
565 newWriteOffset = secParcSize;
566 }
567
568 writeOffset_->store(newWriteOffset, std::memory_order_release);
569 if (wOffset < rOffset && newWriteOffset >= rOffset) {
570 HDF_LOGW("warning:smp ring buffer overflow");
571 }
572 return 0;
573 }
574
575 template <typename T>
ReadNonBlocking(T * data,size_t count)576 int SharedMemQueue<T>::ReadNonBlocking(T *data, size_t count)
577 {
578 if (count == 0) {
579 return -EINVAL;
580 }
581
582 if (count > GetAvalidReadSize()) {
583 return -ENODATA;
584 }
585
586 auto qCount = meta_->GetElementCount();
587 auto rOffset = readOffset_->load(std::memory_order_acquire);
588 if (rOffset + count <= qCount) {
589 if (memcpy_s(data, count * sizeof(T), queueBuffer_ + (rOffset * sizeof(T)), count * sizeof(T)) != EOK) {
590 return HDF_FAILURE;
591 }
592 readOffset_->store((rOffset + count) % qCount, std::memory_order_release);
593 return 0;
594 }
595
596 size_t firstPartSize = qCount - rOffset;
597 size_t secPartSize = count - firstPartSize;
598
599 if (memcpy_s(data, count * sizeof(T), queueBuffer_ + (rOffset * sizeof(T)), firstPartSize * sizeof(T)) != EOK) {
600 return HDF_FAILURE;
601 }
602 if (memcpy_s(data + firstPartSize, (count - firstPartSize) * sizeof(T),
603 queueBuffer_, secPartSize * sizeof(T)) != EOK) {
604 return HDF_FAILURE;
605 };
606 readOffset_->store(secPartSize, std::memory_order_release);
607
608 return 0;
609 }
610
611 template <typename T>
GetAvalidWriteSize()612 size_t SharedMemQueue<T>::GetAvalidWriteSize()
613 {
614 return meta_->GetElementCount() - GetAvalidReadSize();
615 }
616
617 template <typename T>
GetAvalidReadSize()618 size_t SharedMemQueue<T>::GetAvalidReadSize()
619 {
620 auto wOffset = writeOffset_->load(std::memory_order_acquire);
621 auto rOffset = readOffset_->load(std::memory_order_acquire);
622 auto size = wOffset >= rOffset ? (wOffset - rOffset) : (wOffset + meta_->GetElementCount() - rOffset);
623 return size;
624 }
625
626 template <typename T>
GetSize()627 size_t SharedMemQueue<T>::GetSize()
628 {
629 return meta_->GetSize();
630 }
631
632 template <typename T>
GetMeta()633 std::shared_ptr<SharedMemQueueMeta<T>> SharedMemQueue<T>::GetMeta()
634 {
635 return meta_;
636 }
637 } // namespace Base
638 } // namespace HDI
639 } // namespace OHOS
640
641 #ifdef HDF_LOG_TAG
642 #undef HDF_LOG_TAG
643 #endif
644
645 #endif /* HDI_SHARED_MEM_QUEUEHDI_INF_H */
646