Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<chrono>: Possible way forward to avoid tzdb's memory leak #2504

Open
mnatsuhara opened this issue Jan 26, 2022 · 3 comments
Open

<chrono>: Possible way forward to avoid tzdb's memory leak #2504

mnatsuhara opened this issue Jan 26, 2022 · 3 comments
Labels
bug Something isn't working chrono C++20 chrono

Comments

@mnatsuhara
Copy link
Contributor

mnatsuhara commented Jan 26, 2022

[ Copied over from DevCom-1644641 ]

[severity:It’s more difficult to complete my work]

This bug has already been reported at https://developercommunity2.visualstudio.com/t/std::chrono::current_zone-produces-a/1513362 before, but that issue got closed.

Repro Program: chronoleak.cpp:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include <chrono>

int main() {
        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
        _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
        _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
        _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
        _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
        _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
        {
                std::chrono::get_tzdb_list();
        }
        return 0;
}

Compile command: cl /std:c++20 /permissive- /EHsc /MTd chronoleak.cpp

Output see attached leaks.txt (memory leaks reported)

I disagree with the comment in https://developercommunity2.visualstudio.com/t/std::chrono::current_zone-produces-a/1513362#T-N1516296.

I think wrapping inline atomic<tzdb_list*> _Global_tzdb_list; (see https://github.com/microsoft/STL/blob/59a87ccc947ed4ccefd3b87e51b6a7136341c674/stl/inc/chrono#L2852>) in a struct that has a destructor could run the required cleanup. If destruction order on process shutdown is a concern, I think the standard allows throwing if it would be called after the containing object had already been destroyed. See C++20 [time.zone.db.access]:

Throws: runtime_error if for any reason a reference cannot be returned to a valid tzdb_list containing one or more valid tzdbs.

This memory leak blocks adoption of C++20 std::chrono functionality in OpenMPT and libopenmpt (https://github.com/OpenMPT/openmpt/) because the resulting noise makes using the CRT memory leak detector impractical for us. In our case, tzdb is loaded via a call to std::chrono::from_stream() as in:

std::chrono::utc_clock::time_point tp{};
std::istringstream s;
s.imbue(std::locale::classic());
s.str("2022-01-01 23:42");
std::chrono::from_stream(s, "%F %R", tp);

Related Context

Further ABI Considerations

As noted in #2047, we were initially skeptical that we could address the memory leaking issue in (1) a Standard-compliant way and (2) without breaking ABI as <chrono> is ABI-locked under /std:c++20. However, given the suggestion by the author of this DevCom issue along with the cited permission to fail ([time.zone.db.access]), we believe we could fix the memory leaking while remaining Standard-conformant. Further, @StephanTLavavej suggests that perhaps renaming the _Global_tzdb_list variable to something like _Global_tzdb_list_v2 would allow us to make this change without breaking ABI.

Filing this issue to track the need to investigate this potential fix for this issue.

Tracked by VSO-1471620 / AB#1471620 .

@mnatsuhara mnatsuhara added bug Something isn't working chrono C++20 chrono labels Jan 26, 2022
@AlexGuteniev
Copy link
Contributor

Can you also unleak dll handle #2316 ?

@schlzber
Copy link

schlzber commented Mar 15, 2022

Maybe this helps, as for the same reason I added this "cleanup code" during program shutdown (in desctructor of CWinApp derived class in MFC project):

#ifdef _DEBUG
	// to prevent the tzdb allocations from being reported as memory leaks
	std::chrono::get_tzdb_list().~tzdb_list();
#endif

manxorist added a commit to OpenMPT/openmpt that referenced this issue May 27, 2022
… to disable std::chrono date handling completely for MSVC when targeting Windows earlier than Windows 10 1903 due to <microsoft/STL#1911> and <microsoft/STL#2163>.

[Ref] mpt/base/detect_quirks.hpp: Add MPT_LIBCXX_QUIRK_NO_CHRONO_DATE_PARSE tp disable std::chrono for date parsing in VS2022 due to <https://developercommunity.visualstudio.com/t/stdchronoget-tzdb-list-memory-leak/1644641> / <microsoft/STL#2504>.


git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@17377 56274372-70c3-4bfc-bfc3-4c3a0b034d27
manxorist added a commit to OpenMPT/openmpt that referenced this issue Jul 9, 2022
…e designators: UTC, Local, Unspecified.

[Mod] Edit History: When the timezone is Unspecified, dates are now formatted without the UTC timezone signifier 'Z' in ISO8601 format. This affects libopenmpt.
[Mod] IT/MPTM: Dates can now be written in UTC. Loading can decide by looking at m_dwLastSavedWithVersion how to interpret dates. After loading, all dates with Local timezone can be converted into UTC timezone, dates with Unspecified timezone are kept as is. This is guarded by a currently disabled MPT_TIME_UTC_ON_DISK feature flag.
[Mod] MOD: Assume dates are in Local timezone.
[Ref] Edit History: OpenMPT still displays dates in Local timezone, and assumes so also for Unspecified timezone dates.
[Imp] When calculating save time from load time and open duration, properly take Local timezone into account. This uses C++20 tzdb (for C++20 or later), or Windows historic timezone information (for Windows 8 or later), or current Windows timezone information (for Windows XP or later), or C stdlib implementation (otherwise).
[Reg/Fix] On pre-C++20 pre-WinXP builds, Local time stored during DST will now be interpreted as non-DST. This is a fundamental limitation of the C time.h API that does not allow to determine whether a specific Local time has DST active or not from the timestamp alone (or whether it is ambiguous). As no module format stores the isdst flag of struct tm, this information has always been always lost. Timestamp + duration calculation have always been wrong here when crossing DST change. The alternative to this regression/misinterpretation would be to always do it wrong forever.
[Fix] Work-around MSVC tzdb memleak. See <microsoft/STL#2504 (comment)>.
[Ref] mptTime: Remove all <ctime> usage for modern builds.


git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@17651 56274372-70c3-4bfc-bfc3-4c3a0b034d27
@StephanTLavavej
Copy link
Member

I haven't checked, but it might be possible to allocate CRT blocks in these vectors, because they can still be freed via the normal mechanism.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working chrono C++20 chrono
Projects
None yet
Development

No branches or pull requests

4 participants