SQLite3 and EINTR

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

SQLite3 and EINTR

Török Edwin
Hi,

If I send signals to a process that is running an sqlite query on a DB in WAL mode I get an SQLITE_BUSY error.
I did set a busy handler, and the timeout hasn't elapsed yet, so it appears that EINTR is treated as an error and not retried, although
I haven't tracked down exactly where. I noticed that unixFileLock doesn't handle EINTR though.

Testcase for SQLite 3.8.2 on Linux amd64:
$ gcc eintr.c -lsqlite3 -o eintr && ./eintr
Query 'INSERT INTO tbl(x) VALUES(3)' failed: database is locked [after 0.025 sec]
^^^ expected to see >20s here.

#include <stdio.h>
#include <sqlite3.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/signal.h>

static int run(sqlite3 *db, const char *sql)
{
    struct timeval tv0, tv1;
    char *errmsg = NULL;
    gettimeofday(&tv0, NULL);
    /* should use prepared statements for speed, but this is just for a testcase */
    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg)) {
        gettimeofday(&tv1, NULL);
        double dt = tv1.tv_sec - tv0.tv_sec + (tv1.tv_usec - tv0.tv_usec)/1000000.0;
        fprintf(stderr,"Query '%s' failed: %s [after %.3f sec]\n", sql, errmsg, dt);
        sqlite3_free(errmsg);
        return -1;
    }
    return 0;
}

static void handler(int sig)
{
}

int main(int argc, char *argv[])
{
    sqlite3 *db = NULL;
    char *errmsg = NULL;
    int ret = 1;
    pid_t parent, pid;
    struct timeval tv0, tv1;

    signal(SIGUSR2, handler);

    parent = getpid();
    pid = fork();
    if (pid < 0) {
        perror("fork failed");
        return 2;
    }
    if (!pid) {
        gettimeofday(&tv0, NULL);
        /* child */
        for(;;) {
             /* if you comment out the kill() line, then the queries fail showing >20s wait times */
            kill(parent, SIGUSR2);
            usleep(50);
            gettimeofday(&tv1, NULL);
            if ((tv1.tv_sec - tv0.tv_sec) * 1000 + (tv1.tv_usec - tv0.tv_usec)/1000 > 5000)
                break;
        }
        exit(0);
    }
    pid = fork();
    do {
        int i;
        if (sqlite3_open_v2("test.db", &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
            fprintf(stderr,"failed to open DB: %s", sqlite3_errmsg(db));
            break;
        }
        sqlite3_busy_timeout(db, 20000);
        if (run(db, "CREATE TABLE IF NOT EXISTS tbl(x INTEGER)"))
            break;
        if (run(db, "PRAGMA journal_mode=WAL"))
            break;
        if (run(db, "PRAGMA synchronous=normal"))
            break;
        for (i=0;i<1000;i++) {
            if (run(db, "INSERT INTO tbl(x) VALUES(3)"))
                break;
            if (run(db, "UPDATE tbl set x = x+1"))
                break;
        }
        if (i == 1000)
            ret = 0;
    } while(0);
    if (sqlite3_close(db)) {
        fprintf(stderr,"Failed to close DB");
        ret = 1;
    }
    return ret;
}

P.S: thank you for the quick fix for sqlite3_randomness!
_______________________________________________
sqlite-users mailing list
[hidden email]
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users