agrosner / DBFlow

A blazing fast, powerful, and very simple ORM android database library that writes database code for you.
MIT License
4.87k stars 598 forks source link

DeadLock when call save in thread and transaction at the same time. #1364

Closed olbb closed 7 years ago

olbb commented 7 years ago

DBFlow Version: 4.0.3 Description: Simple way to reproduce the problem like this:

  private void testDeadLock() {
          final SimpleModule module = new SimpleModule();
          module.name = "module1";
          module.time = System.currentTimeMillis();
          module.save();

          Thread thread = new Thread(new Runnable() {
              @Override
              public void run() {
                  Log.d(TAG, "save start");
                  module.time = System.currentTimeMillis();
                  module.save();
                  Log.d(TAG, "save finish");
              }
          });
          thread.start();
          FlowManager.getDatabase(SimpleDataBase.class).executeTransaction(new ITransaction() {
              @Override
              public void execute(DatabaseWrapper databaseWrapper) {
                  Log.d(TAG, "save in transaction start");
                  module.time = System.currentTimeMillis();
                  module.save();
                  Log.d(TAG, "save in transaction finish");
              }
          });
      }

Then

07-11 10:35:25.458 4410-4494/? D/MainActivity: save start
07-11 10:35:25.459 4410-4410/? D/MainActivity: save in transaction start
07-11 10:35:55.473 4410-4494/com.example.myapplication W/SQLiteConnectionPool: The connection pool for database '/data/user/0/com.example.myapplication/databases/simple.db' has been unable to grant a connection to thread 2331 (Thread-2) with flags 0x1 for 30.000002 seconds.
                                                                                     Connections: 0 active, 1 idle, 0 available.
07-11 10:36:25.474 4410-4494/com.example.myapplication W/SQLiteConnectionPool: The connection pool for database '/data/user/0/com.example.myapplication/databases/simple.db' has been unable to grant a connection to thread 2331 (Thread-2) with flags 0x1 for 60.001003 seconds.
                                                                                     Connections: 0 active, 1 idle, 0 available.
qianlvable commented 7 years ago

Same issue here,please suggest a fix

simon1867 commented 7 years ago

I've been having the same issue. By looking at ANRs on the playstore I can see that they happened infrequently when I was using 4.0.0-beta 5. I had to update DBFlow when beta5 broke on Jitpack. Ever since I've shipped with beta 7 I went from having 1-5 deadlocks a day to about 25 - 50 a day.

In testing so far I haven't been able to reproduce the dead lock issue when I use .async() before calling save().

kutukoff commented 7 years ago

Same issue too!

agrosner commented 7 years ago

Run the operations on the same thread. dont do DB ops on different threads for a start.

agrosner commented 7 years ago

FlowManager.getDatabase(...).beginTransactionAsync(...).build().execute()

agrosner commented 7 years ago

https://agrosner.gitbooks.io/dbflow/content/StoringData.html under Async transactions.

simon1867 commented 7 years ago

@agrosner Is there an advantage to using an async transaction over calling save() on an AsyncModel, or would they both do the same thing?

agrosner commented 7 years ago

same thing. transaction more advantageous because you can do more within a transaction and itll overall be more efficient. Like for example:

list.forEach { it.async().save() }

Will create a transaction for each object and execute them in parallel.


FlowManager.getDatabase(MyDatabase::class.java).beginTransactionAsync { db ->
  list.forEach { it.save(db) }
}.build().execute()

which will do only one transaction for any number of models.

olbb commented 7 years ago

But in some times cannot access run the operations on the same thread elegant.Like this, I got some records from the server in a background thread. At the same time I handle some user event from the UI thread, and should update the record.Although I can let them execution in a same thread, but this made code less readable. Do you have any suggestions?

agrosner commented 7 years ago

if you need single model updates to happen youll have to be reactive to the changes, you can attach a model changed listener to an AsyncModel and wait for it to return then update your UI. For readability its tough to tell depending on your architecture and also callbacks arent always that readable anyways. Thread safety though is paramount.

olbb commented 7 years ago

thanks