Предотвратить одновременное использование и освобождение - PullRequest
0 голосов
/ 30 мая 2018

С учетом следующего C-API, внутренне реализованного в C ++

struct OpaqueObject;

struct OpaqueObject *allocateObject();
int deallocateObject(struct OpaqueObject *obj);

int useObject(struct OpaqueObject *obj);

Безопасно распределять, использовать и освобождать несколько отдельных struct OpaqueObject -экземпляров одновременно.Конечно, одновременное использование одного struct OpaqueObject -Instance недопустимо и приведет к неопределенному поведению.В качестве гарантии struct OpaqueObject содержит мьютекс, запрещающий именно эту ситуацию: функция useObject() возвращается с кодом ошибки, если несколько потоков пытаются вызвать его с одним и тем же struct OpaqueObject -Instance.

struct OpaqueObject {
    std::mutex access;
    // ...
};

int useObject(struct OpaqueObject *obj) {
    if (!obj->access.try_lock()) {
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        // start using this obj
        // ...
        obj->access.unlock();
        return OK;
    }
}

Но как этот защитный механизм может быть расширен до функции deallocateObject()?Первым наивным подходом будет

int deallocateObject(struct OpaqueObject *obj) {
    if (!obj->access.try_lock()) {
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        delete obj; // <--- (1)
        return OK;
    }
}

Но уничтожение мьютекса, когда он все еще заблокирован, не определено.Мы не можем просто разблокировать его прямо перед строкой (1), так как это полностью помешало бы нашим усилиям предотвратить одновременное использование и освобождение.

Можно ли вернуться с ошибкой либо в useObject(), либо в * 1020?*, если эти функции использовались одновременно с одним и тем же struct OpaqueObject -Instance?

1 Ответ

0 голосов
/ 30 мая 2018

Вы можете обменять std::mutex на std::atomic<int>:

struct OpaqueObject {
    std::atomic<int> access = 0;
    // ...
};

И затем в своих функциях вы можете атомарно обмениваться значениями и посмотреть, используется ли оно:

int useObject(struct OpaqueObject *obj) {
    if (obj->access.exchange(1)) {
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        // start using this obj
        // ...
        obj->access.exchange(0);
        return OK;
    }
}

Если используется объект, переменная access = 1 и std::atomic::exchange вернет 1. В противном случае он возвращает 0 и устанавливает access в 1.

Также будет работать удаление объекта.

int deallocateObject(struct OpaqueObject *obj) {
    if (obj->access.exchange(1)) { // (*)
        // different thread currently uses this obj
        return CONCURRENT_USE_ERROR;
    } else {
        delete obj;                 // (**)
        return OK;
    }
}

Важно: Рассматривали ли вы, что происходит после удаления объекта?Как вы уведомляете другие темы о его удалении?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...