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

Crash: Query.nativeFindFirst(Native method) #494

Closed
LeeKingHung opened this issue Jul 6, 2018 · 14 comments
Closed

Crash: Query.nativeFindFirst(Native method) #494

LeeKingHung opened this issue Jul 6, 2018 · 14 comments

Comments

@LeeKingHung
Copy link

LeeKingHung commented Jul 6, 2018

I got a crash from the method query.build().findFirst(). It does not happen in emulator and modern device, but always (from what I observed) happened in older device.

Any clue?

Issue Basics

  • ObjectBox version (are using the latest version?): 1.5.0
  • Reproducibility: On old devices (tried on Android OS 5.0)
A/art: art/runtime/thread_list.cc:170] "MyHandlerThread" prio=5 tid=38 Native
A/art: art/runtime/thread_list.cc:170]   | group="main" sCount=1 dsCount=0 obj=0x12f12cf0 self=0xaf491800
A/art: art/runtime/thread_list.cc:170]   | sysTid=8572 nice=0 cgrp=apps sched=0/0 handle=0xaf4a7080
A/art: art/runtime/thread_list.cc:170]   | state=S schedstat=( 965853050 120766065 537 ) utm=91 stm=5 core=1 HZ=100
A/art: art/runtime/thread_list.cc:170]   | stack=0x81d40000-0x81d42000 stackSize=1036KB
A/art: art/runtime/thread_list.cc:170]   | held mutexes=
A/art: art/runtime/thread_list.cc:170]   native: #00 pc 00010110  /system/lib/libc.so (syscall+28)
A/art: art/runtime/thread_list.cc:170]   native: #01 pc 000aa127  /system/lib/libart.so (art::ConditionVariable::Wait(art::Thread*)+82)
A/art: art/runtime/thread_list.cc:170]   native: #02 pc 000b48b3  /system/lib/libart.so (art::ScopedCheck::ScopedCheck(_JNIEnv*, int, char const*)+2434)
A/art: art/runtime/thread_list.cc:170]   native: #03 pc 000b649b  /system/lib/libart.so (art::CheckJNI::NewObjectV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+26)
A/art: art/runtime/thread_list.cc:170]   native: #04 pc 000780ab  /data/app/com.something-1/lib/arm/libobjectbox.so (???)
A/art: art/runtime/thread_list.cc:170]   native: #05 pc 00079ff7  /data/app/com.something-1/lib/arm/libobjectbox.so (???)
A/art: art/runtime/thread_list.cc:170]   native: #06 pc 000768b9  /data/app/com.something-1/lib/arm/libobjectbox.so (???)
A/art: art/runtime/thread_list.cc:170]   native: #07 pc 0007d205  /data/app/com.something-1/lib/arm/libobjectbox.so (Java_io_objectbox_query_Query_nativeFindFirst+40)
A/art: art/runtime/thread_list.cc:170]   native: #08 pc 00a84bff  /data/dalvik-cache/arm/data@app@com.something@base.apk@classes.dex (Java_io_objectbox_query_Query_nativeFindFirst__JJ+110)
A/art: art/runtime/thread_list.cc:170]   at io.objectbox.query.Query.nativeFindFirst(Native method)
A/art: art/runtime/thread_list.cc:170]   at io.objectbox.query.Query$1.call(Query.java:178)
A/art: art/runtime/thread_list.cc:170]   at io.objectbox.BoxStore.callInReadTx(BoxStore.java:703)
A/art: art/runtime/thread_list.cc:170]   at io.objectbox.BoxStore.callInReadTxWithRetry(BoxStore.java:630)
A/art: art/runtime/thread_list.cc:170]   at io.objectbox.query.Query.callInReadTx(Query.java:304)
A/art: art/runtime/thread_list.cc:170]   at io.objectbox.query.Query.findFirst(Query.java:174)
@greenrobot
Copy link
Member

Thanks for reporting. Do you have some details on which devices (brand and model) this happens?

@greenrobot
Copy link
Member

Also, would it be possible for you to extract the offending code into a demo project? That would help us immensely.

@LeeKingHung
Copy link
Author

It happened on Samsung Note 3 (OS 5). It seemed no problem on OS 6 and above.

@LeeKingHung
Copy link
Author

LeeKingHung commented Jul 9, 2018

The code is quite complicated......... There are 3 classes below.
Note: The crash occurred in RelationSetup.getFieldValue().

RelationSetup class

public class RelationSetup {

	static final int DB_LIMIT = 2000;

	final BoxStore mBoxStore;
	final HashMap< Class, Property[] > mTargetProperties = new HashMap<>();    // filter properties
	final int mMaxThreads;

	public RelationSetup ( final BoxStore boxStore ) {
		mBoxStore = boxStore;
		mMaxThreads = Math.max( 2, Runtime.getRuntime().availableProcessors() );
		mTargetProperties.put( Location.class, new Property[] { Location_.mServerId } );
		...
	}

	// Start point
	public void setRelation ( ) {	
		final EntityInfo entityInfo = Asset_.__INSTANCE;
		final Property propertyIsRelationSet = Asset_.mIsRelationsSet;
		final Query query = getData( entityInfo, propertyIsRelationSet );		
		final long count = query.count();
		setRelation( entityInfo, query, count );
	}

	Query getData ( final EntityInfo entityInfo, final Property property ) {
		final Box box = mBoxStore.boxFor( entityInfo.getEntityClass() );
		final Property propertyId = entityInfo.getIdProperty();
		return box.query().equal( property, false ).or().isNull( property ).greater( propertyId, -1 ).build();
	}

	< V extends Model & RelationSetupStatus > void setRelation ( final EntityInfo< V > entityInfo, final Query< V > query, final long count ) {

		final Class< V > c = entityInfo.getEntityClass();
		final Wrapper< Long > lastId = new Wrapper<>( 0L );  // Wrapper is just an object containing another object
		final Property propertyId = entityInfo.getIdProperty();
		final int numLoops = Math.min( mMaxThreads, ( count + DB_LIMIT - 1 ) / DB_LIMIT  );

		for ( int i = 0 ; i < numLoops ; i++ ) { // set up relation in multiple threads

			mBoxStore.internalScheduleThread( () -> {

				Process.setThreadPriority( Process.THREAD_PRIORITY_MORE_FAVORABLE );

				mBoxStore.runInReadTx( () -> {

					while ( true ) {

						final List< V > data;

						synchronized ( lastId ) {  // Only one call at a time to ensure that the data will not be repeated in different threads
							data = query.setParameter( propertyId, lastId.mObject ).find( 0, DB_LIMIT );
							if ( data.size() == 0 ) break;
							lastId.mObject = data.get( data.size() - 1 ).getId();
						}

						List< V > toBeSaved = new ArrayList<>();

						for ( final V v : data ) {
							if ( setRelation( v ) ) toBeSaved.add( v );
						}

						final Runnable runnable = () -> {
							Process.setThreadPriority( Process.THREAD_PRIORITY_MORE_FAVORABLE - 1 );
							mBoxStore.boxFor( c ).put( toBeSaved );
						};

						mBoxStore.runInTxAsync( runnable, null );

					}

				} );

			} );

		}

	}

	// return true if save of v is needed
	public < V extends RelationSetupStatus > boolean setRelation ( final V v ) {

		v.setRelationSetupStatus( true );
		boolean isSaveNeeded = false;

		try {
			v.setupRelations( this );
		} catch ( final Throwable e ) {
			v.setRelationSetupStatus( false );
		}

		return isSaveNeeded || v.isRelationsSet();

	}

	public < V extends RelationSetupStatus, W > W getFieldValue ( final V v, final Object[] filters, final Class< W > classRelation ) throws Throwable {

		final Property[] propertyTargetKeys = mTargetProperties.get( classRelation );
		final W w;
		final Box< W > boxW = mBoxStore.boxFor( classRelation );
		final QueryBuilder< W > query = boxW.query();

		for ( int i = 0 ; i < filters.length ; i++ ) {
			final Object key = filters[ i ];
			if ( key instanceof String ) query.equal( propertyTargetKeys[ i ], ( String ) key );
			else query.equal( propertyTargetKeys[ i ], ( long ) key );
		}

		w = query.build().findFirst();   // This line throws the error  (Note: only Lollipop will have such problem, Kitkat and below not tried, OS 6 and above OK)
		return w;
		
	}

}

@LeeKingHung
Copy link
Author

Asset class (the entity)

@Entity
public class Asset implements RelationSetupStatus {

	...
	Boolean mIsRelationsSet;
	String mLocationDescription;
	String mLocation;

	public Asset () { }
	
	...

	// =============================================================================================================
	// Relation Setup
	// =============================================================================================================

	@Override
	public Boolean isRelationsSet () {
		return mIsRelationsSet;
	}

	@Override
	public void setRelationSetupStatus ( final boolean isAllSet ) {
		mIsRelationsSet = isAllSet;
	}

	@Override
	public void setupRelations ( final RelationSetup setup ) throws Throwable {
		mLocationDescription = setupStringRelation( setup, mLocationDescription, Location.class, mLocation );   // check RelationSetupStatus class
	}

}

@LeeKingHung
Copy link
Author

RelationSetupStatus

public interface RelationSetupStatus {

	void setRelationSetupStatus ( final boolean isAllSet );

	Boolean isRelationsSet ();

	void setupRelations ( final RelationSetup setup ) throws Throwable;

	default < W > String setupStringRelation ( final RelationSetup setup, final String field, final Class< W > objectClass, final Object... filters ) throws Throwable {

		final W w = setup.getFieldValue( this, filters, objectClass );

		if ( w == null ) {
			setRelationSetupStatus( false );
			return null;
		}

		return w.toString();

	}

}

@LeeKingHung
Copy link
Author

I removed multi threads, runInReadTx, runInTxAsync and synchronized, and it seemed worked. Maybe there was a dead lock. But it was not happening in OS 6 and above.

@LeeKingHung
Copy link
Author

LeeKingHung commented Jul 9, 2018

Just a question: is it allowed to use the same query object in multiple read transactions ?

@greenrobot
Copy link
Member

I removed multi threads, runInReadTx, runInTxAsync and synchronized, and it seemed worked.

Does that also apply to the older device where it initially crashed?

is it allowed to use the same query object in multiple read transactions ?

As long you synchronize all the calls to setParameter and find you should be fine.

@LeeKingHung
Copy link
Author

LeeKingHung commented Jul 9, 2018

Does that also apply to the older device where it initially crashed?

Yes.

I changed the codes about the multi-threading and it is now working. I think the error came from the threading instead of ObjectBox.

Sorry for spending your time.

@greenrobot
Copy link
Member

I changed the codes about the multi-threading and it is now working. I think the error came from the threading instead of ObjectBox.

Even so, it should not crash like this. We'll keep an eye on it. Also if you have additional info, please share.

@LeeKingHung
Copy link
Author

I think the crash is due to the thread timeout, probably it was waiting for another thread to finish (I have a lot of threads running at the same time).

@NBXXF
Copy link

NBXXF commented May 5, 2022

#5 pc 0003a381 /data/app/com.next.space.cflow-gmt3IJl8WdffrgTa2TYXpg==/base.apk!/lib/armeabi-v7a/libobjectbox-jni.so (Java_io_objectbox_query_Query_nativeFindFirst+28)
华为畅享10e 机型上出现

@greenrobot-team
Copy link
Member

@NBXXF This issue is rather outdated. Can you please create a new one and fill out the bug report template with as much information as possible?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants