Использование flock, открытие и закрытие файла для реализации многих читателей одной блокировки писателя

У меня есть проект, который состоит из нескольких процессов, которые могут читать или записывать в одну базу данных. Я хочу реализовать блокировку с одним писателем / несколькими считывателями, синхронизированную файлом блокировки с использованием системных вызовов flock / open / close.

В случае сбоя блокировки любая повторная попытка снова взять блокировку будет выполняться на более высоком уровне, который запрашивал блокировку (в отличие от спин-блокировки).

К сожалению, во время тестирования этой модели она потерпела неудачу в сценарии разблокировки, которому не предшествовала блокировка. возможно, вы можете помочь мне найти то, что я сделал не так:

// keep read and write file descriptors as global variables.
// assuming no more than 1 thread can access db on each process. 

int write_descriptor=0;
int read_descriptor=0;

int lock_write() {
    if((write_descriptor = open(LOCKFILE, O_RDWR|O_CREAT,0644))<0) {
        return LOCK_FAIL;
    }

    if(flock(write_descriptor, LOCK_EX)<0) {
        close(write_descriptor);
        write_descriptor = 0;
        return LOCK_FAIL;
    }
    return LOCK_SUCCESS;
}

int unlock_write() {
    if(!write_descriptor) {
        // sanity: try to unlock before lock.
        return LOCK_FAIL;
    }

    if(flock(write_descriptor,LOCK_UN)<0) {
        // doing nothing because even if unlock failed, we
        // will close the fd anyway to release all locks.
    }
    close(write_descriptor);
    write_descriptor = 0;
    return LOCK_SUCCESS;
}


int lock_read() {
    if((read_descriptor = open(LOCKFILE,O_RDONLY))<0) {
        return LOCK_FAIL;
    }

    if(flock(read_descriptor, LOCK_SH)<0) {
        close(read_descriptor);
        return LOCK_FAIL;
    }
    return LOCK_SUCCESS;
}

int unlock_read() {
    if(!read_descriptor) {
        // sanity : try to unlock before locking first.
        return LOCK_FAIL;
    }

    if(flock(read_descriptor, LOCK_UN)<0) {
        // doing nothing because even if unlock failed, we
        // will close the fd anyway to release all locks.
    }
    close(read_descriptor);
    read_descriptor = 0;
    return LOCK_SUCCESS;
}


int read_db() {
    if(lock_read() != LOCK_SUCCESS) {
        return DB_FAIL;
    }
    // read from db
    if(unlock_read() != LOCK_SUCCESS) {
        // close fd also unlock - so we can fail here (can i assume that ?)
    }
}

int write_db() {
    if(lock_write() != LOCK_SUCCESS) {
        return DB_FAIL;
    }
    //write to db.
    if(unlock_write() != LOCK_SUCCESS) {
        // close fd also unlock - so we can fail here (can i assume that ?)
    }
}

1 ответ

Решение

В обоих lock_read а также lock_write добавьте это как первую строку:

assert ((read_descriptor == 0) && (write_descriptor == 0));

В unlock_read, Добавь это:

assert (read_descriptor != 0);

И в unlock_write, Добавь это:

assert (write_descriptor != 0);

И изменить код как:

if(flock(read_descriptor, LOCK_SH)<0) {
    close(read_descriptor);
    return LOCK_FAIL;
}

чтобы:

if(flock(read_descriptor, LOCK_SH)<0) {
    close(read_descriptor);
    read_descriptor = 0;
    return LOCK_FAIL;
}

Сделайте то же самое для кода записи, чтобы каждый раз, когда дескриптор закрывался, соответствующий глобал устанавливался в ноль. (Вы действительно должны использовать -1 для неверного файлового дескриптора, поскольку ноль допустим.)

Сделайте отладочную сборку и запустите ее. Когда assert поездки, у вас будет свой преступник.

Другие вопросы по тегам