Ваше описание не очень понятно. Вещи обычно не вызываются из «глобальной области видимости», только определенные виды конструкторов и деструкторов. Возможно, вы говорите о вызове внешней функции: обратный вызов вызывается каким-то внешним контекстом в другом программном компоненте. Вы также не даете понять, кто пишет обратный вызов. Эта сторонняя библиотека вызывает ваш зарегистрированный обратный вызов? «Обратный вызов третьей стороны» будет функцией сторонней библиотеки, которая регистрируется как обратный вызов для вас.
Я предполагаю, что вы вызываете какую-то стороннюю библиотеку, которая вызывает ваш обратный вызов, и вы попадаете в тупиковую ситуацию, либо в вашем коде, либо в стороннем.
Если в стороннем коде возникает тупик, возможно, вы не соблюдаете надлежащие правила в отношении того, что может или не может сделать обратный вызов. Вы делаете звонки в этот сторонний код из обратного вызова? Может быть, вам не разрешено это делать, или нет определенных. Возможно, сторонняя библиотека удерживает мьютекс при вызове вашего обратного вызова, и функция, которую вы вызываете из этого обратного вызова, пытается получить тот же мьютекс.
Если тупик находится в вашем собственном коде, на вашем собственном мьютексе, возможно, вы делаете что-то глупое, например, удерживаете мьютекс, вызываете сторонний код и затем пытаетесь снова заблокировать мьютекс. Если это так просто, есть разные подходы. Никогда нельзя удерживать мьютекс при вызове внешних компонентов (что часто является хорошей идеей, потому что мьютексы должны храниться в течение коротких периодов времени, чтобы избежать конфликтов). Освободите мьютекс при выходе из вашего кода и повторно заполучите его при входе. Иногда это неудобно, потому что всякий раз, когда вы освобождаете свой мьютекс, «мир меняется», и вам приходится пересматривать состояние защищенных переменных и обрабатывать случаи, которые могут возникнуть. Ошибки могут появляться, когда ваш код полагается на предположения предыдущего времени, когда он удерживал мьютекс, который больше не применяется, потому что он освободил мьютекс.
Другой подход, который может работать с этой ситуацией обратного вызова, заключается в том, чтобы обратный вызов предполагал
что мьютекс уже удерживается и не блокируйте его дважды. Если обратный вызов - это некоторая общая функция, которая также используется как обратный вызов, разделите ее на версию без блокировки и версию с блокировкой. Привязать обратный вызов к неблокирующему.
Еще одна вещь, которая может вызвать взаимоблокировки, это получение нескольких блокировок в обратном порядке. Это легко случается, когда потоки пересекают объекты в большой программе в разных порядках. Например. какой-то поток работает с объектом A, который вызывает метод для объекта B, а другой поток делает что-то, что переходит от B к A. Техника всегда снимает блокировку, когда вы покидаете модуль, поможет предотвратить это. В ситуациях, когда вы не можете этого сделать, вы можете изменить код так, чтобы он всегда получал блокировки в одном и том же порядке.
Другим инструментом являются различные виды замков. Рекурсивные блокировки могут помочь (хотя они и являются клуджем). Блокировки чтения-записи могут помочь в некоторых ситуациях. Алгоритмы «без блокировки» (которые используют крошечные мьютексы в виде атомарных машинных инструкций вместо больших мьютексов ОС).