9

Are monotonic properties of std::chrono::steady_clock preserved across threads? For example, suppose I have the following program.

#include <chrono>
#include <mutex>
#include <thread>

using namespace std;
using namespace chrono;

mutex m;
int i = 0;

void do_something(int &x) {
  x += 1;
}

void f1() {
  unique_lock<mutex> lock(m);
  auto time = steady_clock::now();
  do_something(i);
}

void f2() {
  unique_lock<mutex> lock(m);
  auto time = steady_clock::now();
  do_something(i);
}

int main() {
  thread t1(f1);
  thread t2(f2);
  t1.join();
  t2.join();
  return 0;
}

Can I assume that the thread that has the smaller time value in the end (supposing they have different value at all) modified i before the other and that the other saw i as it was left by the first one?

Giovanni Mascellani
  • 1,218
  • 2
  • 11
  • 26
  • Does 'auto' mean to put the time on the stack and discard it when the function returns? – Marichyasana Dec 02 '16 at 11:05
  • @Marichyasana `auto` is just shorthand to instruct the compiler to automatically infer the type so that I do not have to type it. Assume you have `steady_clock::time_point` instead of `auto` if you do not like it. Of course the variable is automatic and so is discarded at the end of the thread, but suppose I have some way to report it and decide, comparing the timestamps, which thread executed before. Can I have guarantee from the `steady_clock` monotonicity? – Giovanni Mascellani Dec 02 '16 at 11:13
  • I have never seen a clock that runs backwards some of the time and forwards some of the time. – Marichyasana Dec 02 '16 at 11:15
  • 1
    @Marichyasana There are many. Now, `steady_clock` makes the guarantee that it never does, meaning that if you call it twice the first call will return a value which is not greater than the second. But I do not understand whether this guarantees extends across different threads, and that is what I am asking. – Giovanni Mascellani Dec 02 '16 at 11:17
  • (Sorry, I meant summer time,. not leap year :p brain fart) – deviantfan Dec 02 '16 at 11:28
  • @deviantfan However, leap second would have been ok as well... – Giovanni Mascellani Dec 02 '16 at 11:30
  • "When the difference between UTC and UT1 approaches 0.9 seconds, a leap second is added to UTC and to clocks worldwide. By adding an additional second to the time count, our clocks are effectively stopped for that second to give Earth the opportunity to catch up with atomic time." From: https://www.timeanddate.com/time/leapseconds.html – Marichyasana Dec 02 '16 at 12:03
  • Monotonicity has been answered. But since your `i` is not atomic, you have a race condition. Therefore, your corollary that _the thread that has the smaller time value ... modified `i` before the other and that the other saw `i` as it was left by the first one_ is **not** correct; instead, you have undefined behavior. – j6t Dec 02 '16 at 17:28
  • @j6t Can you explain this in detail, where do you see UB and race conditions? Did you miss the mutex? – deviantfan Dec 02 '16 at 19:18
  • @deviantfan Ah, yeah, sorry, I missed the mutex. – j6t Dec 02 '16 at 22:54

1 Answers1

10

Standard [time.clock.steady]

...
static constexpr bool is_steady = true;
static time_point now() noexcept;
...  

is_steady has to be true in all implementations (ie. the class can not exist with false, if the OS etc. isn't capable of it), and both members are independent of instances.

Standard [time.clock.req]:

Clock requirements
...
C1 and C2 denote clock types. t1 and t2 are values returned by C1::now() where the call returning t1 happens before (1.10) the call returning t2 and both of these calls occur before C1::time_-point::max().
...
C1::is_steady: true if t1 <= t2 is always true and the time between clock ticks is constant, otherwise false.

And the 1.10 part contains:

Multi-threaded executions and data races
...
An evaluation A happens before an evaluation B if:
A is sequenced before B, or
A inter-thread happens before B.
...
An evaluation A inter-thread happens before an evaluation B if
A synchronizes with B, or ...

I don't think synchronizing needs to be copied here (a mutex should be enough to fulfil that),
so: Yes, it's ok.

deviantfan
  • 11,268
  • 3
  • 32
  • 49