date icon indicating copy to clipboard operation
date copied to clipboard

Throw a runtime error instead of terminating

Open kalliste opened this issue 4 years ago • 8 comments

Elsewhere in this file if something goes wrong we throw a runtime error. If we also do that here instead of calling std::terminate we can catch it and fix the calling code.

kalliste avatar Jun 12 '20 23:06 kalliste

This is very difficult logic.

Have you ever had a case where this terminate was called? If so, can you reproduce it? How do you propose that the calling code do when this exception is caught? Or if uncaught, what can the programmer change when discovering the uncaught exception?

HowardHinnant avatar Jun 12 '20 23:06 HowardHinnant

I had a case where terminate was called. It has happened multiple times for end users of my program but I haven't reproduced it yet. I may be able to.

I think probably the cause was that I was calling make_zoned with either NULL or a string that was not a valid timezone in this code:

struct tm t = pAlert->m_TimeStart;                                                                                                                                                                                                                                                                                            
auto system = sys_days{year{t.tm_year + 1900}/(t.tm_mon+1)/t.tm_mday} + hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec};
auto local = make_zoned(szCtlrTimezone, system);
auto lt = local.get_local_time();
auto ld = floor<days>(lt);
time_of_day<seconds> tod{lt - ld};                  
year_month_day ymd{ld};

switch (iIntID)
{
    case SF_INT_ID2_NOTIFICATION_YEAR_LOCAL:          return int{ymd.year()};
    case SF_INT_ID2_NOTIFICATION_MONTH_LOCAL:         return unsigned{ymd.month()};
    case SF_INT_ID2_NOTIFICATION_DAY_LOCAL:           return unsigned{ymd.day()};
    case SF_INT_ID2_NOTIFICATION_HOUR_LOCAL:          return tod.hours().count();
    case SF_INT_ID2_NOTIFICATION_MINUTE_LOCAL:        return tod.minutes().count();
}

Currently I am wrapping this whole section in a try/catch so if it throws instead of terminates then I can log what I was doing when I screwed up

kalliste avatar Jun 13 '20 00:06 kalliste

If you pass in a bad time zone name, you'll get an exception with a .what() that says something like: "bad_name not found in timezone database", where "bad_name" is the name you passed in.

So NULL sounds like a more likely culprit. I get a segfault with that. But it didn't make it to the terminate() you propose changing. But guarding against that NULL sounds reasonable.

HowardHinnant avatar Jun 13 '20 00:06 HowardHinnant

I've added checks to the zoned_time constructors to check for nullptr and throw if found: https://github.com/HowardHinnant/date/commit/d544e5af2536bf5319367cb7dafe6928d4205cd2

HowardHinnant avatar Jun 14 '20 23:06 HowardHinnant

I did encounter an assertion error when trying to query the TZDB for dates earlier than the minimum year.

dkhalanskyjb avatar Aug 14 '20 09:08 dkhalanskyjb

If you've got a test case I'd be happy to explore it. I did this and got garbage, but no assert:

#include "date/tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;

    zoned_time zt{current_zone(), sys_days{} - years{50'000}};
    auto lt = zt.get_local_time();
    cout << lt << '\n';
}

HowardHinnant avatar Aug 14 '20 13:08 HowardHinnant

Sure. With the current master,

current_zone()->get_info(local_seconds(seconds(-4266666666664)));

crashes for me with

src/tz.cpp:2128: date::sys_info date::time_zone::load_sys_info(std::vector<date::detail::transition>::const_iterator) const: Assertion `i != transitions_.begin()' failed.

I encountered this on both Linux and Mac.

On Linux, I compile the reproducer with these flags:

-lpthread -std=c++17 -DUSE_OS_TZDB=1

dkhalanskyjb avatar Aug 17 '20 08:08 dkhalanskyjb

Thanks much @dkhalanskyjb ! Fixed with https://github.com/HowardHinnant/date/commit/658a3b94955d876675caca033fb799f5169a389c

HowardHinnant avatar Aug 18 '20 01:08 HowardHinnant