1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.room
17 
18 import androidx.annotation.RestrictTo
19 import androidx.sqlite.db.SupportSQLiteStatement
20 import java.util.concurrent.atomic.AtomicBoolean
21 
22 /**
23  * Represents a prepared SQLite state that can be re-used multiple times.
24  *
25  * This class is used by generated code. After it is used, `release` must be called so that it can
26  * be used by other threads.
27  *
28  * To avoid re-entry even within the same thread, this class allows only 1 time access to the shared
29  * statement until it is released.
30  *
31  * @constructor Creates an SQLite prepared statement that can be re-used across threads. If it is in
32  *   use, it automatically creates a new one.
33  */
34 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
35 abstract class SharedSQLiteStatement(private val database: RoomDatabase) {
36     private val lock = AtomicBoolean(false)
37 
<lambda>null38     private val stmt: SupportSQLiteStatement by lazy { createNewStatement() }
39 
40     /**
41      * Create the query.
42      *
43      * @return The SQL query to prepare.
44      */
createQuerynull45     protected abstract fun createQuery(): String
46 
47     protected open fun assertNotMainThread() {
48         database.assertNotMainThread()
49     }
50 
createNewStatementnull51     private fun createNewStatement(): SupportSQLiteStatement {
52         val query = createQuery()
53         return database.compileStatement(query)
54     }
55 
getStmtnull56     private fun getStmt(canUseCached: Boolean): SupportSQLiteStatement {
57         val stmt =
58             if (canUseCached) {
59                 stmt
60             } else {
61                 // it is in use, create a one off statement
62                 createNewStatement()
63             }
64         return stmt
65     }
66 
67     /** Call this to get the statement. Must call [.release] once done. */
acquirenull68     open fun acquire(): SupportSQLiteStatement {
69         assertNotMainThread()
70         return getStmt(lock.compareAndSet(false, true))
71     }
72 
73     /**
74      * Must call this when statement will not be used anymore.
75      *
76      * @param statement The statement that was returned from acquire.
77      */
releasenull78     open fun release(statement: SupportSQLiteStatement) {
79         if (statement === stmt) {
80             lock.set(false)
81         }
82     }
83 }
84