mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-31 18:47:21 +08:00
lockstep_scheduler: simplify LockstepScheduler::cond_timedwait & reduce locking
- the loop is not needed - we optimize for the fast case and lock only if really needed
This commit is contained in:
+2
-1
@@ -23,9 +23,10 @@ private:
|
|||||||
pthread_mutex_t *passed_lock{nullptr};
|
pthread_mutex_t *passed_lock{nullptr};
|
||||||
uint64_t time_us{0};
|
uint64_t time_us{0};
|
||||||
bool timeout{false};
|
bool timeout{false};
|
||||||
bool done{false};
|
std::atomic<bool> done{false};
|
||||||
};
|
};
|
||||||
std::vector<std::shared_ptr<TimedWait>> timed_waits_{};
|
std::vector<std::shared_ptr<TimedWait>> timed_waits_{};
|
||||||
std::mutex timed_waits_mutex_{};
|
std::mutex timed_waits_mutex_{};
|
||||||
bool timed_waits_iterator_invalidated_{false};
|
bool timed_waits_iterator_invalidated_{false};
|
||||||
|
std::atomic<bool> setting_time_{false}; ///< true if set_absolute_time() is currently being executed
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ void LockstepScheduler::set_absolute_time(uint64_t time_us)
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock_timed_waits(timed_waits_mutex_);
|
std::unique_lock<std::mutex> lock_timed_waits(timed_waits_mutex_);
|
||||||
|
setting_time_ = true;
|
||||||
|
|
||||||
auto it = std::begin(timed_waits_);
|
auto it = std::begin(timed_waits_);
|
||||||
|
|
||||||
@@ -21,13 +22,12 @@ void LockstepScheduler::set_absolute_time(uint64_t time_us)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (temp_timed_wait->time_us <= time_us &&
|
if (temp_timed_wait->time_us <= time_us &&
|
||||||
!temp_timed_wait->timeout &&
|
!temp_timed_wait->timeout) {
|
||||||
!temp_timed_wait->done) {
|
|
||||||
temp_timed_wait->timeout = true;
|
|
||||||
// We are abusing the condition here to signal that the time
|
// We are abusing the condition here to signal that the time
|
||||||
// has passed.
|
// has passed.
|
||||||
timed_waits_iterator_invalidated_ = false;
|
timed_waits_iterator_invalidated_ = false;
|
||||||
pthread_mutex_lock(temp_timed_wait->passed_lock);
|
pthread_mutex_lock(temp_timed_wait->passed_lock);
|
||||||
|
temp_timed_wait->timeout = true;
|
||||||
pthread_cond_broadcast(temp_timed_wait->passed_cond);
|
pthread_cond_broadcast(temp_timed_wait->passed_cond);
|
||||||
pthread_mutex_unlock(temp_timed_wait->passed_lock);
|
pthread_mutex_unlock(temp_timed_wait->passed_lock);
|
||||||
|
|
||||||
@@ -41,6 +41,8 @@ void LockstepScheduler::set_absolute_time(uint64_t time_us)
|
|||||||
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setting_time_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,27 +65,33 @@ int LockstepScheduler::cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *loc
|
|||||||
timed_waits_iterator_invalidated_ = true;
|
timed_waits_iterator_invalidated_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
int result = pthread_cond_wait(cond, lock);
|
||||||
int result = pthread_cond_wait(cond, lock);
|
|
||||||
|
|
||||||
// We need to unlock before aqcuiring the timed_waits_mutex, otherwise
|
const bool timeout = new_timed_wait->timeout;
|
||||||
// we are at rist of priority inversion.
|
|
||||||
pthread_mutex_unlock(lock);
|
|
||||||
|
|
||||||
{
|
if (result == 0 && timeout) {
|
||||||
std::lock_guard<std::mutex> lock_timed_waits(timed_waits_mutex_);
|
result = ETIMEDOUT;
|
||||||
|
|
||||||
if (result == 0 && new_timed_wait->timeout) {
|
|
||||||
result = ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_timed_wait->done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The lock needs to be locked on exit of this function
|
|
||||||
pthread_mutex_lock(lock);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_timed_wait->done = true;
|
||||||
|
|
||||||
|
if (!timeout && setting_time_) {
|
||||||
|
// This is where it gets tricky: the timeout has not been triggered yet,
|
||||||
|
// and another thread is in set_absolute_time().
|
||||||
|
// If it already passed the 'done' check, it will access the mutex and
|
||||||
|
// the condition variable next. However they might be invalid as soon as we
|
||||||
|
// return here, so we wait until set_absolute_time() is done.
|
||||||
|
// In addition we have to unlock 'lock', otherwise we risk a
|
||||||
|
// deadlock due to a different locking order in set_absolute_time().
|
||||||
|
// Note that this case does not happen too frequently, and thus can be
|
||||||
|
// a bit more expensive.
|
||||||
|
pthread_mutex_unlock(lock);
|
||||||
|
timed_waits_mutex_.lock();
|
||||||
|
timed_waits_mutex_.unlock();
|
||||||
|
pthread_mutex_lock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LockstepScheduler::usleep_until(uint64_t time_us)
|
int LockstepScheduler::usleep_until(uint64_t time_us)
|
||||||
|
|||||||
Reference in New Issue
Block a user