inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

naringcode님의 게시글

naringcode naringcode

@naringcode

수강평 작성수
6
평균평점
5.0

게시글 1

질문&답변

궁금한 점이 있습니다.

분석해 보았을 때 우려하신 문제가 발생하려면 꽤 까다로운 조건을 충족해야 합니다. 문제 상황 1(보낼 SendBuffer가 없는데 WSASend()를 호출) 1. Send가 걸린 상황 2. Thread A : Lock을 걸어 sendQueue에 데이터를 삽입 후 Lock을 품(Send). 3. Thread B : 완료 루틴에 의해 깨어나 sendQueue에 데이터가 있는지 확인하고 RegisterSend 호출(ProcessSend). 4. Thread B : sendQueue에 예약된 데이터를 전송(RegisterSend). 5. Thread C : 전송 결과를 완료 루틴으로 받고 sendRegistered 플래그를 품(ProcessSend). 6. Thread A : 이어서 sendRegistered 플래그를 확인하고 RegisterSend 호출(Send). 7. Thread A : sendQueue를 확인했으나 SendBuffer가 없는 상황이 발생(RegisterSend). 8. Thread A : 보낼 SendBuffer가 없는데 전송을 시도하니 WSAEINVAL(잘못된 인자) 에러가 뜨고 실패함(RegisterSend). 이 문제 상황 1을 더 깊게 파고 들면 다음의 연장선에 위치한 문제를 파악할 수 있습니다. 문제 상황 2(데이터를 넣었는데 보내지 않는 상황) 1. Send가 걸린 상황 2. Thread A : Lock을 걸어 sendQueue에 데이터를 삽입 후 Lock을 품(Send). 3. Thread B : 완료 루틴에 의해 깨어나 sendQueue에 데이터가 있는지 확인하고 RegisterSend 호출(ProcessSend). 4. Thread B : sendQueue에 예약된 데이터를 전송(RegisterSend). 5. Thread C : 전송 결과를 완료 루틴으로 받고 sendRegistered 플래그를 품(ProcessSend). 6. Thread A : 이어서 sendRegistered 플래그를 확인하고 RegisterSend 호출(Send). 7. Thread A : sendQueue를 확인했으나 SendBuffer가 없는 상황이 발생(RegisterSend). 8. Thread A : 보낼 SendBuffer가 없는데 전송을 시도하니 에러가 뜨고 실패함(RegisterSend). int32 errCode = ::WSAGetLastError(); if (WSA_IO_PENDING != errCode) { HandleError(errCode); _sendEvent.owner = nullptr; _sendEvent.sendBuffers.clear(); // 9. Thread D : Lock을 걸어 sendQueue에 데이터를 삽입 후 Lock을 품(Send). 10. Thread D : sendRegistered 플래그를 확인했는데 누군가 점유 중인 상태이니 RegisterSend를 호출하지 않음(Send). 11. Thread A : sendRegistered 플래그를 false로 바꿈(RegisterSend). 12. Thread D에서 넣은 SendBuffer를 처리하지 못 하는 상황이 발생. 정확히 말해선 7번 과정에서 sendQueue에 있는 내용을 전부 빼오고 Lock을 푸는 그 시점부터 WSASend() 호출 후 에러가 발생해 sendRegistered를 false로 바꾸기 이전의 모든 구간에서 문제 상황 2 가 발생할 수 있습니다. 애초에 WRITE_LOCK을 걸고 sendQueue에 변화를 주는 로직과 sendRegistered의 상태를 전이하는 로직이 분리되어서 생기는 문제이기에 다음과 같이 Lock을 건다고 해서 해결되지 않습니다. sendQueue는 이미 변화되었고 이를 탐지하지 못 했기에 발생한 문제이니까요. int32 errCode = ::WSAGetLastError(); if (WSA_IO_PENDING != errCode) { HandleError(errCode); _sendEvent.owner = nullptr; _sendEvent.sendBuffers.clear(); WRITE_LOCK; _sendRegistered.store(false); } ProcessSend()의 문제가 아니라 여러 스레드를 통해 Send() -> RegisterSend()를 하는 과정에서 발생하는 문제입니다. 그리고 그 과정에서 문제 상황 1 이 먼저 선행되어 에러가 발생할 수 있는 조건이 충족되어야 합니다.

좋아요수
4
댓글수
2
조회수
520