{"action":"closed","issue":{"url":"https://api.github.com/repos/Raizlabs/DBFlow/issues/183","labels_url":"https://api.github.com/repos/Raizlabs/DBFlow/issues/183/labels{/name}","comments_url":"https://api.github.com/repos/Raizlabs/DBFlow/issues/183/comments","events_url":"https://api.github.com/repos/Raizlabs/DBFlow/issues/183/events","html_url":"https://github.com/Raizlabs/DBFlow/issues/183","id":66987193,"number":183,"title":"Calling save() Inside transact() May Deadlock","user":{"login":"dylanrjames","id":1726731,"avatar_url":"https://avatars.githubusercontent.com/u/1726731?v=3","gravatar_id":"","url":"https://api.github.com/users/dylanrjames","html_url":"https://github.com/dylanrjames","followers_url":"https://api.github.com/users/dylanrjames/followers","following_url":"https://api.github.com/users/dylanrjames/following{/other_user}","gists_url":"https://api.github.com/users/dylanrjames/gists{/gist_id}","starred_url":"https://api.github.com/users/dylanrjames/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dylanrjames/subscriptions","organizations_url":"https://api.github.com/users/dylanrjames/orgs","repos_url":"https://api.github.com/users/dylanrjames/repos","events_url":"https://api.github.com/users/dylanrjames/events{/privacy}","received_events_url":"https://api.github.com/users/dylanrjames/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Raizlabs/DBFlow/labels/bug","name":"bug","color":"fc2929"}],"state":"closed","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/Raizlabs/DBFlow/milestones/6","html_url":"https://github.com/Raizlabs/DBFlow/milestones/2.0.0","labels_url":"https://api.github.com/repos/Raizlabs/DBFlow/milestones/6/labels","id":987037,"number":6,"title":"2.0.0","description":null,"creator":{"login":"agrosner","id":2117859,"avatar_url":"https://avatars.githubusercontent.com/u/2117859?v=3","gravatar_id":"","url":"https://api.github.com/users/agrosner","html_url":"https://github.com/agrosner","followers_url":"https://api.github.com/users/agrosner/followers","following_url":"https://api.github.com/users/agrosner/following{/other_user}","gists_url":"https://api.github.com/users/agrosner/gists{/gist_id}","starred_url":"https://api.github.com/users/agrosner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/agrosner/subscriptions","organizations_url":"https://api.github.com/users/agrosner/orgs","repos_url":"https://api.github.com/users/agrosner/repos","events_url":"https://api.github.com/users/agrosner/events{/privacy}","received_events_url":"https://api.github.com/users/agrosner/received_events","type":"User","site_admin":false},"open_issues":11,"closed_issues":16,"state":"open","created_at":"2015-02-19T16:57:43Z","updated_at":"2015-05-06T15:49:57Z","due_on":null,"closed_at":null},"comments":0,"created_at":"2015-04-07T20:48:52Z","updated_at":"2015-05-06T15:49:57Z","closed_at":"2015-05-06T15:49:57Z","body":"I've run into an issue where I had some code that ends up trying to concurrently update the same model table via nested objects. Since one of the `save()` calls is wrapped in a `transact()`, there is a certain sequence of events which sometimes causes a deadlock. For context, I have model objects being stored that look something like:\r\n\r\n```\r\npublic class MyInnerObject {\r\n     // a bunch of fields\r\n}\r\n\r\npublic class MyOuterObject {\r\n    private MyInnerObject innerObject;\r\n}\r\n```\r\n\r\nWhen the deadlock happens, I get the following message in logcat:\r\n\r\n```\r\nW/SQLiteConnectionPool﹕ The connection pool for database '/data/data/com.myapp/databases/AppDatabase.db' has been unable to grant a connection to thread 1 (main) with flags 0x5 for 60.002003 seconds.\r\n    Connections: 0 active, 1 idle, 0 available.\r\n```\r\n\r\nAt this point if I have the debugger attached and suspend all threads, I will find one thread waiting for the synchronized lock on the `ModelAdapter` for my inner object, with a stack trace like:\r\n\r\n```\r\nThread-1@9999, prio=5, in group 'main', status: 'MONITOR'\r\n\t waiting for Thread-1@9999 to release lock on <0x132a> (a com.myApp.MyInnerModel$Adapter)\r\n\t  at com.raizlabs.android.dbflow.structure.ModelAdapter.save(ModelAdapter.java:61)\r\n\t  at com.raizlabs.android.dbflow.structure.BaseModel.save(BaseModel.java:54)\r\n\t  at com.myApp.MyOuterModel$Adapter.bindToContentValues(MyOuterModel$Adapter.java:85)\r\n\t  at com.myApp.MyOuterModel$Adapter.bindToContentValues(MyOuterModel$Adapter.java:12)\r\n\t  at com.raizlabs.android.dbflow.sql.SqlUtils.update(SqlUtils.java:344)\r\n\t  at com.raizlabs.android.dbflow.sql.SqlUtils.save(SqlUtils.java:312)\r\n\t  at com.raizlabs.android.dbflow.structure.ModelAdapter.save(ModelAdapter.java:61)\r\n\t  at com.raizlabs.android.dbflow.structure.BaseModel.save(BaseModel.java:54)\r\n\t  at com.raizlabs.android.dbflow.structure.cache.BaseCacheableModel.save(BaseCacheableModel.java:82)\r\n\t  at com.myApp.MyOuterModelManager$1.run(MyOuterModelManager.java:99)\r\n\t  at com.raizlabs.android.dbflow.runtime.TransactionManager.transact(TransactionManager.java:120)\r\n\t  at com.raizlabs.android.dbflow.runtime.TransactionManager.transact(TransactionManager.java:109)\r\n\t  at com.myApp.MyOuterModelManager.store(MyOuterModelManager.java:99)\r\n\t  ...\r\n```\r\n\r\nI will find a second thread which is stuck waiting on the `SQLiteConnectionPool`, which has the following stack trace:\r\n\r\n```\r\nThread-2@9999, prio=5, in group 'main', status: 'WAIT'\r\n\t blocks Thread-2@9999\r\n\t  at java.lang.Object.wait(Object.java:-1)\r\n\t  at java.lang.Thread.parkFor(Thread.java:1220)\r\n\t  at sun.misc.Unsafe.park(Unsafe.java:299)\r\n\t  at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:197)\r\n\t  at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:670)\r\n\t  at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)\r\n\t  at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)\r\n\t  at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:586)\r\n\t  at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)\r\n\t  at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)\r\n\t  at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1574)\r\n\t  at com.raizlabs.android.dbflow.SQLiteCompatibilityUtils.updateWithOnConflict(SQLiteCompatibilityUtils.java:63)\r\n\t  at com.raizlabs.android.dbflow.sql.SqlUtils.update(SqlUtils.java:345)\r\n\t  at com.raizlabs.android.dbflow.sql.SqlUtils.save(SqlUtils.java:312)\r\n\t  at com.raizlabs.android.dbflow.structure.ModelAdapter.save(ModelAdapter.java:61)\r\n\t  at com.raizlabs.android.dbflow.structure.BaseModel.save(BaseModel.java:54)\r\n\t  at com.myApp.MyInnerModelManager.store(MyInnerModelManager.java:99)\r\n\t  ...\r\n```\r\n\r\nThread 1 has started a transaction to make all of the changes to `MyOuterModel` through `save()`, but this `save()` is recursively going into the `MyInnerModel` and trying to do another `save()`. Thread 2 has come in and tried to call `save()` with a set of `MyInnerModel` updates. Thread 2 has made it far enough to take the `save()` lock for `MyInnerModel`, but it is unable to perform the SQL operations since Thread 1 has not ended its transaction. Thread 1 is unable to finish its transaction because it cannot perform the `save()` call on `MyInnerObject` since Thread 2 is holding the lock. As such the database deadlocks and I can no longer access or write to the database."}}