mdbx: костыль для глушения/игнорирования EDEADLK в ряде сценариев при использовании Valgrind или ASAN.

Достаточно запутанно:

 - Для полноценного контроля при использовании Valgrind или ASAN
   требуется закрашивать/отравлять отображение файла БД выше границы
   распределенных страниц.

 - Производить такое подкрашивание/отравление необходимо в синхронизации
   с пишущими транзакциями и запросами на изменение геометрии, в том числе
   при изменении размера БД и/или геометрии другим процессом.

 - Для такой синхронизации логично и проще всего использовать основной
   мьютекс/механизм блокировки пишущих транзакций, что и происходит внутри
   txn_valgrind().

 - Однако, в этой схеме может возникать ошибка EDEADLK, когда
   txn_valgrind() вызывается при завершении читающей транзакции
   выполняющейся с дополнительной блокировкой пишущих транзакций.

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

 = В результате, при использовании Valgrind или ASAN утилита mdbx_chk
   запущенная с опциями `-wc` всегда завершается неудачей из-за как минимум
   одной проблемы в ходе проверки. Что внешне выглядит как
   недочет/ошибка/регресс и создает проблемы при автоматизированном
   тестировании.

Добавленный костыль использует atomic-счетчик, который инкремируется до
и декремируется после попытки захвата блокировки изнутри txn_valgrind().
В свою очередь, код обрабатывающий ошибку захвата блокировки, игнорирует
EDEADLK при ненулевом значении счетчика. Активируется костыль только при
сборке с поддержкой Valgrind или включенном ASAN, и не оказывает
никакого влияния в остальных случаях.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2023-10-07 21:53:25 +03:00
parent 45721d4064
commit 1aead6869a
3 changed files with 12 additions and 4 deletions

View File

@ -9131,7 +9131,7 @@ static int txn_renew(MDBX_txn *txn, const unsigned flags) {
}
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
txn_valgrind(env, txn);
#endif
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
txn->mt_owner = tid;
return MDBX_SUCCESS;
}
@ -9813,8 +9813,10 @@ static int txn_end(MDBX_txn *txn, const unsigned mode) {
txn->mt_txnid == slot->mr_txnid.weak &&
slot->mr_txnid.weak >= env->me_lck->mti_oldest_reader.weak);
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
atomic_add32(&env->me_ignore_EDEADLK, 1);
txn_valgrind(env, nullptr);
#endif
atomic_sub32(&env->me_ignore_EDEADLK, 1);
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
atomic_store32(&slot->mr_snapshot_pages_used, 0, mo_Relaxed);
safe64_reset(&slot->mr_txnid, false);
atomic_store32(&env->me_lck->mti_readers_refresh_flag, true,
@ -9843,7 +9845,7 @@ static int txn_end(MDBX_txn *txn, const unsigned mode) {
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
if (txn == env->me_txn0)
txn_valgrind(env, nullptr);
#endif
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
txn->mt_flags = MDBX_TXN_FINISHED;
txn->mt_owner = 0;
@ -15292,7 +15294,7 @@ bailout:
} else {
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
txn_valgrind(env, nullptr);
#endif
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
}
osal_free(env_pathname.buffer_for_free);
return rc;

View File

@ -1482,6 +1482,7 @@ struct MDBX_env {
int me_valgrind_handle;
#endif
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
MDBX_atomic_uint32_t me_ignore_EDEADLK;
pgno_t me_poison_edge;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */

View File

@ -822,6 +822,11 @@ __cold static int mdbx_ipclock_failed(MDBX_env *env, osal_ipclock_t *ipc,
#error "FIXME"
#endif /* MDBX_LOCKING */
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
if (rc == EDEADLK && atomic_load32(&env->me_ignore_EDEADLK, mo_Relaxed) > 0)
return rc;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
ERROR("mutex (un)lock failed, %s", mdbx_strerror(err));
if (rc != EDEADLK)
env->me_flags |= MDBX_FATAL_ERROR;