Займався проблемою дедлока. Суть у слідуючому: є дві послідовності викликів:
1. TRAFFCOUNTER::SomeMethod1 -> USER::SomeMethod2
2. AUTH::SomeMethod3 -> USER::SomeMethod4 -> (Notifier) -> TRAFFCOUNTER::SomeMethod5
Вони відбуваються у різних потоках і на початку кожного виклику становлюється мьютекс. Звісно іноді виникає дедлок. Для того щоб позбутись цього я вирішив розірвати другий ланцюг по лінії Notifier.
Notifier це такий допоміжний клас що використовується для повідомлення одних об’єктів про зміни у інших. Виклики через Notifier виконуються у тому ж потоці що й подія яка їх викликала (вибачте за каламбур). Щоб не зламати все я вирішив поки що не чіпати систему повідомленнь а зробити чергу "дій" на стороні TRAFFCOUNTER які будуть задаватись Notifier'ом.
Початкова реалізація була проста: структура з двома полями (тип дії і дані) і диспетчер який за типом дії викликав необхідний метод. І вона працювала, але дуже не подобалась мені наявністю switch для диспетчеризації. Тоді я подумав: "Метод який необхідно викликати для виконання дії відомий, можна його вказувати явно і позбутись диспетчера". Для цього структура дещо змінилась:
TRAFFCOUNTER зберігає список із таких структур. Notifier додае структуру до списку:
Сам TRAFFCOUNTER у своєму потоці оброблює дії:
Щось мені підказує що все це можна ще більше спростити, але поки не допетрав як.
Upd. Забув викласти найсмачніше:
1. TRAFFCOUNTER::SomeMethod1 -> USER::SomeMethod2
2. AUTH::SomeMethod3 -> USER::SomeMethod4 -> (Notifier) -> TRAFFCOUNTER::SomeMethod5
Вони відбуваються у різних потоках і на початку кожного виклику становлюється мьютекс. Звісно іноді виникає дедлок. Для того щоб позбутись цього я вирішив розірвати другий ланцюг по лінії Notifier.
Notifier це такий допоміжний клас що використовується для повідомлення одних об’єктів про зміни у інших. Виклики через Notifier виконуються у тому ж потоці що й подія яка їх викликала (вибачте за каламбур). Щоб не зламати все я вирішив поки що не чіпати систему повідомленнь а зробити чергу "дій" на стороні TRAFFCOUNTER які будуть задаватись Notifier'ом.
Початкова реалізація була проста: структура з двома полями (тип дії і дані) і диспетчер який за типом дії викликав необхідний метод. І вона працювала, але дуже не подобалась мені наявністю switch для диспетчеризації. Тоді я подумав: "Метод який необхідно викликати для виконання дії відомий, можна його вказувати явно і позбутись диспетчера". Для цього структура дещо змінилась:
typedef void (TRAFFCOUNTER::*Actor)(user_iter);
struct Action {
Actor actor;
user_iter data;
Action(Actor a, user_iter d) : actor(a), data(d) {};
};
|
| _Winnie C++ Colorizer |
TRAFFCOUNTER зберігає список із таких структур. Notifier додае структуру до списку:
inline
void DEL_USER_NONIFIER::Notify(const user_iter & user)
{
STG_LOCKER(&traffCnt.mutex, __FILE__, __LINE__);
traffCnt.pending.push_back(Action(&TRAFFCOUNTER::UnSetUserNotifiers, user));
traffCnt.pending.push_back(Action(&TRAFFCOUNTER::DelUser, user));
}
|
| _Winnie C++ Colorizer |
Сам TRAFFCOUNTER у своєму потоці оброблює дії:
class InvokeAction : public std::unary_function<const Action &, void>
{
public:
InvokeAction(TRAFFCOUNTER & t) : tc(t) {};
void operator()(const Action & action);
private:
TRAFFCOUNTER & tc;
};
|
| _Winnie C++ Colorizer |
void TRAFFCOUNTER::ProcessActions()
{
if (pending.empty())
return;
std::list<Action> local;
{
STG_LOCKER lock(&mutex, __FILE__, __LINE__);
local.swap(pending);
printfd(__FILE__, "local.size() = %d, pending.size() = %d\n", local.size(), pending.size());
}
std::for_each(
local.begin(),
local.end(),
InvokeAction(*this)
);
}
|
| _Winnie C++ Colorizer |
Щось мені підказує що все це можна ще більше спростити, але поки не допетрав як.
Upd. Забув викласти найсмачніше:
inline
void InvokeAction::operator()(const Action & action)
{
// Invoke method action.actor with data action.data
(tc.*action.actor)(action.data);
}
|
| _Winnie C++ Colorizer |