Developer World
Spresense SDK Library v3.3.0-375c679
MsgQueBlock.h
Go to the documentation of this file.
1/****************************************************************************
2 * modules/include/memutils/message/MsgQueBlock.h
3 *
4 * Copyright 2018 Sony Semiconductor Solutions Corporation
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * 3. Neither the name of Sony Semiconductor Solutions Corporation nor
17 * the names of its contributors may be used to endorse or promote
18 * products derived from this software without specific prior written
19 * permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
41#ifndef MSG_QUE_BLOCK_H_INCLUDED
42#define MSG_QUE_BLOCK_H_INCLUDED
43
53#include "memutils/common_utils/common_types.h"
54#include "memutils/common_utils/common_assert.h"
55#include "memutils/os_utils/chateau_osal.h"
56#include "memutils/message/cache.h"
57#include "memutils/message/MsgQue.h"
58#include "memutils/message/MsgLog.h"
59#ifdef USE_MULTI_CORE
60#include "SpinLockManager.h" /* InterCpuLock::SpinLockId */
61#endif
62
63#include <semaphore.h>
64
65/*****************************************************************
66 * Message queue block class
67 *****************************************************************/
73public:
74#ifdef USE_MULTI_CORE
75 typedef InterCpuLock::SpinLockId SpinLockId;
76#else
77 typedef uint16_t SpinLockId;
78#endif
79
86 err_t recv(uint32_t ms, FAR MsgPacket **packet);
87
88 /* Discard message packet. */
89
90 err_t pop();
91
92 /* Get CPU-ID of queue owner (recipient). */
93
94 MsgCpuId getOwner() const { return m_owner; }
95
96 /* Get whether it is a shared queue or not. */
97
98 bool isShare() const { return m_spinlock != 0; }
99
100 /* Get message packet count. */
101
102 uint16_t getNumMsg(MsgPri pri) const;
103
104 /* Get the number of message packets that can be stored.
105 * (Unused queue returns 0)
106 */
107
108 uint16_t getRest(MsgPri pri) const;
109
110 /* Reset the information of message. */
111
112 void reset()
113 {
114 /* Reset semaphore with initialize value 0. */
115
116 Chateau_DeleteSemaphore(m_count_sem);
117 Chateau_CreateSemaphore(&m_count_sem, 0, 0);
118 }
119
120 /* Debug only. */
121
122 void dump() const;
123 void dumpQue(MsgPri pri) const { m_que[pri].dump(); }
124
125protected:
126 friend class MsgLib;
127
128 /* Static initialization only. */
129
130 MsgQueBlock(MsgQueId id, MsgCpuId owner, SpinLockId spinlock);
131
132 /* Dynamic initialization. */
133
134 err_t setup(drm_t n_drm, uint16_t n_size, uint16_t n_num,
135 drm_t h_drm, uint16_t h_size, uint16_t h_num);
136
137 /* Get queue element size. */
138
139 uint16_t getElemSize(MsgPri pri) const { return m_que[pri].elem_size(); }
140
141 /* Check if own CPU owned queue. */
142
143#ifdef USE_MULTI_CORE
144 bool isOwn() const { return GET_CPU_ID() == getOwner(); }
145#else
146 /* If it is not multi, always return true. */
147
148 bool isOwn() const { return true; }
149#endif
150 /* Find the size of the transmitted message. */
151
152 template<typename T>
153 static size_t getSendSize(const T& param, bool type_check);
154
155 /* Message sending process from task context */
156
157 template<typename T>
158 err_t send(MsgPri pri, MsgType type, MsgQueId reply, MsgFlags flags, const T& param);
159
160 /* Message transmission processing from ISR.
161 * (Only to non-shared queue owned by own CPU)
162 */
163
164 template<typename T>
165 err_t sendIsr(MsgPri pri, MsgType type, MsgQueId reply, const T& param);
166
167 /* Notify other CPU that sending message.
168 * (H/W dependent part. User implements for each CPU)
169 */
170
171 void notifySend(MsgCpuId cpu, MsgQueId dest);
172
173 /* Notify receipt of message.(from other CPU) */
174
175 void notifyRecv();
176
177 /* Insert a message packet header at the end of the queue
178 * and return that address.
179 */
180
181 MsgPacket* pushHeader(MsgPri pri, const MsgPacketHeader& header);
182
183 /* Lock/Unlock Queue. */
184
185 void lock();
186 void unlock();
187
188 struct Tally {
189 uint32_t total_pending;
190 uint16_t max_pending;
191 uint16_t max_queuing[NumMsgPri];
192 public:
193 Tally() { clear(); }
194 void clear() { memset(this, 0x00, sizeof(*this)); }
195 void dump() const {
196 printf("tally: total_pending=%ld, max_pending=%d, max_queuing=%d, %d\n",
197 total_pending, max_pending, max_queuing[MsgPriNormal], max_queuing[MsgPriHigh]);
198 }
199 };
200
201private:
202 const MsgQueId m_id; /* ID of message queue. */
203 bool m_initDone; /* Flag of Initialized. */
204 const MsgCpuId m_owner; /* CPU-ID of queue owner (recipient). */
205 Chateau_sem_handle_t m_count_sem; /* Count semaphore indicating
206 * the total number of messages.
207 */
208 const SpinLockId m_spinlock; /* ID of Spin lock.
209 * 0, no sharing between CPUs.
210 */
211 uint16_t m_pendingMsgCount; /* Number of messages waiting
212 * for parameter write.
213 */
214 MsgQue m_que[NumMsgPri]; /* Queue by priority. */
215 MsgQue* m_cur_que; /* Queue during message processing. */
216 Tally m_tally; /* Various measurement values. */
217 uint32_t m_context; /* Variable for locking. */
218}; /* class MsgQueBlock */
219
220/*****************************************************************
221 * Constructor (Static initialization only)
222 *****************************************************************/
223inline MsgQueBlock::MsgQueBlock(MsgQueId id, MsgCpuId owner, SpinLockId spinlock) :
224 m_id(id),
225 m_initDone(false),
226 m_owner(owner),
227// m_count_sem(0),
228 m_spinlock(spinlock),
229 m_pendingMsgCount(0),
230 m_cur_que(NULL),
231 m_tally()
232{
233 m_count_sem.semcount = 0;
234}
235
236/*****************************************************************
237 * Dynamic initialization
238 *****************************************************************/
239inline err_t MsgQueBlock::setup(drm_t n_drm, uint16_t n_size, uint16_t n_num,
240 drm_t h_drm, uint16_t h_size, uint16_t h_num)
241{
242 /* What has not been initialized yet. */
243
244 if (m_initDone != false)
245 {
246 return ERR_STS;
247 }
248
249 /* Set queue address, element length, number of elements. */
250
251 m_que[MsgPriNormal].init(n_drm, n_size, n_num);
252 if (h_drm != INVALID_DRM)
253 {
254 m_que[MsgPriHigh].init(h_drm, h_size, h_num);
255 }
256
257 /* Cache clear message area. */
258
259 if (isShare())
260 {
261 Dcache_clear(DRM_TO_CACHED_VA(n_drm), n_size * n_num);
262 if (h_drm != INVALID_DRM)
263 {
264 Dcache_clear(DRM_TO_CACHED_VA(h_drm), h_size * h_num);
265 }
266 }
267
268 /* Create semaphore with initial value 0. */
269
270 Chateau_CreateSemaphore(&m_count_sem, 0, 0);
271
272 m_initDone = true; /* Initialization end. */
273 Dcache_flush(this, sizeof(*this)); /* Do sync at the caller
274 * collectively.
275 */
276
277 return ERR_OK;
278}
279
280/*****************************************************************
281 * Get message packet count
282 *****************************************************************/
283inline uint16_t MsgQueBlock::getNumMsg(MsgPri pri) const
284{
285 D_ASSERT2(pri == MsgPriNormal || pri == MsgPriHigh, AssertParamLog(AssertIdBadParam, pri));
286
287 /* Dynamic information of other CPU owned queue must be read
288 * after cache clearing.
289 */
290
291 if (!isOwn()) {
292 Dcache_clear_sync(this, sizeof(*this));
293 }
294 return m_que[pri].size();
295}
296
297/*****************************************************************
298 * Get the number of message packets that can be stored
299 * (Unused queue returns 0)
300 *****************************************************************/
301inline uint16_t MsgQueBlock::getRest(MsgPri pri) const
302{
303 D_ASSERT2(pri == MsgPriNormal || pri == MsgPriHigh, AssertParamLog(AssertIdBadParam, pri));
304
305 /* Dynamic information of other CPU owned queue must be read
306 * after cache clearing.
307 */
308
309 if (!isOwn()) {
310 Dcache_clear_sync(this, sizeof(*this));
311 }
312 return m_que[pri].rest();
313}
314
315/*****************************************************************
316 * Get the size of the transmitted message
317 *****************************************************************/
318/* Send message size(With parameter). */
319
320template<typename T>
321size_t MsgQueBlock::getSendSize(const T& /* param */, bool type_check)
322{
323 return type_check ?
324 sizeof(MsgPacketHeader) + sizeof(TypeHolder<T>) :
325 sizeof(MsgPacketHeader) + MEMUTILS_ROUND_UP(sizeof(T), sizeof(int));
326}
327/* Send message size(No parameter). */
328
329template<>
330inline size_t MsgQueBlock::getSendSize<MsgNullParam>(const MsgNullParam& /* param */, bool /* type_check */)
331{
332 return sizeof(MsgPacketHeader);
333}
334
335/* Send message size(Address Range Parameter). */
336
337template<>
338inline size_t MsgQueBlock::getSendSize<MsgRangedParam>(const MsgRangedParam& param, bool /* type_check */)
339{
340 return sizeof(MsgPacketHeader) + param.getParamSize();
341}
342
343
344/*****************************************************************
345 * Class for acquiring message packet information
346 *****************************************************************/
347template<typename T>
349 static const bool typed_param = true;
350 static const bool null_param = false;
351};
352
353template<>
355 static const bool typed_param = false;
356 static const bool null_param = true;
357};
358
359template<>
361 static const bool typed_param = false;
362 static const bool null_param = false;
363};
364
365/*****************************************************************
366 * Message sending process from task context
367 *****************************************************************/
368template<typename T>
369err_t MsgQueBlock::send(MsgPri pri, MsgType type, MsgQueId reply, MsgFlags flags, const T& param)
370{
371 /* Check that the message fits in the element size of the queue */
372
373 bool type_check = MSG_PARAM_TYPE_MATCH_CHECK && MsgPacketInfo<T>::typed_param && isOwn();
374 size_t send_size = getSendSize(param, type_check);
375 if (send_size > getElemSize(pri))
376 {
377 return ERR_DATA_SIZE;
378 }
379
380 /* Put the message packet header in the queue
381 * and add the parameter after the interrupt is enabled.
382 */
383
384 lock(); /* In the shared queue,
385 * the cache of the queue management area is also cleared.
386 */
387
388 MsgPacket* msg = pushHeader(pri, MsgPacketHeader(type, reply, flags));
389 if (msg)
390 {
391 /* If it is a shared queue, cache flush of the packet header part.
392 * (The synchronization process is performed by the unlock process)
393 */
394
395 if (isShare())
396 {
397 Dcache_flush_clear(msg, MEMUTILS_ROUND_UP(sizeof(MsgPacketHeader), CACHE_BLOCK_SIZE));
398 }
399
400 /* Since most ITRON APIs can not be executed in the interrupt
401 * disabled state, interrupts are permitted here.
402 */
403
404 unlock(); /* In the shared queue, the cache flush
405 * of the queue management area is also performed.
406 */
407
408 /* Add parameter. (When there is no parameter, empty function) */
409
410 msg->setParam(param, type_check); /* ITRON's API executable
411 * with copy constructor.
412 */
413
414 DUMP_MSG_SEQ_LOCK(MsgSeqLog('s', m_id, pri, m_que[pri].size(), msg));
415
416 /* When the parameter part is added to the shared queue,
417 * the message packet region is cached flash.
418 */
419
420
421 if (!MsgPacketInfo<T>::null_param && isShare())
422 {
423 Dcache_flush_clear_sync(msg, MEMUTILS_ROUND_UP(send_size, CACHE_BLOCK_SIZE));
424 }
425
426 if (isShare() == false || isOwn())
427 {
428 /* Update total message count */
429
430 Chateau_SignalSemaphoreTask(m_count_sem);
431 } else {
432 /* Request to update the total number of messages
433 * by inter-CPU communication.
434 */
435
436 notifySend(m_owner, m_id);
437 }
438 }
439 else
440 {
441 /* In the shared queue, the cache flush of the queue management area
442 * is also performed.
443 */
444
445 unlock();
446 }
447 return (msg) ? ERR_OK : ERR_QUE_FULL;
448}
449
450/*****************************************************************
451 * Message transmission processing from ISR
452 * (Only to non-shared queue owned by own CPU)
453 *****************************************************************/
454template<typename T>
455err_t MsgQueBlock::sendIsr(MsgPri pri, MsgType type, MsgQueId reply, const T& param)
456{
457 /* Transmission from the ISR to the shared queue is prohibited. */
458
459 D_ASSERT2(isShare() == false, AssertParamLog(AssertIdBadMsgQueState, m_id));
460
461 /* Check that the message fits in the element size of the queue. */
462
463 bool type_check = MSG_PARAM_TYPE_MATCH_CHECK && MsgPacketInfo<T>::typed_param;
464 if (getSendSize(param, type_check) > getElemSize(pri))
465 {
466 return ERR_DATA_SIZE;
467 }
468
469 /* Queue the message packet header. */
470
471 MsgPacket* msg = pushHeader(pri, MsgPacketHeader(type, reply, MsgPacket::MsgFlagNull));
472 if (msg)
473 {
474 /* Add parameter. (When there is no parameter, empty function) */
475
476 /* ITRON API can not be executed with copy constructor. */
477
478 msg->setParam(param, type_check);
479
480 /* Update total message count. */
481
482 Chateau_SignalSemaphoreIsr(m_count_sem);
483 DUMP_MSG_SEQ(MsgSeqLog('i', m_id, pri, m_que[pri].size(), msg));
484 }
485 return (msg) ? ERR_OK : ERR_QUE_FULL;
486}
487
488/*****************************************************************
489 * Insert a message packet header at the end of the queue
490 * and return that address
491 *****************************************************************/
492inline MsgPacket* MsgQueBlock::pushHeader(MsgPri pri, const MsgPacketHeader& header)
493{
494 D_ASSERT2(pri == MsgPriNormal || pri == MsgPriHigh, AssertParamLog(AssertIdBadParam, pri));
495 /* Check unused high priority queue specification. */
496
497 D_ASSERT2(m_que[pri].is_init(), AssertParamLog(AssertIdBadMsgQueState, m_id, pri));
498#ifdef USE_MULTI_CORE
499 D_ASSERT2(isOwn() || (isShare() && InterCpuLock::SpinLockManager::isMember(m_spinlock)),
500 AssertParamLog(AssertIdBadParam, m_id));
501#else
502 D_ASSERT2(isOwn(), AssertParamLog(AssertIdBadParam, m_id));
503#endif
504
505 MsgPacket* msg = m_que[pri].pushHeader(header);
506 if (msg) {
507 if (m_que[pri].size() > m_tally.max_queuing[pri]) {
508 m_tally.max_queuing[pri] = m_que[pri].size();
509 /* Leave peak value, message type etc in the log. */
510
511 DUMP_MSG_PEAK(m_id, pri, MsgPeakLog(m_tally.max_queuing[pri], msg, m_que[pri].frontMsg()));
512 }
513 }
514 return msg;
515}
516
517/*****************************************************************
518 * Notify receipt of message(from other CPU)
519 *****************************************************************/
520inline void MsgQueBlock::notifyRecv() {
521 Chateau_SignalSemaphoreIsr(m_count_sem);
522}
523
524/*****************************************************************
525 * Receive message packet
526 *****************************************************************/
527inline err_t MsgQueBlock::recv(uint32_t ms, FAR MsgPacket **packet)
528{
529 bool result;
530
531 /* Check if own CPU is owned, and
532 * check that the previously received packet is discarded.
533 */
534
535 if (!(isOwn() && m_cur_que == NULL))
536 {
537 return ERR_QUE_FULL;
538 }
539
540retry: /* Wait to receive. */
541
542 if (ms != TIME_FOREVER)
543 {
544 timespec tm;
545 tm.tv_sec = ms / 1000;
546 tm.tv_nsec = ms % 1000;
547 result = Chateau_TimedWaitSemaphore(m_count_sem, tm);
548 }
549 else
550 {
551 result = Chateau_WaitSemaphore(m_count_sem);
552 }
553
554 if (result == false)
555 {
556 return ERR_SEM_TAKE;
557 }
558
559 /* If it is a shared queue, clear the lock & queue control area cache. */
560
561 if (isShare())
562 {
563 lock();
564 }
565
566 /* Get a pointer to the message packet of the queue
567 * with the highest priority.
568 */
569
570 MsgPri pri = (m_que[MsgPriHigh].size()) ? MsgPriHigh : MsgPriNormal;
571 MsgQue* que = &m_que[pri];
572 MsgPacket* msg = que->frontMsg();
573
574 if (msg == NULL)
575 {
576 return ERR_QUE_EMP;
577 }
578
579 /* If it waits for parameter writing,
580 * it stores it and returns to waiting for message again.
581 */
582
583 if (msg->getFlags() & MsgPacket::MsgFlagWaitParam)
584 {
585 /* When recv is done with nonshared queue and multiple tasks,
586 * inconsistency may occur with the following increment.
587 * However, simultaneous recv to the same queue due to multiple tasks
588 * is not supported because it is outside the support range
589 * by specification.
590 */
591
592 /* Record the number of times the semaphore count was consumed. */
593
594 ++m_pendingMsgCount;
595 m_tally.max_pending = MAX(m_pendingMsgCount, m_tally.max_pending);
596 ++m_tally.total_pending;
597
598 if (isShare())
599 {
600#ifdef USE_MULTI_CORE
601 /* In the case of a message from another CPU,
602 * it is necessary to clear the area cache.
603 */
604
605 if (msg->getSrcCpu() != GET_CPU_ID())
606 {
607 Dcache_clear(msg, que->elem_size());
608 }
609#endif
610 /* Cache flushing and unlocking queue management area. */
611
612 unlock();
613 }
614 goto retry;
615 }
616
617 /* Update queue management area before cache flush. */
618
619 uint16_t pending = m_pendingMsgCount;
620 m_pendingMsgCount = 0;
621 m_cur_que = que;
622
623 /* If shared queue, cache queue management area cache flash & unlock. */
624
625 if (isShare())
626 {
627 unlock();
628 }
629
630 /* Recover the semaphore count that was consumed while waiting
631 * for parameter write.
632 */
633
634 while (pending--)
635 {
636 Chateau_SignalSemaphoreTask(m_count_sem);
637 }
638
639 DUMP_MSG_SEQ_LOCK(MsgSeqLog('r', m_id, pri, m_que[pri].size(), msg));
640
641 *packet = msg;
642
643 return ERR_OK;
644}
645
646/*****************************************************************
647 * Discard message packet
648 *****************************************************************/
649inline err_t MsgQueBlock::pop()
650{
651 /* Check if own CPU is owned, and check Packet Received */
652
653 if (!(isOwn() && m_cur_que != NULL))
654 {
655 return ERR_STS;
656 }
657
658 /* Check that the parameter length of the message packet
659 * to be discarded is 0.
660 */
661
662 MsgPacket* msg = m_cur_que->frontMsg();
663
664 if (msg->getParamSize() != 0)
665 {
666 return ERR_MEM_BUSY;
667 }
668
669 lock();
670
671 /* Discard the packet from the queue. */
672
673 if (m_cur_que->pop() == false)
674 {
675 return ERR_QUE_FREE;
676 }
677
678 /* In case of shared queue, clear cache of discarded packet area.
679 * (The synchronization process is performed by the unlock process)
680 * It is indispensable to prevent the value from remaining
681 * at the next recv and to prevent write back of the dirty cache.
682 */
683
684#if MSG_FILL_VALUE_AFTER_POP == 0x00
685 if (isShare())
686 {
687 Dcache_clear(msg, m_cur_que->elem_size());
688 }
689#else
690 if (isShare())
691 {
692 Dcache_flush_clear(msg, m_cur_que->elem_size());
693 } /* flush is the fill value write after pop. */
694#endif
695 m_cur_que = NULL; /* Make the packet unreceived state. */
696 unlock();
697
698 return ERR_OK;
699}
700
701/*****************************************************************
702 * Lock queue
703 *****************************************************************/
704inline void MsgQueBlock::lock() {
705 if (isShare() == false) { /* Check local (nonshared) queue. */
706 Chateau_LockInterrupt(&m_context);
707 } else {
708#ifdef USE_MULTI_CORE
709 InterCpuLock::SpinLockManager::acquire(m_spinlock);
710 /* Clear cache of queue management area (self instance). */
711
712 Dcache_clear_sync(this, sizeof(*this));
713#else
714 F_ASSERT(0);
715#endif
716 }
717}
718
719/*****************************************************************
720 * Unlock queue
721 *****************************************************************/
722inline void MsgQueBlock::unlock() {
723 if (isShare() == false) { /* Check local (nonshared) queue. */
724 Chateau_UnlockInterrupt(&m_context);
725 } else {
726#ifdef USE_MULTI_CORE
727 /* Cache flash & clear of queue management area (self instance). */
728
729 Dcache_flush_clear_sync(this, sizeof(*this));
730 InterCpuLock::SpinLockManager::release(m_spinlock);
731#else
732 F_ASSERT(0);
733#endif
734 }
735}
736
737/*****************************************************************
738 * Dump display of message cube lock
739 * Because execution of this function causes the message packet
740 * to appear in the cache, caution is required because there
741 * is a possibility that message reception from other CPU
742 * may be affected.
743 *****************************************************************/
744inline void MsgQueBlock::dump() const
745{
746 printf("ID:%d, init=%d, owner=%d, spinlock=%d, count_sem=%d, cur_pending=%d, cur_que=%p\n",
747 m_id, m_initDone, m_owner, m_spinlock, m_count_sem.semcount, m_pendingMsgCount, m_cur_que);
748 m_tally.dump();
749
750 printf("Normal priority queue=%p\n", &m_que[MsgPriNormal]);
751 m_que[MsgPriNormal].dump();
752
753 if (m_que[MsgPriHigh].capacity()) {
754 printf("High priority queue=%p\n", &m_que[MsgPriHigh]);
755 m_que[MsgPriHigh].dump();
756 }
757}
758
767#endif /* MSG_QUE_BLOCK_H_INCLUDED */
Definition: cpp_util.h:45
Message Library Class.
Definition: Message.h:88
Definition: MsgPacket.h:119
Definition: MsgPacket.h:75
Definition: MsgPacket.h:142
Message Queue Class.
Definition: MsgQueBlock.h:72
Definition: MsgQue.h:52
Definition: MsgPacket.h:124
Definition: type_holder.h:122
err_t recv(uint32_t ms, FAR MsgPacket **packet)
Definition: MsgQueBlock.h:527
Definition: AssertInfo.h:210
Definition: MsgLog.h:42
Definition: MsgQueBlock.h:348
Definition: MsgLog.h:100
Definition: MsgQueBlock.h:188