Java アプリケーションで共有ライブラリを使用するために NDK を使用するアプリケーションがあります。共有ライブラリは下でスレッドを使用し、ライブラリで AttachCurrentThread/DetachCurrentThread を管理するのは非常に面倒なので、boost::thread_specific_ptr を使用して JNIEnv* を管理するクラスを作成しようとしています。問題は、boost::thread_specific_ptr をすべて使用すると、数秒後にアプリケーションがハングすることです。助言がありますか?!?!
更新: 内部的には非常に似ていると思われる 2 番目の方法を追加しましたが、同じ問題があります。gdb スタックトレースもあります。
boost::detail::thread_data_base::~thread_data_base() at thread.cpp:42 0x72e91b74
boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, XXX>, boost::_bi::list1<boost::_bi::value<XXX*> > > >::~thread_data() at thread.hpp:91 0x72d69198
boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, XXX>, boost::_bi::list1<boost::_bi::value<XXX*> > > >::~thread_data() at thread.hpp:91 0x72d691e4
boost::checked_delete<boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, XXX>, boost::_bi::list1<boost::_bi::value<XXX*> > > > >() at checked_delete.hpp:34 0x72d69230
boost::detail::sp_counted_impl_p<boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, XXX>, boost::_bi::list1<boost::_bi::value<XXX*> > > > >::dispose() at sp_counted_impl.hpp:78 0x72d72c54
boost::detail::sp_counted_base::release() at sp_counted_base_spin.hpp:103 0x72c67480
boost::detail::shared_count::~shared_count() at shared_count.hpp:371 0x72c67550
~shared_ptr() at shared_ptr.hpp:328 0x72e91a3c
boost::thread::join_noexcept() at thread.cpp:340 0x72e91a3c
boost::thread::join() at thread.hpp:751 0x72cda274
問題は、i->_M_current が null であるデストラクタにあるようです。実際の問題を確認するためにブースト コードを調べていますが、動きが遅いです。
thread_data_base::~thread_data_base()
{
for (notify_list_t::iterator i = notify.begin(), e = notify.end();
i != e; ++i)
{
i->second->unlock();
i->first->notify_all();
}
for (async_states_t::iterator i = async_states_.begin(), e = async_states_.end();
i != e; ++i)
{
(*i)->make_ready();
}
}
最初の試み
class ThreadJNIEnv
{
private:
bool m_bDetach;
JNIEnv* m_pJavaEnv;
public:
ThreadJNIEnv( ) :
m_pJavaEnv( NULL )
{
LOGI( "Attaching Thread" );
g_pJavaVM->AttachCurrentThread( &m_pJavaEnv, NULL );
m_bDetach = true;
}
ThreadJNIEnv( JNIEnv* pJavaEnv ) :
m_pJavaEnv( pJavaEnv )
{
LOGI( "Attach (main thread): %p ", m_pJavaEnv );
m_bDetach = false;
}
~ThreadJNIEnv( )
{
if( m_bDetach )
{
LOGI( "Detaching Thread" );
g_pJavaVM->DetachCurrentThread( );
}
}
JNIEnv* GetEnv( ) { return m_pJavaEnv; };
};
// Class which manages JNIEnv per thread
boost::thread_specific_ptr<ThreadJNIEnv> g_oJNIEnv;
// Sample thread local data which also hangs the app
boost::thread_specific_ptr<int> g_oValue;
JNIEnv* GetJNIEnv( )
{
// Do we already have a JNIEnv?
ThreadJNIEnv* pJNIEnv = g_oJNIEnv.get( );
if( pJNIEnv == NULL )
{
// Create a new JNIEnv (attach the thread)
g_oJNIEnv.reset( new ThreadJNIEnv( ) );
}
return g_oJNIEnv->GetEnv( );
}
jint JNI_OnLoad( JavaVM* vm, void* reserved )
{
g_pJavaVM = vm;
g_pJavaVM->GetEnv( (void **)&g_pJniEnv, JNI_VERSION_1_6 );
// This is a simple one that also exhibits the problem
g_oValue.reset( new int[10] );
// Add the main thread environment
//g_oJNIEnv.reset( new ThreadJNIEnv( pJavaEnv ) );
LOGI( "JNI_OnLoad called: vm=%p, env=%p", g_pJavaVM, g_pJniEnv );
return JNI_VERSION_1_6;
}
2 回目の試行
void DetachThread( )
{
LOGI( "Detaching Thread" );
g_pJavaVM->DetachCurrentThread( );
}
JNIEnv* GetJNIEnv( )
{
JNIEnv* pJNIEnv = NULL;
int status = g_pJavaVM->GetEnv( (void **)&pJNIEnv, JNI_VERSION_1_6 );
switch( status )
{
case JNI_EDETACHED:
{
LOGI( "Attaching Thread" );
g_pJavaVM->AttachCurrentThread( &pJNIEnv, NULL );
boost::this_thread::at_thread_exit( DetachThread );
}
break;
case JNI_OK:
{
// Everything is ok
}
break;
case JNI_EVERSION:
{
LOGE( "GetEnv: version not supported" );
}
break;
}
LOGI( "GetJNIEnv: %p", pJNIEnv );
return pJNIEnv;
}