static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Clearing window %p", window); status_t status = window->clear(); if (status) { LOG_WINDOW("Could not clear window. error=%d", status); } }
static void native_clear(JNIEnv * env, jobject object) { CursorWindow * window = GET_WINDOW(env, object); LOG_WINDOW("Clearing window %p", window); if (window == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()"); return; } window->clear(); }
status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) { String8 ashmemName("CursorWindow: "); ashmemName.append(name); status_t result; int ashmemFd = ashmem_create_region(ashmemName.string(), size); if (ashmemFd < 0) { ALOG(LOG_ERROR,LOG_TAG,"CursorWindow::create, ashmem_create_region return errno = %d",errno); result = -errno; } else { result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE); if (result >= 0) { void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); if (data == MAP_FAILED) { ALOG(LOG_ERROR,LOG_TAG,"CursorWindow::create, mmap return errno = %d",errno); result = -errno; } else { result = ashmem_set_prot_region(ashmemFd, PROT_READ); if (result >= 0) { CursorWindow* window = new CursorWindow(name, ashmemFd, data, size, false /*readOnly*/); result = window->clear(); if (!result) { LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%d, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, window->mSize, window->mData); *outCursorWindow = window; return OK; } ALOG(LOG_ERROR,LOG_TAG,"CursorWindow::create, window->clear return errno = %d",errno); delete window; } else { ALOG(LOG_ERROR,LOG_TAG,"CursorWindow::create, ashmeme_set_prot_region return errno = %d",errno); } } ::munmap(data, size); } ::close(ashmemFd); } *outCursorWindow = NULL; return result; }
static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr, jlong windowPtr, jint startPos, jint requiredPos, jboolean countAllRows) { SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr); sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr); CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); status_t status = window->clear(); if (status) { String8 msg; msg.appendFormat("Failed to clear the cursor window, status=%d", status); throw_sqlite3_exception(env, connection->db, msg.string()); return 0; } int numColumns = sqlite3_column_count(statement); status = window->setNumColumns(numColumns); if (status) { String8 msg; msg.appendFormat("Failed to set the cursor window column count to %d, status=%d", numColumns, status); throw_sqlite3_exception(env, connection->db, msg.string()); return 0; } int retryCount = 0; int totalRows = 0; int addedRows = 0; bool windowFull = false; bool gotException = false; while (!gotException && (!windowFull || countAllRows)) { int err = sqlite3_step(statement); if (err == SQLITE_ROW) { LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows); retryCount = 0; totalRows += 1; // Skip the row if the window is full or we haven't reached the start position yet. if (startPos >= totalRows || windowFull) { continue; } CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) { // We filled the window before we got to the one row that we really wanted. // Clear the window and start filling it again from here. // TODO: Would be nicer if we could progressively replace earlier rows. window->clear(); window->setNumColumns(numColumns); startPos += addedRows; addedRows = 0; cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); } if (cpr == CPR_OK) { addedRows += 1; } else if (cpr == CPR_FULL) { windowFull = true; } else { gotException = true; } } else if (err == SQLITE_DONE) { // All rows processed, bail LOG_WINDOW("Processed all rows"); break; } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { // The table is locked, retry LOG_WINDOW("Database locked, retrying"); if (retryCount > 50) { ALOGE("Bailing on database busy retry"); throw_sqlite3_exception(env, connection->db, "retrycount exceeded"); gotException = true; } else { // Sleep to give the thread holding the lock a chance to finish usleep(1000); retryCount++; } } else { throw_sqlite3_exception(env, connection->db); gotException = true; } } LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows" "to the window in %d bytes", statement, totalRows, addedRows, window->size() - window->freeSpace()); sqlite3_reset(statement); // Report the total number of rows on request. if (startPos > totalRows) { ALOGE("startPos %d > actual rows %d", startPos, totalRows); } jlong result = jlong(startPos) << 32 | jlong(totalRows); return result; }