Reentrancy

struct TrafficAdministration: Observer<Person>{
void TrafficAdministration::field_changed(Person& source, const string& field_name) override {
if(field_name=="age"){
if(source.get_age()<17) cout<<"Whoa there, you are not old enough to drive!\n";
else{
cout<<"We no longer care!\n";
source.unsubscribe(this);
}
}
}
};
위의 TrafficAdministration 클래스는 age가 17세가 될 때, 알림 수신 등록을 해제한다.
만일 아래와 같은 연이은 호출이 발생한 경우,
notify()->field_changed()->unsubscribe()

unsubscribe()의 실행이 이미 앞에서 락이 점유된 이후에 일어난다.
(이미 점유된 락을 다시 점유하려고 한다.)
이와 같은 문제를 재진입(reentrancy) 문제라고 한다.

- 재진입 문제가 발생하는 상황을 금지한다.
- 항목을 삭제하는 작업자체를 우회적으로 처리한다
void subscribe(Observer<T>* o){
auto it=find(observers.begin(), observers.end(), o);
// vector , nullptr
if(it!=observers.end()) *it=nullptr;
}
void notify(T& source, const string& name){
for(auto obs: observers){
if(obs) obs->field_changed(source, name); // nullptr
}
}
위와 같이 구현하게되면, 추가적으로 notify에서 검사를 필요로 한다.
또한 여전히 subscribe()와 unsubscribe()가 병렬로 호출되어 수정을 시도할 수 있음

혹은 notiry() 안에서 컬렉션 전체를 복사하여 사용하는 경우, 여전히 락은 필요하지만
알림을 보내는 단계에서 락을 점유할 필요가 없다.
void notify(T& source, const string& name){
vector<Observer<T>*> observers_copy;
{
lock_guard<mutex_t> lock{mtx};
observers_copy=observers;
} // lock
for(auto obs: observers_copy){
if(obs) obs->field_changed(source, name);
}
}
혹은 재진입 문제를 해결하기 위한 recursive_mutex를 사용할 수도 있다.
하지만, 코드 설계에 약간의 주의만 기울인다면 재귀적인 특성이 없는 보통의 mutex를 사용해 구현할 수 있다.