Mark3 Realtime Kernel
ksemaphore.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 ===========================================================================*/
22 #include "mark3.h"
23 
24 namespace Mark3
25 {
26 namespace
27 {
28  //---------------------------------------------------------------------------
38  void TimedSemaphore_Callback(Thread* pclOwner_, void* pvData_)
39  {
40  KERNEL_ASSERT(nullptr != pclOwner_);
41  KERNEL_ASSERT(nullptr != pvData_);
42 
43  auto* pclSemaphore = static_cast<Semaphore*>(pvData_);
44 
45  // Indicate that the semaphore has expired on the thread
46  pclOwner_->SetExpired(true);
47 
48  // Wake up the thread that was blocked on this semaphore.
49  pclSemaphore->WakeMe(pclOwner_);
50 
51  if (pclOwner_->GetCurPriority() >= Scheduler::GetCurrentThread()->GetCurPriority()) {
52  Thread::Yield();
53  }
54  }
55 } // anonymous namespace
56 
57 //---------------------------------------------------------------------------
59 {
60  // If there are any threads waiting on this object when it goes out
61  // of scope, set a kernel panic.
62  if (nullptr != m_clBlockList.GetHead()) {
64  }
65 }
66 
67 //---------------------------------------------------------------------------
68 void Semaphore::WakeMe(Thread* pclChosenOne_)
69 {
70  KERNEL_ASSERT(pclChosenOne_);
72 
73  // Remove from the semaphore waitlist and back to its ready list.
74  UnBlock(pclChosenOne_);
75 }
76 
77 //---------------------------------------------------------------------------
79 {
80  auto* pclChosenOne = m_clBlockList.HighestWaiter();
81  KERNEL_ASSERT(pclChosenOne);
82 
83  // Remove from the semaphore waitlist and back to its ready list.
84  UnBlock(pclChosenOne);
85 
86  // Call a task switch if higher or equal priority thread
87  if (pclChosenOne->GetCurPriority() >= Scheduler::GetCurrentThread()->GetCurPriority()) {
88  return 1;
89  }
90  return 0;
91 }
92 
93 //---------------------------------------------------------------------------
94 void Semaphore::Init(uint16_t u16InitVal_, uint16_t u16MaxVal_)
95 {
97 
98  // Copy the paramters into the object - set the maximum value for this
99  // semaphore to implement either binary or counting semaphores, and set
100  // the initial count. Clear the wait list for this object.
101  m_u16Value = u16InitVal_;
102  m_u16MaxValue = u16MaxVal_;
103 
104  SetInitialized();
105 }
106 
107 //---------------------------------------------------------------------------
109 {
111 
112  auto bThreadWake = false;
113  auto bBail = false;
114  // Increment the semaphore count - we can mess with threads so ensure this
115  // is in a critical section. We don't just disable the scheudler since
116  // we want to be able to do this from within an interrupt context as well.
117 
118  { // Begin critical section
119  const auto cs = CriticalGuard{};
120  // If nothing is waiting for the semaphore
121  if (nullptr == m_clBlockList.GetHead()) {
122  // Check so see if we've reached the maximum value in the semaphore
123  if (m_u16Value < m_u16MaxValue) {
124  // Increment the count value
125  m_u16Value++;
126  } else {
127  // Maximum value has been reached, bail out.
128  bBail = true;
129  }
130  } else {
131  // Otherwise, there are threads waiting for the semaphore to be
132  // posted, so wake the next one (highest priority goes first).
133  bThreadWake = (WakeNext() != 0u);
134  }
135  } // end critical section
136 
137  // If we weren't able to increment the semaphore count, fail out.
138  if (bBail) {
139  return false;
140  }
141 
142  // if bThreadWake was set, it means that a higher-priority thread was
143  // woken. Trigger a context switch to ensure that this thread gets
144  // to execute next.
145  if (bThreadWake) {
146  Thread::Yield();
147  }
148  return true;
149 }
150 
151 //---------------------------------------------------------------------------
152 bool Semaphore::Pend_i(uint32_t u32WaitTimeMS_)
153 {
155 
156  auto clSemTimer = Timer {};
157  auto bUseTimer = false;
158 
159  // Once again, messing with thread data - ensure
160  // we're doing all of these operations from within a thread-safe context.
161 
162  { // Begin critical section
163  const auto cs = CriticalGuard{};
164  // Check to see if we need to take any action based on the semaphore count
165  if (0 != m_u16Value) {
166  // The semaphore count is non-zero, we can just decrement the count
167  // and go along our merry way.
168  m_u16Value--;
169  } else {
170  // The semaphore count is zero - we need to block the current thread
171  // and wait until the semaphore is posted from elsewhere.
172  if (0u != u32WaitTimeMS_) {
173  g_pclCurrent->SetExpired(false);
174  clSemTimer.Init();
175  clSemTimer.Start(false, u32WaitTimeMS_, TimedSemaphore_Callback, this);
176  bUseTimer = true;
177  }
179 
180  // Switch Threads immediately
181  Thread::Yield();
182  }
183  } // End critical section
184 
185  if (bUseTimer) {
186  clSemTimer.Stop();
187  return (g_pclCurrent->GetExpired() == false);
188  }
189  return true;
190 }
191 
192 //---------------------------------------------------------------------------
193 // Redirect the untimed pend API to the timed pend, with a null timeout.
195 {
196  Pend_i(0);
197 }
198 
199 //---------------------------------------------------------------------------
200 bool Semaphore::Pend(uint32_t u32WaitTimeMS_)
201 {
202  return Pend_i(u32WaitTimeMS_);
203 }
204 
205 //---------------------------------------------------------------------------
207 {
209 
210  auto cs = CriticalGuard{};
211  return m_u16Value;
212 }
213 } // namespace Mark3
uint8_t WakeNext()
Wake the next thread waiting on the semaphore. Used internally.
Definition: ksemaphore.cpp:78
void UnBlock(Thread *pclThread_)
UnBlock Unblock a thread that is already blocked on this object, returning it to the "ready" state by...
Definition: blocking.cpp:56
#define KERNEL_ASSERT(x)
Definition: kerneldebug.h:36
void BlockPriority(Thread *pclThread_)
BlockPriority Same as Block(), but ensures that threads are added to the block-list in priority-order...
Definition: blocking.cpp:41
Definition: atomic.cpp:23
uint16_t m_u16MaxValue
Maximum count that can be held by this semaphore.
Definition: ksemaphore.h:140
Mark3::Thread * g_pclCurrent
Definition: scheduler.cpp:25
PORT_PRIO_TYPE GetCurPriority(void)
GetCurPriority Return the priority of the current thread.
Definition: thread.h:181
bool Pend_i(uint32_t u32WaitTimeMS_)
Pend_i.
Definition: ksemaphore.cpp:152
Single include file given to users of the Mark3 Kernel API.
The Thread Class. This object providing the fundamental thread control data structures and functions ...
Definition: thread.h:64
void WakeMe(Thread *pclChosenOne_)
Wake a thread blocked on the semaphore. This is an internal function used for implementing timed sema...
Definition: ksemaphore.cpp:68
void SetExpired(bool bExpired_)
SetExpired Set the status of the current blocking call on the thread.
Definition: thread.cpp:417
bool IsInitialized(void)
IsInitialized.
Definition: blocking.h:123
static void Yield(void)
Yield Yield the thread - this forces the system to call the scheduler and determine what thread shoul...
Definition: thread.cpp:304
#define PANIC_ACTIVE_SEMAPHORE_DESCOPED
Definition: paniccodes.h:29
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
Thread * HighestWaiter()
HighestWaiter Return a pointer to the highest-priority thread in the thread-list. ...
Definition: threadlist.cpp:128
bool GetExpired()
GetExpired Return the status of the most-recent blocking call on the thread.
Definition: thread.cpp:424
The Timer Class. This class provides kernel-managed timers, used to provide high-precision delays...
Definition: timer.h:68
uint16_t GetCount()
Return the current semaphore counter. This can be usedd by a thread to bypass blocking on a semaphore...
Definition: ksemaphore.cpp:206
bool Post()
Increment the semaphore count. If the semaphore count is zero at the time this is called...
Definition: ksemaphore.cpp:108
ThreadList m_clBlockList
Definition: blocking.h:133
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
void SetInitialized(void)
SetInitialized.
Definition: blocking.h:117
static Thread * GetCurrentThread()
GetCurrentThread Return the pointer to the currently-running thread.
Definition: scheduler.h:116
uint16_t m_u16Value
Current count held by the semaphore.
Definition: ksemaphore.h:139
static void Panic(uint16_t u16Cause_)
Panic Cause the kernel to enter its panic state.
Definition: kernel.cpp:70