LogCabin
|
00001 /* Copyright (c) 2012 Stanford University 00002 * Copyright (c) 2014-2015 Diego Ongaro 00003 * 00004 * Permission to use, copy, modify, and distribute this software for any 00005 * purpose with or without fee is hereby granted, provided that the above 00006 * copyright notice and this permission notice appear in all copies. 00007 * 00008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00015 */ 00016 00017 #include <algorithm> 00018 #include <cassert> 00019 #include <cstdlib> 00020 #include <cstring> 00021 #include <functional> 00022 #include <stdexcept> 00023 00024 #include "Core/Debug.h" 00025 #include "Core/Time.h" 00026 00027 namespace LogCabin { 00028 namespace Core { 00029 namespace Time { 00030 00031 CSystemClock::time_point 00032 CSystemClock::now() 00033 { 00034 struct timespec now; 00035 int r = clock_gettime(CLOCK_REALTIME, &now); 00036 if (r != 0) { 00037 PANIC("clock_gettime(CLOCK_REALTIME) failed: %s", 00038 strerror(errno)); 00039 } 00040 return time_point(std::chrono::nanoseconds( 00041 int64_t(now.tv_sec) * 1000 * 1000 * 1000 + 00042 now.tv_nsec)); 00043 } 00044 00045 CSteadyClock::time_point 00046 CSteadyClock::now() 00047 { 00048 struct timespec now; 00049 int r = clock_gettime(STEADY_CLOCK_ID, &now); 00050 if (r != 0) { 00051 PANIC("clock_gettime(STEADY_CLOCK_ID=%d) failed: %s", 00052 STEADY_CLOCK_ID, strerror(errno)); 00053 } 00054 return time_point(std::chrono::nanoseconds( 00055 int64_t(now.tv_sec) * 1000 * 1000 * 1000 + 00056 now.tv_nsec)); 00057 } 00058 00059 int64_t 00060 parseSignedDuration(const std::string& description) 00061 { 00062 const char* start = description.c_str(); 00063 char* end = NULL; 00064 errno = 0; 00065 int64_t r = strtol(start, &end, 10); 00066 if (errno == ERANGE) { 00067 // pass 00068 } else if (errno != 0 || start == end) { 00069 throw std::runtime_error( 00070 std::string("Invalid time description: " 00071 "could not parse number from ") + description); 00072 } 00073 00074 std::string units = end; 00075 // The black magic is from https://stackoverflow.com/a/217605 00076 // trim whitespace at end of string 00077 units.erase(std::find_if(units.rbegin(), units.rend(), 00078 std::not1(std::ptr_fun<int, int>(std::isspace))) 00079 .base(), 00080 units.end()); 00081 // trim whitespace at beginning of string 00082 units.erase(units.begin(), 00083 std::find_if(units.begin(), units.end(), 00084 std::not1(std::ptr_fun<int, int>(std::isspace)))); 00085 00086 int64_t overflow; 00087 if (r < 0L) 00088 overflow = std::numeric_limits<int64_t>::min(); 00089 else 00090 overflow = std::numeric_limits<int64_t>::max(); 00091 00092 if (units == "ns" || 00093 units == "nanosecond" || 00094 units == "nanoseconds") { 00095 // pass 00096 } else if (units == "us" || 00097 units == "microsecond" || 00098 units == "microseconds") { 00099 if (std::abs(r) <= 9223372036854775L) 00100 r *= 1000L; 00101 else 00102 r = overflow; 00103 } else if (units == "ms" || 00104 units == "millisecond" || 00105 units == "milliseconds") { 00106 if (std::abs(r) <= 9223372036854L) 00107 r *= 1000000L; 00108 else 00109 r = overflow; 00110 } else if (units == "s" || 00111 units == "second" || 00112 units == "seconds" || 00113 units == "") { 00114 if (std::abs(r) <= 9223372036L) 00115 r *= 1000000000L; 00116 else 00117 r = overflow; 00118 } else if (units == "min" || 00119 units == "minute" || 00120 units == "minutes") { 00121 if (std::abs(r) <= 153722867L) 00122 r *= 1000000000L * 60L; 00123 else 00124 r = overflow; 00125 } else if (units == "h" || 00126 units == "hr" || 00127 units == "hour" || 00128 units == "hours") { 00129 if (std::abs(r) <= 2562047L) 00130 r *= 1000000000L * 60L * 60L; 00131 else 00132 r = overflow; 00133 } else if (units == "d" || 00134 units == "day" || 00135 units == "days") { 00136 if (std::abs(r) <= 106751L) 00137 r *= 1000000000L * 60L * 60L * 24L; 00138 else 00139 r = overflow; 00140 } else if (units == "w" || 00141 units == "wk" || 00142 units == "week" || 00143 units == "weeks") { 00144 if (std::abs(r) <= 15250L) 00145 r *= 1000000000L * 60L * 60L * 24L * 7L; 00146 else 00147 r = overflow; 00148 } else if (units == "mo" || 00149 units == "month" || 00150 units == "months") { 00151 // Months vary in length, so this is the average number of seconds in a 00152 // month. If someone is specifying durations in such large units, they 00153 // probably won't care. 00154 if (std::abs(r) <= 3507L) 00155 r *= 1000000000L * 2629800L; 00156 else 00157 r = overflow; 00158 } else if (units == "y" || 00159 units == "yr" || 00160 units == "year" || 00161 units == "years") { 00162 // Years vary in length due to leap years, so this is the number of 00163 // seconds in a 365.25-day year. If someone is specifying durations in 00164 // such large units, they probably won't care. 00165 if (std::abs(r) <= 292L) 00166 r *= 1000000000L * 31557600L; 00167 else 00168 r = overflow; 00169 } else { 00170 throw std::runtime_error( 00171 std::string("Invalid time description: " 00172 "could not parse units from ") + description); 00173 } 00174 return r; 00175 } 00176 00177 uint64_t 00178 parseNonNegativeDuration(const std::string& description) 00179 { 00180 int64_t r = parseSignedDuration(description); 00181 if (r < 0) { 00182 throw std::runtime_error(Core::StringUtil::format( 00183 "Invalid time description: '%s' is negative", 00184 description.c_str())); 00185 } 00186 return static_cast<uint64_t>(r); 00187 } 00188 00189 void 00190 sleep(SteadyClock::time_point wake) 00191 { 00192 struct timespec wakeSpec = makeTimeSpec(wake); 00193 if (wakeSpec.tv_sec < 0) 00194 return; 00195 int r = clock_nanosleep(STEADY_CLOCK_ID, 00196 TIMER_ABSTIME, 00197 &wakeSpec, 00198 NULL); 00199 if (r != 0) { 00200 PANIC("clock_nanosleep(STEADY_CLOCK_ID=%d, %s) failed: %s", 00201 STEADY_CLOCK_ID, 00202 Core::StringUtil::toString(wake).c_str(), 00203 strerror(r)); 00204 } 00205 } 00206 00207 void 00208 sleep(std::chrono::nanoseconds duration) 00209 { 00210 typedef SteadyClock::time_point TimePoint; 00211 if (duration <= std::chrono::nanoseconds::zero()) 00212 return; 00213 TimePoint now = SteadyClock::now(); 00214 TimePoint wake = now + duration; 00215 if (wake < now) { // overflow 00216 wake = TimePoint::max(); 00217 } 00218 Core::Time::sleep(wake); 00219 } 00220 00221 SteadyTimeConverter::SteadyTimeConverter() 00222 : steadyNow(SteadyClock::now()) 00223 , systemNow(SystemClock::now()) 00224 { 00225 } 00226 00227 SystemClock::time_point 00228 SteadyTimeConverter::convert(SteadyClock::time_point when) 00229 { 00230 // Note that this relies on signed integer wrapping, so -fwrapv or 00231 // -fno-strict-overflow must be on for correctness under optimizing 00232 // comiplers. The unit tests are pretty good at catching when this isn't 00233 // the case. 00234 std::chrono::nanoseconds diff = when - steadyNow; 00235 SystemClock::time_point then = systemNow + diff; 00236 if (when > steadyNow && then < systemNow) // overflow 00237 return SystemClock::time_point::max(); 00238 return then; 00239 } 00240 00241 int64_t 00242 SteadyTimeConverter::unixNanos(SteadyClock::time_point when) 00243 { 00244 return std::chrono::nanoseconds( 00245 convert(when).time_since_epoch()).count(); 00246 } 00247 00248 } // namespace LogCabin::Core::Time 00249 } // namespace LogCabin::Core 00250 } // namespace LogCabin 00251 00252 namespace std { 00253 00254 namespace { 00255 00256 std::string 00257 padFraction(int64_t fraction, uint64_t digits) 00258 { 00259 if (fraction == 0) 00260 return ""; 00261 if (fraction < 0) 00262 fraction *= -1; 00263 while (fraction % 1000 == 0 && digits >= 3) { 00264 digits -= 3; 00265 fraction /= 1000; 00266 } 00267 char ret[digits + 2]; 00268 ret[0] = '.'; 00269 ret[digits + 1] = '\0'; 00270 for (uint64_t i = digits; i > 0; --i) { 00271 ret[i] = char('0' + (fraction % 10)); 00272 fraction /= 10; 00273 } 00274 return ret; 00275 } 00276 00277 } // namespace std::<anonymous> 00278 00279 std::ostream& 00280 operator<<(std::ostream& os, 00281 const std::chrono::nanoseconds& duration) 00282 { 00283 int64_t nanos = duration.count(); 00284 if (nanos / 1000000000L != 0) { 00285 int64_t whole = nanos / 1000000000L; 00286 int64_t fraction = nanos % 1000000000L; 00287 os << whole << padFraction(fraction, 9) << " s"; 00288 } else if (nanos / 1000000L != 0) { 00289 int64_t whole = nanos / 1000000L; 00290 int64_t fraction = nanos % 1000000L; 00291 os << whole << padFraction(fraction, 6) << " ms"; 00292 } else if (nanos / 1000L != 0) { 00293 int64_t whole = nanos / 1000L; 00294 int64_t fraction = nanos % 1000L; 00295 os << whole << padFraction(fraction, 3) << " us"; 00296 } else { 00297 os << nanos << " ns"; 00298 } 00299 return os; 00300 } 00301 00302 std::ostream& 00303 operator<<(std::ostream& os, 00304 const std::chrono::microseconds& duration) 00305 { 00306 return os << chrono::duration_cast<chrono::nanoseconds>(duration); 00307 } 00308 00309 std::ostream& 00310 operator<<(std::ostream& os, 00311 const std::chrono::milliseconds& duration) 00312 { 00313 return os << chrono::duration_cast<chrono::nanoseconds>(duration); 00314 } 00315 00316 std::ostream& 00317 operator<<(std::ostream& os, 00318 const std::chrono::seconds& duration) 00319 { 00320 return os << chrono::duration_cast<chrono::nanoseconds>(duration); 00321 } 00322 00323 std::ostream& 00324 operator<<(std::ostream& os, 00325 const std::chrono::minutes& duration) 00326 { 00327 return os << chrono::duration_cast<chrono::nanoseconds>(duration); 00328 } 00329 00330 std::ostream& 00331 operator<<(std::ostream& os, 00332 const std::chrono::hours& duration) 00333 { 00334 return os << chrono::duration_cast<chrono::nanoseconds>(duration); 00335 } 00336 00337 } // namespace std