I need your help in reviewing the following code. My main concern is dead-lock, but there might also be other issues.
The EventGenerator
object is owned by a single thread that running the ThreadTask
. Other threads might access EventGenerator
public APIs to set/remove event handlers.
A few corner cases that I've thought about:
- The thread that is executing
ThreadTask
might callEventGenerator::{Add,Remove}Handler
via theEventDelegate::Invoke
. - The registered
EventDelegate
might be cancelled at any time. IfEventDelegate::Cancel
was called before the invocation, the invoke should not happen, if was called during/after, then nothing should happen.
#include <thread>
#include <atomic>
#include <mutex>
#include <iostream>
#include <vector>
#include <memory>
#include <chrono>
class IEventHandler {
public: virtual void onEvent(int data) = 0;
};
class EventDelegate {
private:
IEventHandler* _handler;
std::atomic<bool> _cancelled;
public:
EventDelegate(IEventHandler* h) : _handler(h), _cancelled(false) {};
void Cancel() { _cancelled = true; }
void Invoke(int data) { if (!_cancelled) _handler->onEvent(data); }
};
class EventGenerator {
private:
std::vector<std::shared_ptr<EventDelegate>> _handlers;
std::mutex _mutex;
public:
void AddHandler(std::shared_ptr<EventDelegate> handler) {
std::lock_guard<std::mutex> lck(_mutex);
_handlers.push_back(handler);
}
void RemoveHanler(std::shared_ptr<EventDelegate> handler) {
std::lock_guard<std::mutex> lck(_mutex);
for (auto it = _handlers.begin(); it != _handlers.end(); ++it) {
if ((*it) == handler) { _handlers.erase(it); break; }
}
}
void ThreadTask() {
int data = 0;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
data++;
std::vector<std::shared_ptr<EventDelegate>> handlers_copy;
{
std::lock_guard<std::mutex> lck(_mutex);
handlers_copy = _handlers;
}
for (auto& h : handlers_copy) { h->Invoke(data); }
if (data == 15) { break; }
}
}
};
// Concrete class
class MyEventHandler : public IEventHandler {
public:
virtual void onEvent(int data) {
std::cout << "onEvent: data = " << data << std::endl;
}
};
int main(int argc, char** argv) {
EventGenerator generator;
std::thread t(&EventGenerator::ThreadTask, &generator);
MyEventHandler h;
std::shared_ptr<EventDelegate> deleg = std::make_shared<EventDelegate>(&h);
generator.AddHandler(deleg);
std::this_thread::sleep_for(std::chrono::seconds(3));
generator.RemoveHanler(deleg);
std::this_thread::sleep_for(std::chrono::seconds(3));
generator.AddHandler(deleg);
std::this_thread::sleep_for(std::chrono::seconds(3));
deleg->Cancel();
std::this_thread::sleep_for(std::chrono::seconds(2));
t.join();
return 0;
}