Mark3 Realtime Kernel
mutex.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 ===========================================================================*/
20 #include "mark3.h"
21 namespace Mark3
22 {
23 namespace
24 {
25  //---------------------------------------------------------------------------
35  void TimedMutex_Callback(Thread* pclOwner_, void* pvData_)
36  {
37  KERNEL_ASSERT(nullptr != pclOwner_);
38  KERNEL_ASSERT(nullptr != pvData_);
39 
40  auto* pclMutex = static_cast<Mutex*>(pvData_);
41 
42  // Indicate that the semaphore has expired on the thread
43  pclOwner_->SetExpired(true);
44 
45  // Wake up the thread that was blocked on this semaphore.
46  pclMutex->WakeMe(pclOwner_);
47 
48  if (pclOwner_->GetCurPriority() >= Scheduler::GetCurrentThread()->GetCurPriority()) {
49  Thread::Yield();
50  }
51  }
52 } // anonymous namespace
53 
54 //---------------------------------------------------------------------------
56 {
57  // If there are any threads waiting on this object when it goes out
58  // of scope, set a kernel panic.
59  if (nullptr != m_clBlockList.GetHead()) {
61  }
62 }
63 
64 //---------------------------------------------------------------------------
65 void Mutex::WakeMe(Thread* pclOwner_)
66 {
67  KERNEL_ASSERT(nullptr != pclOwner_);
68  // Remove from the semaphore waitlist and back to its ready list.
69  UnBlock(pclOwner_);
70 }
71 
72 //---------------------------------------------------------------------------
73 uint8_t Mutex::WakeNext()
74 {
75  // Get the highest priority waiter thread
76  auto* pclChosenOne = m_clBlockList.HighestWaiter();
77  KERNEL_ASSERT(pclChosenOne);
78 
79  // Unblock the thread
80  UnBlock(pclChosenOne);
81 
82  // The chosen one now owns the mutex
83  m_pclOwner = pclChosenOne;
84 
85  // Signal a context switch if it's a greater than or equal to the current priority
86  if (pclChosenOne->GetCurPriority() >= Scheduler::GetCurrentThread()->GetCurPriority()) {
87  return 1;
88  }
89  return 0;
90 }
91 
92 //---------------------------------------------------------------------------
93 void Mutex::Init(bool bRecursive_)
94 {
95  // Cannot re-init a mutex which has threads blocked on it
97 
98  // Reset the data in the mutex
99  m_bReady = true; // The mutex is free.
100  m_uMaxPri = 0; // Set the maximum priority inheritence state
101  m_pclOwner = nullptr; // Clear the mutex owner
102  m_u8Recurse = 0; // Reset recurse count
103  m_bRecursive = bRecursive_;
104  SetInitialized();
105 }
106 
107 //---------------------------------------------------------------------------
108 bool Mutex::Claim_i(uint32_t u32WaitTimeMS_)
109 {
111 
112  auto clTimer = Timer {};
113  auto bUseTimer = false;
114 
115  // Disable the scheduler while claiming the mutex - we're dealing with all
116  // sorts of private thread data, can't have a thread switch while messing
117  // with internal data structures.
119 
120  // Check to see if the mutex is claimed or not
121  if (false != m_bReady) {
122  // Mutex isn't claimed, claim it.
123  m_bReady = false;
124  m_u8Recurse = 0;
127 
129  return true;
130  }
131 
132  // If the mutex is already claimed, check to see if this is the owner thread,
133  // since we allow the mutex to be claimed recursively.
134  if (m_bRecursive && (g_pclCurrent == m_pclOwner)) {
135  // Ensure that we haven't exceeded the maximum recursive-lock count
136  KERNEL_ASSERT((m_u8Recurse < 255));
137  m_u8Recurse++;
138 
139  // Increment the lock count and bail
141  return true;
142  }
143 
144  // The mutex is claimed already - we have to block now. Move the
145  // current thread to the list of threads waiting on the mutex.
146  if (0u != u32WaitTimeMS_) {
147  g_pclCurrent->SetExpired(false);
148  clTimer.Init();
149  clTimer.Start(false, u32WaitTimeMS_, TimedMutex_Callback, this);
150  bUseTimer = true;
151  }
153 
154  // Check if priority inheritence is necessary. We do this in order
155  // to ensure that we don't end up with priority inversions in case
156  // multiple threads are waiting on the same resource.
157  if (m_uMaxPri <= g_pclCurrent->GetPriority()) {
159 
160  auto* pclTemp = m_clBlockList.GetHead();
161  while (nullptr != pclTemp) {
162  pclTemp->InheritPriority(m_uMaxPri);
163  if (m_clBlockList.GetTail() == pclTemp) {
164  break;
165  }
166  pclTemp = pclTemp->GetNext();
167  }
169  }
170 
171  // Done with thread data -reenable the scheduler
173 
174  // Switch threads if this thread acquired the mutex
175  Thread::Yield();
176 
177  if (bUseTimer) {
178  clTimer.Stop();
179  return (false == g_pclCurrent->GetExpired());
180  }
181  return true;
182 }
183 
184 //---------------------------------------------------------------------------
185 void Mutex::Claim(void)
186 {
187  Claim_i(0);
188 }
189 
190 //---------------------------------------------------------------------------
191 bool Mutex::Claim(uint32_t u32WaitTimeMS_)
192 {
193  return Claim_i(u32WaitTimeMS_);
194 }
195 
196 //---------------------------------------------------------------------------
198 {
200 
201  auto bSchedule = false;
202 
203  // Disable the scheduler while we deal with internal data structures.
205 
206  // This thread had better be the one that owns the mutex currently...
208 
209  // If the owner had claimed the lock multiple times, decrease the lock
210  // count and return immediately.
211  if (m_bRecursive && (0u != m_u8Recurse)) {
212  m_u8Recurse--;
214  return;
215  }
216 
217  // Restore the thread's original priority
220 
221  // In this case, we want to reschedule
222  bSchedule = true;
223  }
224 
225  // No threads are waiting on this semaphore?
226  if (nullptr == m_clBlockList.GetHead()) {
227  // Re-initialize the mutex to its default values
228  m_bReady = true;
229  m_uMaxPri = 0;
230  m_pclOwner = nullptr;
231  } else {
232  // Wake the highest priority Thread pending on the mutex
233  if (0u != WakeNext()) {
234  // Switch threads if it's higher or equal priority than the current thread
235  bSchedule = true;
236  }
237  }
238 
239  // Must enable the scheduler again in order to switch threads.
241  if (bSchedule) {
242  // Switch threads if a higher-priority thread was woken
243  Thread::Yield();
244  }
245 }
246 } // namespace Mark3
Thread * m_pclOwner
Pointer to the thread that owns the mutex (when claimed)
Definition: mutex.h:162
PORT_PRIO_TYPE m_uMaxPri
Maximum priority of thread in queue, used for priority inheritence.
Definition: mutex.h:161
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
uint8_t WakeNext()
WakeNext.
Definition: mutex.cpp:73
#define PANIC_ACTIVE_MUTEX_DESCOPED
Definition: paniccodes.h:30
void Release()
Release Release the mutex. When the mutex is released, another object can enter the mutex-protected r...
Definition: mutex.cpp:197
#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
void Claim()
Claim Claim the mutex. When the mutex is claimed, no other thread can claim a region protected by the...
Definition: mutex.cpp:185
uint8_t m_u8Recurse
The recursive lock-count when a mutex is claimed multiple times by the same owner.
Definition: mutex.h:158
void InheritPriority(PORT_PRIO_TYPE uXPriority_)
InheritPriority Allow the thread to run at a different priority level (temporarily) for the purpose o...
Definition: thread.cpp:382
Definition: atomic.cpp:23
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 Claim_i(uint32_t u32WaitTimeMS_)
Claim_i Abstracts out timed/non-timed mutex claim operations.
Definition: mutex.cpp:108
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 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
Thread * HighestWaiter()
HighestWaiter Return a pointer to the highest-priority thread in the thread-list. ...
Definition: threadlist.cpp:128
void Init(bool bRecursive_=true)
Init Initialize a mutex object for use - must call this function before using the object...
Definition: mutex.cpp:93
bool GetExpired()
GetExpired Return the status of the most-recent blocking call on the thread.
Definition: thread.cpp:424
void WakeMe(Thread *pclOwner_)
WakeMe Wake a thread blocked on the mutex. This is an internal function used for implementing timed m...
Definition: mutex.cpp:65
The Timer Class. This class provides kernel-managed timers, used to provide high-precision delays...
Definition: timer.h:68
ThreadList m_clBlockList
Definition: blocking.h:133
bool m_bRecursive
Whether or not the lock is recursive.
Definition: mutex.h:160
void SetInitialized(void)
SetInitialized.
Definition: blocking.h:117
PORT_PRIO_TYPE GetPriority(void)
GetPriority Return the priority of the current thread.
Definition: thread.h:174
static Thread * GetCurrentThread()
GetCurrentThread Return the pointer to the currently-running thread.
Definition: scheduler.h:116
bool m_bReady
State of the mutex - true = ready, false = claimed.
Definition: mutex.h:159
void SetPriority(PORT_PRIO_TYPE uXPriority_)
SetPriority. Set the priority of the Thread (running or otherwise) to a different level...
Definition: thread.cpp:341
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