Opened 11 years ago

Last modified 5 years 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> 11 years ago.
Full stack trace
paths.cpp (493 bytes ) - added by john doe <johndoe> 11 years ago.

Download all attachments as: .zip

Change History (15)

by john doe <johndoe>, 11 years ago

Attachment: CallStack.txt added

Full stack trace

by john doe <johndoe>, 11 years ago

Attachment: paths.cpp added

comment:1 by john doe <johndoe>, 11 years ago

Keywords: filesystem path convert wide/narrow string added

comment:2 by Fraser Hutchison <fraser.hutchison@…>, 11 years ago

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 by Nils Gladitz <gladitz@…>, 11 years ago

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

comment:4 by anonymous, 11 years ago

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 by Nils Gladitz <gladitz@…>, 9 years ago

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 by Andrei Ortolan - andrei-fsnt@…, 7 years ago

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 by Beman Dawes, 7 years ago

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 by anonymous, 5 years ago

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 by anonymous, 5 years ago

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 by anonymous, 5 years ago

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 by Leinad, 5 years ago

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 by Leinad, 5 years ago

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 by Leinad, 5 years ago

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.

Note: See TracTickets for help on using tickets.