Я боюсь делать чрезмерную блокировку только для того, чтобы проверить, изменилась ли конфигурация фильтра (в 99% случаев при попадании в точку трассировки конфигурация не менялась).Я тоже открыт для больших изменений в макросе.Поэтому вместо обсуждения производительности мьютекса и критической секции также было бы приемлемо, если бы макрос просто отправлял событие в цикл событий в другом потоке (при условии, что доступ к циклу событий безопасен для потока)
Как вы думаете, потокобезопасный обмен сообщениями между потоками реализован без блокировок?
В любом случае, вот конструкция, которая может работать:
Структура данных, которая содержит фильтр, должна быть изменена, чтобы онвыделяется динамически из кучи, потому что мы собираемся создавать несколько экземпляров фильтров.Кроме того, к нему нужно добавить счетчик ссылок.Вам нужен typedef что-то вроде:
typedef struct Filter
{
unsigned int refCount;
// all the other filter data
} Filter;
Там где-то объявлен одиночный 'текущий фильтр'.
static Filter* currentFilter;
и инициализирован с некоторыми настройками по умолчанию.
В вашемМакрос TRACE:
#define TRACE(char* msg)
{
static Filter* filter = NULL;
static TraceProc traceCallback = NULL;
if (filterOutOfDate(filter))
{
getNewCallback(__FILE__, __LINE__, &traceCallback, &filter);
}
traceCallback(msg);
}
filterOutOfDate()
просто сравнивает фильтр с currentFilter, чтобы убедиться, что он такой же.Этого должно быть достаточно, чтобы просто сравнить адреса.Он не блокируется.
getNewCallback()
применяет текущий фильтр для получения новой функции трассировки и обновляет переданный фильтр с адресом текущего фильтра.Его реализация должна быть защищена блокировкой мьютекса.Кроме того, он вычисляет refCount исходного фильтра и увеличивает refCount нового фильтра.Это так, мы знаем, когда мы можем освободить старый фильтр.
void getNewCallback(const char* file, int line, TraceProc* newCallback, Filter** filter)
{
// MUTEX lock
newCallback = // whatever you need to do
currentFilter->refCount++;
if (*filter != NULL)
{
*filter->refCount--;
if (*filter->refCount == 0)
{
// free filter and associated resources
}
}
*filter = currentFilter;
// MUTEX unlock
}
Когда вы хотите изменить фильтр, вы делаете что-то вроде
changeFilter()
{
Filter* newFilter = // build a new filter
newFilter->refCount = 0;
// MUTEX lock (same mutex as above)
currentFilter = newFilter;
// MUTEX unlock
}