Mark3 Realtime Kernel
mailbox.cpp
Go to the documentation of this file.
1 /*===========================================================================
2  _____ _____ _____ _____
3  ___| _|__ __|_ |__ __|__ |__ __| __ |__ ______
4 | \ / | || \ || | || |/ / ||___ |
5 | \/ | || \ || \ || \ ||___ |
6 |__/\__/|__|_||__|\__\ __||__|\__\ __||__|\__\ __||______|
7  |_____| |_____| |_____| |_____|
8 
9 --[Mark3 Realtime Platform]--------------------------------------------------
10 
11 Copyright (c) 2012 - 2019 m0slevin, all rights reserved.
12 See license.txt for more information
13 ===========================================================================*/
21 #include "mark3.h"
22 namespace Mark3
23 {
24 //---------------------------------------------------------------------------
26 {
27  // If the mailbox isn't empty on destruction, kernel panic.
28  if (m_u16Free != m_u16Count) {
30  }
31 }
32 
33 //---------------------------------------------------------------------------
34 void Mailbox::Init(void* pvBuffer_, uint16_t u16BufferSize_, uint16_t u16ElementSize_)
35 {
36  KERNEL_ASSERT(u16BufferSize_);
37  KERNEL_ASSERT(u16ElementSize_);
38  KERNEL_ASSERT(nullptr != pvBuffer_);
39 
40  m_pvBuffer = pvBuffer_;
41  m_u16ElementSize = u16ElementSize_;
42 
43  m_u16Count = (u16BufferSize_ / u16ElementSize_);
45 
46  m_u16Head = 0;
47  m_u16Tail = 0;
48 
49  // We use the counting semaphore to implement blocking - with one element
50  // in the mailbox corresponding to a post/pend operation in the semaphore.
52 
53  // Binary semaphore is used to track any threads that are blocked on a
54  // "send" due to lack of free slots.
55  m_clSendSem.Init(0, 1);
56 }
57 
58 //---------------------------------------------------------------------------
59 Mailbox* Mailbox::Init(uint16_t u16BufferSize_, uint16_t u16ElementSize_)
60 {
61  KERNEL_ASSERT(u16BufferSize_);
62  KERNEL_ASSERT(u16ElementSize_);
63 
64  auto* pclNew = AutoAlloc::NewObject<Mailbox, AutoAllocType::MailBox>();
65  auto* pvBuffer = AutoAlloc::NewRawData(u16BufferSize_);
66 
67  KERNEL_ASSERT(nullptr != pclNew);
68  KERNEL_ASSERT(nullptr != pvBuffer);
69 
70  if (!pclNew) {
71  return nullptr;
72  }
73  if (!pvBuffer) {
74  AutoAlloc::DestroyObject<Mailbox, AutoAllocType::MailBox>(pclNew);
75  return nullptr;
76  }
77 
78  pclNew->Init(pvBuffer, u16BufferSize_, u16ElementSize_);
79  return pclNew;
80 }
81 
82 //---------------------------------------------------------------------------
83 void Mailbox::Receive(void* pvData_)
84 {
85  KERNEL_ASSERT(nullptr != pvData_);
86  Receive_i(pvData_, false, 0);
87 }
88 
89 //---------------------------------------------------------------------------
90 bool Mailbox::Receive(void* pvData_, uint32_t u32TimeoutMS_)
91 {
92  KERNEL_ASSERT(nullptr != pvData_);
93  return Receive_i(pvData_, false, u32TimeoutMS_);
94 }
95 
96 //---------------------------------------------------------------------------
97 void Mailbox::ReceiveTail(void* pvData_)
98 {
99  KERNEL_ASSERT(nullptr != pvData_);
100  Receive_i(pvData_, true, 0);
101 }
102 
103 //---------------------------------------------------------------------------
104 bool Mailbox::ReceiveTail(void* pvData_, uint32_t u32TimeoutMS_)
105 {
106  KERNEL_ASSERT(nullptr != pvData_);
107  return Receive_i(pvData_, true, u32TimeoutMS_);
108 }
109 
110 //---------------------------------------------------------------------------
111 bool Mailbox::Send(void* pvData_)
112 {
113  KERNEL_ASSERT(nullptr != pvData_);
114  return Send_i(pvData_, false, 0);
115 }
116 
117 //---------------------------------------------------------------------------
118 bool Mailbox::SendTail(void* pvData_)
119 {
120  KERNEL_ASSERT(nullptr != pvData_);
121  return Send_i(pvData_, true, 0);
122 }
123 
124 //---------------------------------------------------------------------------
125 bool Mailbox::Send(void* pvData_, uint32_t u32TimeoutMS_)
126 {
127  KERNEL_ASSERT(nullptr != pvData_);
128  return Send_i(pvData_, false, u32TimeoutMS_);
129 }
130 
131 //---------------------------------------------------------------------------
132 bool Mailbox::SendTail(void* pvData_, uint32_t u32TimeoutMS_)
133 {
134  KERNEL_ASSERT(nullptr != pvData_);
135  return Send_i(pvData_, true, u32TimeoutMS_);
136 }
137 
138 //---------------------------------------------------------------------------
139 bool Mailbox::Send_i(const void* pvData_, bool bTail_, uint32_t u32TimeoutMS_)
140 {
141  KERNEL_ASSERT(nullptr != pvData_);
142 
143  void* pvDst = nullptr;
144 
145  auto bRet = false;
146  auto bSchedState = Scheduler::SetScheduler(false);
147  auto bBlock = false;
148  auto bDone = false;
149 
150  while (!bDone) {
151  // Try to claim a slot first before resorting to blocking.
152  if (bBlock) {
153  bDone = true;
154  Scheduler::SetScheduler(bSchedState);
155  m_clSendSem.Pend(u32TimeoutMS_);
157  }
158 
159  { // Begin critical section
160  const auto cs = CriticalGuard{};
161  // Ensure we have a free slot before we attempt to write data
162  if (0u != m_u16Free) {
163  m_u16Free--;
164 
165  if (bTail_) {
166  pvDst = GetTailPointer();
168  } else {
169  MoveHeadForward();
170  pvDst = GetHeadPointer();
171  }
172  bRet = true;
173  bDone = true;
174  } else if (0u != u32TimeoutMS_) {
175  bBlock = true;
176  } else {
177  bDone = true;
178  }
179  } // End critical section
180  }
181 
182  // Copy data to the claimed slot, and post the counting semaphore
183  if (bRet) {
184  CopyData(pvData_, pvDst, m_u16ElementSize);
185  }
186 
187  Scheduler::SetScheduler(bSchedState);
188 
189  if (bRet) {
190  m_clRecvSem.Post();
191  }
192 
193  return bRet;
194 }
195 
196 //---------------------------------------------------------------------------
197 bool Mailbox::Receive_i(void* pvData_, bool bTail_, uint32_t u32WaitTimeMS_)
198 {
199  KERNEL_ASSERT(nullptr != pvData_);
200  auto* pvSrc = (const void*){};
201 
202  if (!m_clRecvSem.Pend(u32WaitTimeMS_)) {
203  // Failed to get the notification from the counting semaphore in the
204  // time allotted. Bail.
205  return false;
206  }
207 
208  // Disable the scheduler while we do this -- this ensures we don't have
209  // multiple concurrent readers off the same queue, which could be problematic
210  // if multiple writes occur during reads, etc.
211  auto bSchedState = Scheduler::SetScheduler(false);
212 
213  // Update the head/tail indexes, and get the associated data pointer for
214  // the read operation.
215 
216  { // Begin critical section
217  const auto cs = CriticalGuard{};
218  m_u16Free++;
219  if (bTail_) {
220  MoveTailForward();
221  pvSrc = GetTailPointer();
222  } else {
223  pvSrc = GetHeadPointer();
225  }
226  } // end critical section
227 
228  KERNEL_ASSERT(pvSrc);
229  CopyData(pvSrc, pvData_, m_u16ElementSize);
230 
231  Scheduler::SetScheduler(bSchedState);
232 
233  // Unblock a thread waiting for a free slot to send to
234  m_clSendSem.Post();
235 
236  return true;
237 }
238 } // namespace Mark3
uint16_t m_u16Count
Count of items in the mailbox.
Definition: mailbox.h:296
void * GetHeadPointer(void)
GetHeadPointer Return a pointer to the current head of the mailbox&#39;s internal circular buffer...
Definition: mailbox.h:187
void * GetTailPointer(void)
GetTailPointer Return a pointer to the current tail of the mailbox&#39;s internal circular buffer...
Definition: mailbox.h:201
void MoveHeadForward(void)
MoveHeadForward Move the head index forward one element.
Definition: mailbox.h:239
void Init(void *pvBuffer_, uint16_t u16BufferSize_, uint16_t u16ElementSize_)
Init Initialize the mailbox object prior to its use. This must be called before any calls can be made...
Definition: mailbox.cpp:34
void CopyData(const void *src_, void *dst_, uint16_t len_)
CopyData Perform a direct byte-copy from a source to a destination object.
Definition: mailbox.h:216
#define KERNEL_ASSERT(x)
Definition: kerneldebug.h:36
void MoveHeadBackward(void)
MoveHeadBackward Move the head index backward one element.
Definition: mailbox.h:263
void Receive(void *pvData_)
Receive Read one envelope from the head of the mailbox. If the mailbox is currently empty...
Definition: mailbox.cpp:83
bool Receive_i(void *pvData_, bool bTail_, uint32_t u32WaitTimeMS_)
Receive_i Internal method which implements all Read() methods in the class.
Definition: mailbox.cpp:197
Semaphore m_clSendSem
Binary semaphore for send-blocked threads.
Definition: mailbox.h:303
Definition: atomic.cpp:23
bool SendTail(void *pvData_)
SendTail Send an envelope to the mailbox. This safely copies the data contents of the datastructure t...
Definition: mailbox.cpp:118
void MoveTailForward(void)
MoveTailForward Move the tail index forward one element.
Definition: mailbox.h:227
Single include file given to users of the Mark3 Kernel API.
uint16_t m_u16Tail
Current tail index.
Definition: mailbox.h:294
volatile uint16_t m_u16Free
Current number of free slots in the mailbox.
Definition: mailbox.h:297
Semaphore m_clRecvSem
Counting semaphore used to synchronize threads on the object.
Definition: mailbox.h:302
uint16_t m_u16Head
Current head index.
Definition: mailbox.h:293
void MoveTailBackward(void)
MoveTailBackward Move the tail index backward one element.
Definition: mailbox.h:251
The CriticalGuard class. This class provides an implemention of RAII for critical sections...
Definition: criticalguard.h:38
void Pend()
Decrement the semaphore count. If the count is zero, the calling Thread will block until the semaphor...
Definition: ksemaphore.cpp:194
The Mailbox class. This class implements an IPC mechnism based on sending/receiving envelopes contain...
Definition: mailbox.h:35
static void * NewRawData(size_t sSize_)
NewRawData Attempt to allocate a blob of raw data from the heap.
Definition: autoalloc.cpp:100
bool Post()
Increment the semaphore count. If the semaphore count is zero at the time this is called...
Definition: ksemaphore.cpp:108
bool Send_i(const void *pvData_, bool bTail_, uint32_t u32TimeoutMS_)
Send_i Internal method which implements all Send() methods in the class.
Definition: mailbox.cpp:139
#define PANIC_ACTIVE_MAILBOX_DESCOPED
Definition: paniccodes.h:33
void Init(uint16_t u16InitVal_, uint16_t u16MaxVal_)
Initialize a semaphore before use. Must be called before attempting post/pend operations on the objec...
Definition: ksemaphore.cpp:94
const void * m_pvBuffer
Pointer to the data-buffer managed by this mailbox.
Definition: mailbox.h:300
bool Send(void *pvData_)
Send Send an envelope to the mailbox. This safely copies the data contents of the datastructure to th...
Definition: mailbox.cpp:111
uint16_t m_u16ElementSize
Size of the objects tracked in this mailbox.
Definition: mailbox.h:299
void ReceiveTail(void *pvData_)
ReceiveTail Read one envelope from the tail of the mailbox. If the mailbox is currently empty...
Definition: mailbox.cpp:97
static void Panic(uint16_t u16Cause_)
Panic Cause the kernel to enter its panic state.
Definition: kernel.cpp:70
static bool SetScheduler(bool bEnable_)
SetScheduler Set the active state of the scheduler. When the scheduler is disabled, the next thread is never set; the currently running thread will run forever until the scheduler is enabled again. Care must be taken to ensure that we don&#39;t end up trying to block while the scheduler is disabled, otherwise the system ends up in an unusable state.
Definition: scheduler.cpp:75