summaryrefslogtreecommitdiff
path: root/indra/llcommon/llcond.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llcond.cpp')
-rw-r--r--indra/llcommon/llcond.cpp111
1 files changed, 111 insertions, 0 deletions
diff --git a/indra/llcommon/llcond.cpp b/indra/llcommon/llcond.cpp
new file mode 100644
index 0000000000..d5362a48fc
--- /dev/null
+++ b/indra/llcommon/llcond.cpp
@@ -0,0 +1,111 @@
+/**
+ * @file llcond.cpp
+ * @author Nat Goodspeed
+ * @date 2019-07-17
+ * @brief Implementation for llcond.
+ *
+ * $LicenseInfo:firstyear=2019&license=viewerlgpl$
+ * Copyright (c) 2019, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llcond.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+
+namespace // anonymous
+{
+
+// See comments in LLCond::convert(const LLDate&) below
+std::time_t compute_lldate_epoch()
+{
+ LLDate lldate_epoch;
+ std::tm tm;
+ // It should be noted that calling LLDate::split() to write directly
+ // into a std::tm struct depends on S32 being a typedef for int in
+ // stdtypes.h: split() takes S32*, whereas tm fields are documented to
+ // be int. If you get compile errors here, somebody changed the
+ // definition of S32. You'll have to declare six S32 variables,
+ // split() into them, then assign them into the relevant tm fields.
+ if (! lldate_epoch.split(&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec))
+ {
+ // Theoretically split() could return false. In that case, we
+ // don't have valid data, so we can't compute offset, so skip the
+ // rest of this.
+ return 0;
+ }
+
+ tm.tm_isdst = 0;
+ std::time_t lldate_epoch_time = std::mktime(&tm);
+ if (lldate_epoch_time == -1)
+ {
+ // Theoretically mktime() could return -1, meaning that the contents
+ // of the passed std::tm cannot be represented as a time_t. (Worrisome
+ // if LLDate's epoch happened to be exactly 1 tick before
+ // std::time_t's epoch...)
+ // In the error case, assume offset 0.
+ return 0;
+ }
+
+ // But if we got this far, lldate_epoch_time is the time_t we want.
+ return lldate_epoch_time;
+}
+
+} // anonymous namespace
+
+// convert LLDate to a chrono::time_point
+std::chrono::system_clock::time_point LLCond::convert(const LLDate& lldate)
+{
+ // std::chrono::system_clock's epoch MAY be the Unix epoch, namely
+ // midnight UTC on 1970-01-01, in fact it probably is. But until C++20,
+ // system_clock does not guarantee that. Unfortunately time_t doesn't
+ // specify its epoch either, other than to note that it "almost always" is
+ // the Unix epoch (https://en.cppreference.com/w/cpp/chrono/c/time_t).
+ // LLDate, being based on apr_time_t, does guarantee 1970-01-01T00:00 UTC.
+ // http://apr.apache.org/docs/apr/1.5/group__apr__time.html#gadb4bde16055748190eae190c55aa02bb
+
+ // The easy, efficient conversion would be
+ // std::chrono::system_clock::from_time_t(std::time_t(LLDate::secondsSinceEpoch())).
+ // But that assumes that both time_t and system_clock have the same epoch
+ // as LLDate -- an assumption that will work until it unexpectedly doesn't.
+
+ // It would be more formally correct to break out the year, month, day,
+ // hour, minute, second (UTC) using LLDate::split() and recombine them
+ // into std::time_t using std::mktime(). However, both split() and
+ // mktime() have integer second granularity, whereas callers of
+ // wait_until() are very likely to be interested in sub-second precision.
+ // In that sense std::chrono::system_clock::from_time_t() is still
+ // preferred.
+
+ // So use the split() / mktime() mechanism to determine the numeric value
+ // of the LLDate / apr_time_t epoch as expressed in time_t. (We assume
+ // that the epoch offset can be expressed as integer seconds, per split()
+ // and mktime(), which seems plausible.)
+
+ // n.b. A function-static variable is initialized only once in a
+ // thread-safe way.
+ static std::time_t lldate_epoch_time = compute_lldate_epoch();
+
+ // LLDate::secondsSinceEpoch() gets us, of course, how long it has
+ // been since lldate_epoch_time. So adding lldate_epoch_time should
+ // give us the correct time_t representation of a given LLDate even if
+ // time_t's epoch differs from LLDate's.
+ // We don't have to worry about the relative epochs of time_t and
+ // system_clock because from_time_t() takes care of that!
+ return std::chrono::system_clock::from_time_t(lldate_epoch_time +
+ lldate.secondsSinceEpoch());
+}
+
+// convert F32Milliseconds to a chrono::duration
+std::chrono::milliseconds LLCond::convert(F32Milliseconds)
+{
+ // extract the F32 milliseconds from F32Milliseconds, construct
+ // std::chrono::milliseconds from that value
+ return std::chrono::milliseconds(timeout_duration.value());
+}