Modify

Opened 5 years ago

Last modified 3 months ago

#6638 assigned Bugs

convert_aux fails while intializing global variable

Reported by: john doe <johndoe> Owned by: Beman Dawes
Milestone: To Be Determined Component: filesystem
Version: Boost 1.59.0 Severity: Problem
Keywords: filesystem path convert wide/narrow string Cc:

Description

path_traits.cpp

convert_aux fails in global scope:

Trying to initialize global path variable by concatenating '/' a wide string path w/ narrow string path fails!

"Unhandled exception at 0x0f8bc688 (msvcp100d.dll) in paths.exe: 0xC0000005: Access violation reading location 0x00000000."

If initializing inside main() scope everything works fine. I think this worked as expected in previous version(s) 1.48.. no luck with 1.49 though :(

Additional info: WIN 32 and 64 bit, MSVS2010

#include <iostream>
#include <boost/filesystem.hpp>

using namespace boost::filesystem;

path p(L"C:\\TEMP\\");
path q(p / L"wide");  // Works!
path r(p / "narrow"); // Breaks :(

int _tmain(int argc, _TCHAR* argv[])
{
    path p(L"C:\\TEMP\\");
    path q(p / L"wide");  // Works!
    path r(p / "narrow"); // Works here!

    std::cout << r.string() << std::endl;
	return 0;
}


P.S. If more info is required I will reply here not by email.

Attachments (2)

CallStack.txt (2.3 KB) - added by john doe <johndoe> 5 years ago.
Full stack trace
paths.cpp (493 bytes) - added by john doe <johndoe> 5 years ago.

Download all attachments as: .zip

Change History (15)

Changed 5 years ago by john doe <johndoe>

Attachment: CallStack.txt added

Full stack trace

Changed 5 years ago by john doe <johndoe>

Attachment: paths.cpp added

comment:1 Changed 5 years ago by john doe <johndoe>

Keywords: filesystem path convert wide/narrow string added

comment:2 Changed 5 years ago by Fraser Hutchison <fraser.hutchison@…>

It appears to be down to an MSVC bug.

The third path constructor calls codecvt(), which calls wchar_t_codecvt_facet(), which returns codecvt_facet. However, codecvt_facet hasn't been initialised at this point.

I believe the Standard requires codecvt_facet to have been initialised before wchar_t_codecvt_facet() since it comes earlier in the same translation unit, hence I think this is an MSVC bug.

I can confirm that there is no problem with this in 1.48.0 - it's moving codecvt_facet from being a static member to a variable in unnamed namespace in 1.49.0 that has caused the issue.

The following slightly simpler program gives the error also:

#include "boost/filesystem/v3/path.hpp"
boost::filesystem::path p("p");
int main() { return 0; }

comment:3 Changed 5 years ago by Nils Gladitz <gladitz@…>

I've got the same problem with Intel 11.1 (+MSVC2005).

comment:4 Changed 5 years ago by anonymous

For what it's worth, you can work around this crash bug by reverting changeset 76303. https://svn.boost.org/trac/boost/changeset/76303/trunk/libs/filesystem/v3/src/path.

This change was implemented to fix issue 6320. https://svn.boost.org/trac/boost/ticket/6320

comment:5 Changed 4 years ago by Nils Gladitz <gladitz@…>

The snippet in the description no longer crashes for me in Boost 1.54.0 but now the following (related?) snippet does:

#include <boost/filesystem.hpp>

using namespace boost::filesystem;

class Test
{
public:
	~Test()
	{
		path p(L"C:\\TEMP\\");
		path r(p / "narrow");
	}
};

Test test1;
Test test2;

int main()
{
	
}

comment:6 Changed 2 years ago by Andrei Ortolan - andrei-fsnt@…

Me too in boost_1_57_0_32_b_vs10

CheckCreateDir?(".\test\test\");

bool CheckCreateDir?(std::string path){

try{

if (boost::filesystem::exists(path))

return true;

} catch (const boost::filesystem::filesystem_error& ex){

std::cout << "\n EX " << ex.what(); return false;

}

bool res; try{

res = boost::filesystem::create_directories(path);

} catch (...){

return false;

} return res;

}

comment:7 Changed 23 months ago by Beman Dawes

Status: newassigned
Version: Boost 1.49.0Boost 1.59.0

The original problem and the example from Fraser Hutchison appear to have been corrected some time ago.

The code supplied by Andrei Ortolan works fine for me, once the missing backslashes are added.

The code supplied by Nils Gladitz (Test test1; Test test2;) fails for me on static builds. It is not failing for me for shared builds. Tests run only on Windows.

There is a simple workaround; add path p("narrow"; before the two Test variables, and it works fine.

I'd like to fix the underlying problem, but have not come up with anything workable. So it will stay open for a while longer.

Thanks,

--Beman

comment:8 Changed 3 months ago by anonymous

Hi things got worse with this error. I beleive this worked with 1.63.

Config:

boost-1.64.0, vc14, x64, debug, static lib, statically linked runtime

Error:

Exception thrown: read access violation.

this->_Ptr->_Facetvec was 0x111011101110111.

If there is a handler for this exception, the program may be safely continued.

Code:

class Alma
{
public:
	~Alma() { std::cout << boost::filesystem::path("c:\\alma.txt").string() << std::endl; }
};

static Alma a;

int main(int argc, char* argv[])
{
	return 0;
}

Call stack:

Test.exe!std::locale::_Getfacet(unsigned __int64 _Id) Line 459	C++
Test.exe!std::use_facet<std::codecvt<wchar_t,char,_Mbstatet> >(const std::locale & _Loc) Line 564	C++
Test.exe!boost::filesystem::path::codecvt() Line 940	C++
Test.exe!boost::filesystem::path_traits::convert(const char * from, const char * from_end, std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > & to) Line 981	C++
Test.exe!boost::filesystem::path_traits::dispatch<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > >(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & c, std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > & to) Line 256	C++
Test.exe!boost::filesystem::path::path<char [12]>(const char[12] & source, void * __formal) Line 144	C++
Test.exe!Alma::~Alma() Line 88	C++
[External Code]	
Test.exe!_execute_onexit_table::__l22::<lambda>() Line 198	C++
Test.exe!__crt_seh_guarded_call<int>::operator()<void <lambda>(void),int <lambda>(void) & __ptr64,void <lambda>(void) >(__acrt_lock_and_call::__l3::void <lambda>(void) && setup, _execute_onexit_table::__l22::int <lambda>(void) & action, __acrt_lock_and_call::__l4::void <lambda>(void) && cleanup) Line 199	C++
Test.exe!__acrt_lock_and_call<int <lambda>(void) >(const __acrt_lock_id lock_id, _execute_onexit_table::__l22::int <lambda>(void) && action) Line 882	C++
Test.exe!_execute_onexit_table(_onexit_table_t * table) Line 222	C++
Test.exe!common_exit(const int return_code, const _crt_exit_cleanup_mode cleanup_mode, const _crt_exit_return_mode return_mode) Line 211	C++
Test.exe!exit(int return_code) Line 283	C++

comment:9 Changed 3 months ago by anonymous

I made two error in my report:

There has to be an instantiation of the test class in the main, too:

class Alma
{
public:
	~Alma() { std::cout << boost::filesystem::path("c:\\alma.txt").string() << std::endl; }
};

static Alma a;

int main(int argc, char* argv[])
{
	Alma();	//without this there is no crash
	return 0;
}

It is an error with 1.63 as well.

comment:10 Changed 3 months ago by anonymous

This version does not crash (but it does if I removed the constructor code):

class Alma
{
public:
	Alma() { boost::filesystem::exists("C:\\alma.txt"); }
	~Alma() { std::cout << boost::filesystem::path("c:\\alma.txt").string() << std::endl; }
};

Alma a;

int main(int argc, char* argv[])
{
	Alma();
	return 0;
}

comment:11 Changed 3 months ago by Leinad

Here is another crashing variation:

#include <boost\filesystem.hpp>

class Alma
{
public:
	Alma() { /*boost::filesystem::exists("C:\\alma.txt");*/ }
	~Alma() { std::cout << boost::filesystem::path("c:\\alma.txt").string() << std::endl; }
};

class Korte
{
public:
	Korte() { boost::filesystem::exists("C:\\korte.txt"); }
	~Korte() { std::cout << boost::filesystem::path("c:\\korte.txt").string() << std::endl; }
};

Alma a;
Korte k;

int main(int argc, char* argv[])
{
	return 0;
}

Please note that the constructor body of Alma is empty.

I wonder whether we could learn from these examples.

In this case first a, then the static std local and facet stuff, then k created. Then at exit k destructed, then the static std local and facet stuff, then a. When a is destructed all the facet stuff is gone and that is why it crashes. Wouldn't it be better that in the path.hpp (or where it is appropriate) we had a static std::local instance instead of creating it lazily in the std::locale& path_locale() function?

comment:12 Changed 3 months ago by Leinad

I tried it and it seems to work.

In path.cpp in the unnamed namespace (the last one for "local helpers") I added std::locale def_locale = default_locale();. Also in the unnamed namespace I altered std::locale& path_locale(): it simply returns the newly added def_locale. That's it. No crash any more.

I hope this helps to close this 5-year old bug.

comment:13 Changed 3 months ago by Leinad

Well, it seems to work, but...

  1. It would not address the concerns about exceptions might be thrown on POSIX systems.
  2. I am not at all sure about that the static initialisation that the previous solution relies on always happens in that order.

There is a little trick though that probably solves all our problems.

At the end of path.hpp we could create a static instance of a reference to codecvt_type in the unnamed namespace:

...
namespace
{
	const boost::filesystem::path::codecvt_type& cvt = boost::filesystem::path::codecvt();
}
...

In this case in each compilation unit where path.hpp included we would have a separate cvt. In a translation unit static initialisation always occurs in the order of the definition of the objects with static storage duration. Calling codecvt() would cause the lazy initialisation of the locale and the facet stuff. After that any static instance that uses codecvt in its destructor would be safe to use:

#include <boost/path.hpp>

class Alma
{
public:
	~Alma() { boost::filesystem::path("c:\\alma.txt").string(); }
};

static Alma a;

int main(int argc, char* argv[])
{
	Alma();	
	return 0;
}

a is created after cvt and destructed before it.

Modify Ticket

Change Properties
Set your email in Preferences
Action
as assigned The owner will remain Beman Dawes.

Add Comment


E-mail address and name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.