TEST(EventBaseTest, RunInThread) { uint32_t numThreads = 50; uint32_t opsPerThread = 100; RunInThreadData data(numThreads, opsPerThread); deque<std::thread> threads; for (uint32_t i = 0; i < numThreads; ++i) { threads.emplace_back([i, &data] { for (int n = 0; n < data.opsPerThread; ++n) { RunInThreadArg* arg = new RunInThreadArg(&data, i, n); data.evb.runInEventBaseThread(runInThreadTestFunc, arg); usleep(10); } }); } // Add a timeout event to run after 3 seconds. // Otherwise loop() will return immediately since there are no events to run. // Once the last thread exits, it will stop the loop(). However, this // timeout also stops the loop in case there is a bug performing the normal // stop. data.evb.tryRunAfterDelay(std::bind(&EventBase::terminateLoopSoon, &data.evb), 3000); TimePoint start; data.evb.loop(); TimePoint end; // Verify that the loop exited because all threads finished and requested it // to stop. This should happen much sooner than the 3 second timeout. // Assert that it happens in under a second. (This is still tons of extra // padding.) auto timeTaken = std::chrono::duration_cast<milliseconds>( end.getTime() - start.getTime()); ASSERT_LT(timeTaken.count(), 1000); VLOG(11) << "Time taken: " << timeTaken.count(); // Verify that we have all of the events from every thread int expectedValues[numThreads]; for (uint32_t n = 0; n < numThreads; ++n) { expectedValues[n] = 0; } for (deque< pair<int, int> >::const_iterator it = data.values.begin(); it != data.values.end(); ++it) { int threadID = it->first; int value = it->second; ASSERT_EQ(expectedValues[threadID], value); ++expectedValues[threadID]; } for (uint32_t n = 0; n < numThreads; ++n) { ASSERT_EQ(expectedValues[n], opsPerThread); } // Wait on all of the threads. for (auto& thread: threads) { thread.join(); } }
// instanciate and play with gates TEST(ALibxx, ChronoTest){ using namespace Davix::Chrono; TimePoint pp; ASSERT_EQ(0, pp.toTimestamp()); pp = Clock(Clock::Monolitic).now(); TimePoint copy_time = pp; ASSERT_EQ(pp, copy_time); copy_time = copy_time + Duration(5); ASSERT_NE(copy_time, pp); ASSERT_EQ(copy_time, pp + Duration(5)); ASSERT_GE(copy_time, pp); ASSERT_GE(copy_time, pp + Duration(5)); ASSERT_GT(copy_time, pp); ASSERT_LE(copy_time, pp + Duration(5)); ASSERT_LE(copy_time, pp + Duration(10)); ASSERT_LT(copy_time, pp + Duration(10)); ASSERT_EQ(copy_time + Duration(5), pp + Duration(10)); copy_time = pp; ASSERT_EQ(copy_time, pp + Duration(10) - Duration(20) + Duration(10)); TimePoint dd; ASSERT_ANY_THROW( Duration res = dd - pp; );
// Print the spatial entities corresponding to the given time point and of the requested type void printSpatialEntities(TimePoint &timePoint, const SubsetSpecificType &spatialEntityType) { for (auto it = timePoint.getSpatialEntitiesBeginIterator(spatialEntityType); it != timePoint.getSpatialEntitiesEndIterator(spatialEntityType); it++) { std::cout << "\t SpatialEntity" << std::endl; std::cout << (*(*it)).toString() << std::endl; } }
void SpatialTemporalDataReader::setTimePointValue(const pt::ptree &timePointTree, TimePoint &timePoint) { double timePointValue; if (timePointHasValue(timePointTree, timePointValue)) { timePoint.setValue(timePointValue); } else { timePoint.setValue(std::numeric_limits<double>::max()); } }
void TimeLine::addPoint(ofPoint _pos){ if ( (startTime == -1) || (points.size() == 0) ){ startTime = ofGetElapsedTimef(); } TimePoint newPoint; newPoint.set(_pos); newPoint.time = ofGetElapsedTimef() - startTime; points.push_back( newPoint ); }
/** * Test destroying a registered EventHandler */ TEST(EventBaseTest, DestroyHandler) { class DestroyHandler : public AsyncTimeout { public: DestroyHandler(EventBase* eb, EventHandler* h) : AsyncTimeout(eb) , handler_(h) {} virtual void timeoutExpired() noexcept { delete handler_; } private: EventHandler* handler_; }; EventBase eb; SocketPair sp; // Fill up the write buffer before starting size_t initialBytesWritten = writeUntilFull(sp[0]); // Register for write events TestHandler* handler = new TestHandler(&eb, sp[0]); handler->registerHandler(EventHandler::WRITE | EventHandler::PERSIST); // After 10ms, read some data, so that the handler // will be notified that it can write. eb.tryRunAfterDelay(std::bind(checkReadUntilEmpty, sp[1], initialBytesWritten), 10); // Start a timer to destroy the handler after 25ms // This mainly just makes sure the code doesn't break or assert DestroyHandler dh(&eb, handler); dh.scheduleTimeout(25); TimePoint start; eb.loop(); TimePoint end; // Make sure the EventHandler was uninstalled properly when it was // destroyed, and the EventBase loop exited T_CHECK_TIMEOUT(start, end, milliseconds(25)); // Make sure that the handler wrote data to the socket // before it was destroyed size_t bytesRemaining = readUntilEmpty(sp[1]); ASSERT_GT(bytesRemaining, 0); }
int TimePoint::getTimeDiffInMinute(const TimePoint &another) const { int diff = convertToMinute() - another.convertToMinute(); if(diff < 0) diff = -diff; return diff; }
//--------------------------------------------------------------------------- void getMeanAndStdErrorEndPoint(const std::vector<TimeSeries>& timeSeries, TimePoint& mean, TimePoint& stdError) { assert(mean.isNull()==true); assert(stdError.isNull()==true); //Only set: biasA, biasB, fractionMixedPairs const unsigned int nTimeSeries = timeSeries.size(); for (unsigned i=0; i<nTimeSeries; ++i) { //Get the index of the final index const unsigned int time = timeSeries[i].timePoints.size() - 1; mean += timeSeries[i].timePoints[time]; } mean/=nTimeSeries; }
inline void setAnimationTime(const TimePoint& timePoint) { if (mode == MapMode::Still) { return; } animationTime = timePoint.time_since_epoch(); };
static inline int32 timeSeed() { using namespace chrono; static int32 count = 0; typedef high_resolution_clock::time_point TimePoint; typedef TimePoint::duration Duration; TimePoint now = high_resolution_clock::now(); Duration sinceEpoch = now.time_since_epoch(); seconds secs = duration_cast<seconds>(sinceEpoch); nanoseconds nsecs = sinceEpoch - secs; return (int32)secs.count() ^ (int32)nsecs.count() ^ count--; }
// Print the given time point void printTimePoint(TimePoint &timePoint) { std::cout << "Time point " << timePoint.getValue() << std::endl; for (std::size_t i = 0; i < NR_SUBSET_SPECIFIC_TYPES; i++) { std::cout << "Subset specific type " << i << ": " << std::endl; printSpatialEntities(timePoint, subsetspecific::computeSubsetSpecificType(i)); } }
const synfig::Node::time_set & ValueNode_DynamicList::ListEntry::get_times() const { synfig::ActivepointList::const_iterator j = timing_info.begin(), end = timing_info.end(); //must remerge with all the other values because we don't know if we've changed... times = value_node->get_times(); for(; j != end; ++j) { TimePoint t; t.set_time(j->get_time()); t.set_guid(j->get_guid()); times.insert(t); } return times; }
static inline int64 OSCTime(TimePoint const & tp) { using namespace chrono; typedef typename TimePoint::duration Duration; Duration sinceEpoch = tp.time_since_epoch(); seconds secs = duration_cast<seconds>(sinceEpoch); nanoseconds nsecs = sinceEpoch - secs; return ((int64)(secs.count() + kSECONDS_FROM_1900_to_1970) << 32) + (int64)(nsecs.count() * kNanosToOSCunits); }
void SpatialTemporalDataReader::addSpatialEntityToTimePoint(const pt::ptree &spatialEntityTree, TimePoint &timePoint) { std::shared_ptr<SpatialEntity> spatialEntity; SubsetSpecificType spatialEntityType; createDerivedSpatialEntity(spatialEntityTree, spatialEntity, spatialEntityType); setSpatialEntityScaleAndSubsystem(spatialEntityTree, spatialEntity); setSpatialEntityMeasureValues(spatialEntityTree, spatialEntity); timePoint.addSpatialEntityAndType(spatialEntity, spatialEntityType); }
void TemporalDataReader::addNumericStateVariablesToTimePoint(const std::vector<std::string> &lineTokens, TimePoint &timePoint) { std::size_t nrOfTokens = lineTokens.size(); for (std::size_t i = 1; i < nrOfTokens; i++) { double observableVariableValue = StringManipulator::convert<double>(lineTokens[i]); timePoint.addNumericStateVariable( numericStateVariableIds[i], observableVariableValue ); } }
jobject mdr::JniStationPrediction::createJniStationPrediction(JNIEnv *env, TimePoint timePoint, float value, std::string timeZone) { auto duration = timePoint.time_since_epoch(); auto epoch = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); jlong jniEpoch = static_cast<jlong>(epoch); jobject jniValue = mdr::JniFloat::toJni(env, value); jstring tz = mdr::JniString::toJni(env, timeZone); auto retVal = env->CallStaticObjectMethod(stationPredictionFactoryClass, factoryCtor, jniEpoch, tz, jniValue); mdr::Jni::checkException(env, true); return retVal; }
bool checkTimeout( const TimePoint& start, const TimePoint& end, nanoseconds expected, bool allowSmaller, nanoseconds tolerance) { auto elapsedTime = end.getTimeStart() - start.getTimeEnd(); if (!allowSmaller) { // Timeouts should never fire before the time was up. // Allow 1ms of wiggle room for rounding errors. if (elapsedTime < (expected - milliseconds(1))) { return false; } } // Check that the event fired within a reasonable time of the timout. // // If the system is under heavy load, our process may have had to wait for a // while to be run. The time spent waiting for the processor shouldn't // count against us, so exclude this time from the check. nanoseconds timeExcluded; if (end.getTid() != start.getTid()) { // We can only correctly compute the amount of time waiting to be scheduled // if both TimePoints were set in the same thread. timeExcluded = nanoseconds(0); } else { timeExcluded = end.getTimeWaiting() - start.getTimeWaiting(); assert(end.getTimeWaiting() >= start.getTimeWaiting()); // Add a tolerance here due to precision issues on linux, see below note. assert((elapsedTime + tolerance) >= timeExcluded); } nanoseconds effectiveElapsedTime(0); if (elapsedTime > timeExcluded) { effectiveElapsedTime = elapsedTime - timeExcluded; } // On x86 Linux, sleep calls generally have precision only to the nearest // millisecond. The tolerance parameter lets users allow a few ms of slop. auto overrun = effectiveElapsedTime - expected; if (overrun > tolerance) { return false; } return true; }
/** * Test the behavior of tryRunAfterDelay() when some timeouts are * still scheduled when the EventBase is destroyed. */ TEST(EventBaseTest, RunAfterDelayDestruction) { TimePoint timestamp1(false); TimePoint timestamp2(false); TimePoint timestamp3(false); TimePoint timestamp4(false); TimePoint start(false); TimePoint end(false); { EventBase eb; // Run two normal timeouts eb.tryRunAfterDelay(std::bind(&TimePoint::reset, ×tamp1), 10); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, ×tamp2), 20); // Schedule a timeout to stop the event loop after 40ms eb.tryRunAfterDelay(std::bind(&EventBase::terminateLoopSoon, &eb), 40); // Schedule 2 timeouts that would fire after the event loop stops eb.tryRunAfterDelay(std::bind(&TimePoint::reset, ×tamp3), 80); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, ×tamp4), 160); start.reset(); eb.loop(); end.reset(); } T_CHECK_TIMEOUT(start, timestamp1, milliseconds(10)); T_CHECK_TIMEOUT(start, timestamp2, milliseconds(20)); T_CHECK_TIMEOUT(start, end, milliseconds(40)); ASSERT_TRUE(timestamp3.isUnset()); ASSERT_TRUE(timestamp4.isUnset()); // Ideally this test should be run under valgrind to ensure that no // memory is leaked. }
void SpatialTemporalDataReader::addNumericStateVariableToTimePoint(const pt::ptree &numericStateVariableTree, TimePoint &timePoint) { std::string name = numericStateVariableTree.get<std::string>(LABEL_NUMERIC_STATE_VARIABLE_NAME); boost::optional<std::string> scaleAndSubsystem = numericStateVariableTree.get_optional<std::string>(LABEL_NUMERIC_STATE_VARIABLE_SCALE_AND_SUBSYSTEM); double value = numericStateVariableTree.get<double>(LABEL_NUMERIC_STATE_VARIABLE_VALUE); NumericStateVariableId numericStateVariableId( name, scaleAndSubsystem.get_value_or(ScaleAndSubsystem::DEFAULT_VALUE) ); timePoint.addNumericStateVariable(numericStateVariableId, value); }
void readMDXVelocityField(const string &filename, TimePoint &timePoint, const Mesh3d &mesh, double time) { ifstream input(filename); double unusedTime; //TODO: Should this be thrown out? input >> unusedTime; // unused if (time != unusedTime) { cout << "Warning: File " << filename << " Specifies time " << unusedTime << ", using " << time << " instead." << endl; } timePoint.time = time; timePoint.resize(mesh.points.size()); cout << "Reading velocities from " << filename << endl; readMDXPoints(timePoint.velocities, input, mesh.points.size()); cout<<" Velocities were read succesfully " <<endl; calculateVorticityMesh(mesh, timePoint); cout<<" Vorticities were calculated succesfully " <<endl; #ifdef DUMP_MSH writeMshFile(filename + "_out.msh", mesh, timePoint); cout<<" Dumped msh file to " << filename << "_out.msh" << endl; #endif }
void TemporalDataReader::setTimePointValue(const std::vector<std::string> &lineTokens, TimePoint &timePoint) { double timePointValue = StringManipulator::convert<double>(lineTokens[0]); timePoint.setValue(timePointValue); }
static inline double secondsSinceEpoch(TimePoint const & tp) { return chrono::duration_cast<chrono::nanoseconds>(tp.time_since_epoch()).count() * 1e-9; }
TimerId Evloop::runEvery(double secs , AlarmCallback&& acb) { TimePoint tp = TimePoint::now(); tp.addSeconds(secs); return timerQueue_->addTimer(tp , std::move(acb) , secs); }
TimerId Evloop::runEvery(double secs , const AlarmCallback& acb) { TimePoint tp = TimePoint::now(); tp.addSeconds(secs); return timerQueue_->addTimer(tp , acb , secs); }
int main(void) { int caseId, numEvent, hour, minute, diff; vector<TimePeriod> events; char desc[MAX_LEN + 1]; int maxInd, maxMinute; TimePoint endP(18, 0); TimePoint startP(10, 0); TimePeriod endPeriod(endP, endP); TimePeriod startPeriod(startP, startP); //freopen("in.txt", "r", stdin); caseId = 1; while(scanf("%d", &numEvent) > 0) { gets(desc); events.clear(); events.push_back(startPeriod); for(int i = 0; i < numEvent; i++) { scanf("%d:%d", &hour, &minute); TimePoint mStart(hour, minute); scanf("%d:%d", &hour, &minute); TimePoint mEnd(hour, minute); events.push_back(TimePeriod(mStart, mEnd)); gets(desc); } events.push_back(endPeriod); numEvent += 2; sort(events.begin(), events.end()); maxInd = 0; maxMinute = 0; for(int i = 0; i < numEvent - 1; i++) { diff = events[i].getDistanceInMinute(events[i + 1]); if(diff > maxMinute) { maxMinute = diff; maxInd = i; } } TimePoint result; result.getTimePointFromMinute(maxMinute); printf("Day #%d: the longest nap starts at %d:%02d and will last for ", caseId, events[maxInd].getEndP().getHour(), events[maxInd].getEndP().getMinute()); if(result.getHour() != 0) printf("%d hours and ", result.getHour()); printf("%d minutes.\n", result.getMinute()); caseId++; } return 0; }
inline int TimePeriod::getDistanceInMinute(const TimePeriod & another) const { return _endP.getTimeDiffInMinute(another._startP); }
inline bool TimePoint::operator< (const TimePoint & another) const { return convertToMinute() < another.convertToMinute(); }
void CellRenderer_TimeTrack::render_vfunc( const ::Cairo::RefPtr< ::Cairo::Context>& cr, Gtk::Widget& /* widget */, const Gdk::Rectangle& /* background_area */, const Gdk::Rectangle& cell_area, Gtk::CellRendererState /* flags */) { if(!cr) return; Glib::RefPtr<Gtk::Adjustment> adjustment=get_adjustment(); // Gtk::StateType state = Gtk::STATE_ACTIVE; // Gtk::ShadowType shadow; Gdk::Color change_time_color("#008800"), curr_time_color("#0000ff"), inactive_color("#000000"), keyframe_color("#a07f7f"); Gdk::Color activepoint_color[2]; activepoint_color[0]=Gdk::Color("#ff0000"); activepoint_color[1]=Gdk::Color("#00ff00"); synfig::Canvas::Handle canvas(property_canvas().get_value()); synfigapp::ValueDesc value_desc = property_value_desc().get_value(); synfig::ValueNode *base_value = value_desc.get_value_node().get(); // synfig::ValueNode_Animated *value_node=dynamic_cast<synfig::ValueNode_Animated*>(base_value); synfig::ValueNode_DynamicList *parent_value_node(0); if(property_value_desc().get_value().parent_is_value_node()) parent_value_node=dynamic_cast<synfig::ValueNode_DynamicList*>(property_value_desc().get_value().get_parent_value_node().get()); // If the canvas is defined, then load up the keyframes if(canvas) { const synfig::KeyframeList& keyframe_list(canvas->keyframe_list()); synfig::KeyframeList::const_iterator iter; for(iter=keyframe_list.begin();iter!=keyframe_list.end();++iter) { if(!iter->get_time().is_valid()) continue; const int x((int)((float)cell_area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->get_time()-adjustment->get_lower()))); if(iter->get_time()>=adjustment->get_lower() && iter->get_time()<adjustment->get_upper()) { cr->set_source_rgb(keyframe_color.get_red_p(), keyframe_color.get_green_p(), keyframe_color.get_blue_p()); cr->rectangle(cell_area.get_x()+x, cell_area.get_y(), 1, cell_area.get_height()+1); cr->fill(); } } } const synfig::Time time_offset = get_time_offset_from_vdesc(value_desc); const synfig::Time time_dilation = get_time_dilation_from_vdesc(value_desc); Gdk::Rectangle area(cell_area); float lower = adjustment->get_lower(), upper = adjustment->get_upper(); //render time points where value changed { std::set<Time> times; get_change_times_from_vdesc(value_desc, times); for(std::set<Time>::const_iterator i = times.begin(); i != times.end(); ++i) { //find the coordinate in the drawable space... Time t_orig = *i; if(!t_orig.is_valid()) continue; Time t = t_orig - time_offset; if (time_dilation!=0) t = t / time_dilation; if(t<adjustment->get_lower() || t>adjustment->get_upper()) continue; const int w = 1; const int h = (area.get_height() - 2)/2; const int x = area.get_x() + (int)((t-lower)*area.get_width()/(upper-lower)); const int y = area.get_y() + (area.get_height() - h)/2; cr->rectangle(x, y, w, h); cr->set_source_rgb( change_time_color.get_red_p(), change_time_color.get_green_p(), change_time_color.get_blue_p() ); cr->fill(); } } //render all the time points that exist { const synfig::Node::time_set *tset = get_times_from_vdesc(value_desc); if(tset) { synfig::Node::time_set::const_iterator i = tset->begin(), end = tset->end(); bool valselected = sel_value.get_value_node() == base_value && !sel_times.empty(); float cfps = get_canvas()->rend_desc().get_frame_rate(); vector<Time> drawredafter; Time diff = actual_time - actual_dragtime;//selected_time-drag_time; for(; i != end; ++i) { //find the coordinate in the drawable space... Time t_orig = i->get_time(); if(!t_orig.is_valid()) continue; Time t = t_orig - time_offset; if (time_dilation!=0) t = t / time_dilation; if(t<adjustment->get_lower() || t>adjustment->get_upper()) continue; //if it found it... (might want to change comparison, and optimize // sel_times.find to not produce an overall nlogn solution) bool selected=false; //not dragging... just draw as per normal //if move dragging draw offset //if copy dragging draw both... if(valselected && sel_times.find(t_orig) != sel_times.end()) { if(dragging) //skip if we're dragging because we'll render it later { if(mode & COPY_MASK) // draw both blue and red moved { drawredafter.push_back(t + diff.round(cfps)); }else if(mode & DELETE_MASK) //it's just red... { selected=true; }else //move - draw the red on top of the others... { drawredafter.push_back(t + diff.round(cfps)); continue; } }else { selected=true; } } //synfig::info("Displaying time: %.3f s",(float)t); const int x = (int)((t-lower)*area.get_width()/(upper-lower)); //should draw me a grey filled circle... Gdk::Rectangle area2( area.get_x() - area.get_height()/2 + x + 1, area.get_y() + 1, area.get_height()-2, area.get_height()-2 ); if (time_dilation!=0) { TimePoint tp = *i; tp.set_time((tp.get_time() - time_offset) / time_dilation); render_time_point_to_window(cr,area2,tp,selected); } } { vector<Time>::iterator i = drawredafter.begin(), end = drawredafter.end(); for(; i != end; ++i) { //find the coordinate in the drawable space... Time t = *i; if(!t.is_valid()) continue; //synfig::info("Displaying time: %.3f s",(float)t); const int x = (int)((t-lower)*area.get_width()/(upper-lower)); //should draw me a grey filled circle... Gdk::Rectangle area2( area.get_x() - area.get_height()/2 + x + 1, area.get_y() + 1, area.get_height()-2, area.get_height()-2 ); render_time_point_to_window(cr,area2,*i,true); } } } } // If the parent of this value node is a dynamic list, then // render the on and off times if(parent_value_node) { const int index(property_value_desc().get_value().get_index()); const synfig::ValueNode_DynamicList::ListEntry& list_entry(parent_value_node->list[index]); const synfig::ValueNode_DynamicList::ListEntry::ActivepointList& activepoint_list(list_entry.timing_info); synfig::ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator iter,next; bool is_off(false); if(!activepoint_list.empty()) is_off=!activepoint_list.front().state; int xstart(0); int x=0 /*,prevx=0*/; for(next=activepoint_list.begin(),iter=next++;iter!=activepoint_list.end();iter=next++) { x=((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(iter->time-adjustment->get_lower()))); if(x<0)x=0; if(x>area.get_width())x=area.get_width(); bool status_at_time=0; if(next!=activepoint_list.end()) { status_at_time=!list_entry.status_at_time((iter->time+next->time)/2.0); } else status_at_time=!list_entry.status_at_time(Time::end()); if(!is_off && status_at_time) { xstart=x; is_off=true; } else if(is_off && !status_at_time) { // render the off time has a dashed line draw_activepoint_off(cr, inactive_color, area.get_height()*2, area.get_x()+xstart, area.get_y(), x-xstart, area.get_y()); is_off=false; } if(iter->time>=adjustment->get_lower() && iter->time<adjustment->get_upper()) { int w(1); if(selected==*iter) w=3; cr->set_source_rgb( activepoint_color[iter->state].get_red_p(), activepoint_color[iter->state].get_green_p(), activepoint_color[iter->state].get_red_p() ); cr->rectangle(area.get_x()+x-w/2, area.get_y(), w, area.get_height()); cr->fill(); } //prevx=x; } if(is_off) { // render the off time has a dashed line draw_activepoint_off(cr, inactive_color, area.get_height()*2, area.get_x()+xstart, area.get_y(), area.get_width()-xstart, area.get_y()); } } // Render a line that defines the current tick in time { const int x((int)((float)area.get_width()/(adjustment->get_upper()-adjustment->get_lower())*(adjustment->get_value()-adjustment->get_lower()))); if(adjustment->get_value()>=adjustment->get_lower() && adjustment->get_value()<adjustment->get_upper()) { cr->set_source_rgb( curr_time_color.get_red_p(), curr_time_color.get_green_p(), curr_time_color.get_red_p() ); cr->rectangle(area.get_x()+x, area.get_y(), 1, area.get_height()); cr->fill(); } } }
inline void print_ms(const TimePoint<Ms>& time_point) { std::cout << time_point.time_since_epoch().count() << " ms\n"; }