|
35 | 35 |
|
36 | 36 | namespace mongo {
|
37 | 37 | stdx::cv_status ClockSource::waitForConditionUntil(stdx::condition_variable& cv,
|
38 |
| - BasicLockableAdapter m, |
| 38 | + BasicLockableAdapter bla, |
39 | 39 | Date_t deadline,
|
40 | 40 | Waitable* waitable) {
|
41 | 41 | if (_tracksSystemClock) {
|
42 | 42 | if (deadline == Date_t::max()) {
|
43 |
| - Waitable::wait(waitable, this, cv, m); |
| 43 | + Waitable::wait(waitable, this, cv, bla); |
44 | 44 | return stdx::cv_status::no_timeout;
|
45 | 45 | }
|
46 | 46 |
|
47 |
| - return Waitable::wait_until(waitable, this, cv, m, deadline.toSystemTimePoint()); |
| 47 | + return Waitable::wait_until(waitable, this, cv, bla, deadline.toSystemTimePoint()); |
48 | 48 | }
|
49 | 49 |
|
50 | 50 | // The rest of this function only runs during testing, when the clock source is virtualized and
|
51 | 51 | // does not track the system clock.
|
52 | 52 |
|
53 |
| - if (deadline <= now()) { |
| 53 | + auto currentTime = now(); |
| 54 | + if (deadline <= currentTime) { |
54 | 55 | return stdx::cv_status::timeout;
|
55 | 56 | }
|
56 | 57 |
|
57 | 58 | struct AlarmInfo {
|
58 |
| - Mutex controlMutex = MONGO_MAKE_LATCH("AlarmInfo::controlMutex"); |
59 |
| - boost::optional<BasicLockableAdapter> waitLock; |
60 |
| - stdx::condition_variable* waitCV; |
61 |
| - stdx::cv_status cvWaitResult = stdx::cv_status::no_timeout; |
| 59 | + stdx::mutex mutex; // NOLINT |
| 60 | + |
| 61 | + stdx::condition_variable* cv; |
| 62 | + stdx::cv_status result = stdx::cv_status::no_timeout; |
62 | 63 | };
|
63 | 64 | auto alarmInfo = std::make_shared<AlarmInfo>();
|
64 |
| - alarmInfo->waitCV = &cv; |
65 |
| - alarmInfo->waitLock = m; |
66 |
| - const auto waiterThreadId = stdx::this_thread::get_id(); |
67 |
| - bool invokedAlarmInline = false; |
68 |
| - invariant(setAlarm(deadline, [alarmInfo, waiterThreadId, &invokedAlarmInline] { |
69 |
| - stdx::lock_guard<Latch> controlLk(alarmInfo->controlMutex); |
70 |
| - alarmInfo->cvWaitResult = stdx::cv_status::timeout; |
71 |
| - if (!alarmInfo->waitLock) { |
72 |
| - return; |
73 |
| - } |
74 |
| - if (stdx::this_thread::get_id() == waiterThreadId) { |
75 |
| - // In NetworkInterfaceMock, setAlarm may invoke its callback immediately if the deadline |
76 |
| - // has expired, so we detect that case and avoid self-deadlock by returning early, here. |
77 |
| - // It is safe to set invokedAlarmInline without synchronization in this case, because it |
78 |
| - // is exactly the case where the same thread is writing and consulting the value. |
79 |
| - invokedAlarmInline = true; |
| 65 | + alarmInfo->cv = &cv; |
| 66 | + |
| 67 | + invariant(setAlarm(deadline, [alarmInfo] { |
| 68 | + // Set an alarm to hit our virtualized deadline |
| 69 | + stdx::lock_guard infoLk(alarmInfo->mutex); |
| 70 | + auto cv = std::exchange(alarmInfo->cv, nullptr); |
| 71 | + if (!cv) { |
80 | 72 | return;
|
81 | 73 | }
|
82 |
| - stdx::lock_guard<BasicLockableAdapter> waitLk(*alarmInfo->waitLock); |
83 |
| - alarmInfo->waitCV->notify_all(); |
| 74 | + |
| 75 | + alarmInfo->result = stdx::cv_status::timeout; |
| 76 | + cv->notify_all(); |
84 | 77 | }));
|
85 |
| - if (!invokedAlarmInline) { |
86 |
| - Waitable::wait(waitable, this, cv, m); |
| 78 | + |
| 79 | + if (stdx::lock_guard infoLk(alarmInfo->mutex); !alarmInfo->cv) { |
| 80 | + // If setAlarm() ran inline, then we've timed out |
| 81 | + return alarmInfo->result; |
87 | 82 | }
|
88 |
| - m.unlock(); |
89 |
| - stdx::lock_guard<Latch> controlLk(alarmInfo->controlMutex); |
90 |
| - m.lock(); |
91 |
| - alarmInfo->waitLock = boost::none; |
92 |
| - alarmInfo->waitCV = nullptr; |
93 |
| - return alarmInfo->cvWaitResult; |
| 83 | + |
| 84 | + // This is a wait_until because theoretically setAlarm could run out of line before this cv |
| 85 | + // joins the wait list. Then it could completely miss the notification and block until a lucky |
| 86 | + // renotify or spurious wakeup. |
| 87 | + Waitable::wait_until(waitable, this, cv, bla, currentTime + kMaxTimeoutForArtificialClocks); |
| 88 | + |
| 89 | + stdx::lock_guard infoLk(alarmInfo->mutex); |
| 90 | + alarmInfo->cv = nullptr; |
| 91 | + return alarmInfo->result; |
94 | 92 | }
|
95 | 93 | } // namespace mongo
|
0 commit comments