Repository navigation
Conversation
AbstractService.sendSync()/DefaultResponse.receive()는 timeout이 지나면 MessageSender 스레드에 interrupt()를 걸고 TIME_OUT을 반환하지만, MessageSender.run()이 그 락을 synchronized(service)로 잡고 있어 모니터 진입 대기 중에는 interrupt가 전혀 먹히지 않았다(JLS 17.1). 그 결과 이미 TIME_OUT을 반환하고 떠난 호출의 스레드가 계속 락을 기다리다, 앞선 호출이 끝나 락이 풀리는 순간 아무도 기다리지 않는 상태에서 뒤늦게 doSend()를 실행해 실제 발송이 나가는 문제가 있었다. synchronized(service) 대신 새로 추가한 AbstractService.invocationLock (ReentrantLock)에 lockInterruptibly()를 사용해, 락을 기다리는 동안 interrupt가 오면 doSend를 시도하지 않고 즉시 종료하도록 변경했다. 같은 서비스 인스턴스에 대해 doSend가 한 번에 하나씩만 실행된다는 기존 상호배제 보장(2017.02.13 CWE-367 대응)은 그대로 유지된다. sendSync가 반환하는 결과값 자체는 바뀌지 않는다(여전히 TIME_OUT 메시지를 반환) - 바뀌는 것은 그 이후 백그라운드에서 뒤늦게 실행되던 doSend 호출이 사라진다는 점이다. MessageSenderLockContentionTest 추가: 두 번째 호출이 락 대기로 타임아웃된 뒤, 첫 번째 호출이 끝나 락이 풀려도 doSend가 실행되지 않음을 확인한다.
jei007
approved these changes
Aug 13, 2026
jei007
left a comment
Contributor
There was a problem hiding this comment.
표준프레임워크에 대한 지속적인 참여에
대단히 감사드립니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
수정 사유 Reason for modification
수정된 소스 내용 Modified source
Integration/org.egovframe.rte.itl.integration/.../support/AbstractService.java문제:
AbstractService.sendSync()와DefaultResponse.receive()는sender.join(timeout)이만료되면
sender.interrupt()를 호출하고TIME_OUT을 반환합니다. 하지만MessageSender.run()은synchronized(service)로 락을 잡는데, 자바 언어 명세(JLS 17.1)상synchronized진입 대기(모니터 획득 대기) 중에는
interrupt()가 아무 효과가 없습니다.그 결과, 같은 연계(Integration) 서비스 인스턴스에 동시 요청 2건이 들어오면:
doSend()(실제 네트워크 I/O)를 실행합니다.timeout이 지나TIME_OUT을 반환받고 호출자는 그대로종료합니다.
풀리는 순간 아무도 기다리지 않는 상태에서
doSend()를 뒤늦게 실행합니다("유령 전송").이 락은 2017-02-13 커밋 주석에 명시된 대로 CWE-367(TOCTOU) 대응으로 의도적으로 추가된
것이므로, 이번 수정은 락을 제거하거나 범위를 좁히지 않고 같은 상호배제 보장을 유지한
채로 대기 중 인터럽트만 가능하게 바꿉니다.
변경 내용:
AbstractService에ReentrantLock invocationLock필드 추가MessageSender.run()에서synchronized(service)대신service.invocationLock.lockInterruptibly()사용InterruptedException)doSend()를 시도하지 않고 즉시 반환try/finally로 항상unlock()sendSync()/sendAsync()/DefaultResponse.receive()의 반환값이나 시그니처는 바뀌지않습니다 -
TIME_OUT을 반환한 뒤에는 실제로도 발송 시도가 없어진다는 점만 달라집니다.JUnit 테스트 JUnit tests
MessageSenderLockContentionTest를 추가했습니다: 같은 서비스 인스턴스에 대해 느린 첫 호출이락을 쥔 상태에서 두 번째 호출이 짧은 timeout으로 락 대기 중 타임아웃되면, 첫 호출이 끝나 락이
풀린 뒤에도 두 번째 호출의
doSend가 실행되지 않음을 확인합니다.AbstractServiceTest6건: 통과Integration/org.egovframe.rte.itl.integration,Integration/org.egovframe.rte.itl.webservice두 모듈 전체 테스트: 통과 (회귀 없음)
mvn test: 644건 전체 통과, errors 0 / failures 0 / skipped 0AbstractService를 상속하는 클래스, 이 두 모듈에의존하는 다른 모듈)를 grep으로 확인한 결과
EgovWebService와 테스트 더미 외에는 없고,다른 모듈에서 이 두 모듈을 의존하는 곳도 없습니다.
테스트 브라우저 Test Browser
해당 없음 - 백엔드 동시성 수정으로 브라우저 테스트 대상이 아닙니다.
Not applicable - this is a backend concurrency fix with no browser-facing surface.
테스트 스크린샷 또는 캡처 영상 Test screenshots or captured video
해당 없음 (UI 변경 없음). 대신 JUnit 재현 테스트 로그로 수정 전/후 동작을 확인했습니다:
TIME_OUT을 반환하지만, 그 스레드는 계속 대기하다첫 호출 종료(3000ms) 직후 아무도 기다리지 않는 상태에서
doSend를 실행함(로그 타임스탬프로2,638ms 지연 확인).
MessageSender run() interrupted while waiting for the lock; doSend not attempted로그가 즉시 찍히고, 첫 호출 종료 후에도 두 번째 호출의doSend는실행되지 않음.