Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 29, 2025

The initPageSize() method had a race condition where concurrent calls to totalSize() or loadPageSize() could execute the PRAGMA query multiple times.

Changes

  • Made m_pageSize field volatile to ensure visibility across threads
  • Added synchronized block with double-checked locking in initPageSize()
  • Use static lock object for synchronization instead of instance-level this
  • Added ConcurrentPageSizeInitialization test to verify thread-safe initialization
private volatile long m_pageSize = -1;
private static final Object PAGE_SIZE_LOCK = new Object();

private void initPageSize() {
    if (m_pageSize == -1) {
        synchronized (PAGE_SIZE_LOCK) {
            if (m_pageSize == -1) {
                // Query PRAGMA page_size
            }
        }
    }
}

The pattern ensures single initialization under concurrent access while maintaining read performance after initialization. Using a static lock object provides better encapsulation and prevents external code from interfering with the synchronization mechanism.

Test Coverage

The new ConcurrentPageSizeInitialization test verifies thread-safe initialization by:

  • Creating 10 threads that simultaneously call loadPageSize() and totalSize()
  • Using CountDownLatch to ensure concurrent execution
  • Verifying all threads receive the same page size value (no race condition)
  • Validating no errors occur during concurrent access
  • Confirming page size is valid (positive and power of 2)

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: anod <171704+anod@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 29, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED -Xmx1536m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-launcher-8.5.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/agents/gradle-instrumentation-agent-8.5.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.5 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Address feedback on updating room version to 2.8.4 Fix thread-safety in initPageSize() using double-checked locking Dec 29, 2025
@anod anod requested a review from Copilot December 29, 2025 13:20
Copilot AI requested a review from anod December 29, 2025 13:20
@anod
Copy link
Member

anod commented Dec 29, 2025

@copilot

  1. query should be able run for multiple thread, only writing of the result is problematic?
  2. Use static object for sync

Co-authored-by: anod <171704+anod@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a race condition in the initPageSize() method where concurrent calls could execute the PRAGMA page_size query multiple times. The fix implements the double-checked locking pattern to ensure thread-safe lazy initialization while maintaining read performance.

  • Made m_pageSize field volatile for proper cross-thread visibility
  • Added synchronized block with double-checked locking in initPageSize()
  • Reorganized exception handling to work correctly within the synchronized block

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 291 to 315

private void initPageSize() {
if (m_pageSize == -1) {
try {
try (Cursor c = m_db.query("PRAGMA page_size", null)) {
if (c.getCount() == 1 && c.getColumnCount() == 1) {
c.moveToFirst();
m_pageSize = c.getLong(0);
} else {
synchronized (PAGE_SIZE_LOCK) {
if (m_pageSize == -1) {
try {
try (Cursor c = m_db.query("PRAGMA page_size", null)) {
if (c.getCount() == 1 && c.getColumnCount() == 1) {
c.moveToFirst();
m_pageSize = c.getLong(0);
} else {
m_pageSize = PAGE_SIZE_DEFAULT;
Log.e("MAE",
String.format("Unexpected result from PRAGMA page_size: %d rows, %d columns",
c.getCount(),
c.getColumnCount()));
}
}
} catch (Exception e) {
m_pageSize = PAGE_SIZE_DEFAULT;
Log.e("MAE",
String.format("Unexpected result from PRAGMA page_size: %d rows, %d columns",
c.getCount(),
c.getColumnCount()));
Log.e("MAE", "Failed to query PRAGMA page_size, using default page size.", e);
}
}
} catch (Exception e) {
m_pageSize = PAGE_SIZE_DEFAULT;
Log.e("MAE", "Failed to query PRAGMA page_size, using default page size.", e);
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thread-safety fix for concurrent initialization lacks test coverage. Consider adding a test that verifies thread-safe initialization by calling totalSize() or loadPageSize() concurrently from multiple threads to ensure the PRAGMA query is only executed once and that no race conditions occur.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ConcurrentPageSizeInitialization test that verifies thread-safe initialization by:

  • Creating 10 threads that simultaneously call loadPageSize() and totalSize()
  • Using CountDownLatch to ensure concurrent execution
  • Verifying all threads receive the same page size value (no race condition)
  • Validating no errors occur during concurrent access
  • Confirming page size is valid (positive and power of 2)

Changes in commit b140ab0.

Copy link
Contributor Author

Copilot AI commented Dec 29, 2025

@copilot

  1. query should be able run for multiple thread, only writing of the result is problematic?
  2. Use static object for sync
    ...

Changed to use a static PAGE_SIZE_LOCK object for synchronization instead of this.

Regarding point 1: The query remains inside the synchronized block to ensure it runs only once. The double-checked locking pattern ensures that after m_pageSize is initialized, subsequent calls skip the synchronized block entirely. If we allowed concurrent query execution, multiple threads could run the PRAGMA query simultaneously, which we want to avoid. The database query itself may be thread-safe, but we only want it to execute once for initialization.

Changes in commit b28b8fb.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED -Xmx1536m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-launcher-8.5.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/agents/gradle-instrumentation-agent-8.5.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.5 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Fix thread-safety in initPageSize() using double-checked locking Fix thread-safety in initPageSize() using double-checked locking with static lock Dec 29, 2025
Co-authored-by: anod <171704+anod@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 29, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED -Xmx1536m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/gradle-launcher-8.5.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.5-bin/5t9huq95ubn472n8rpzujfbqh/gradle-8.5/lib/agents/gradle-instrumentation-agent-8.5.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.5 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

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

Successfully merging this pull request may close these issues.

2 participants