Proper maintenance and care of multi-threading locks
다음 전략은 코드가 교착 상태가 없도록 보장하는 데 사용됩니다(일반적으로 4번째 Coffman 조건: 순환 대기를 해결함으로써).
- 코드를 구조화하여 한 번에 하나의 잠금만 필요하도록 하세요.
- 항상 아래 표에 의해 주어진 순서대로 공유 잠금을 획득하십시오.
- 제한 없는 재귀를 필요로 하는 구조는 피하십시오.
Locks
시스템에 존재하는 모든 잠금과 교착 상태를 피하기 위한 사용 메커니즘은 다음과 같습니다(여기서는 타조 알고리즘을 허용하지 않습니다):
다음은 확실히 리프 잠금(레벨 1)이며, 다른 잠금을 획득하려고 해서는 안 됩니다:
safepoint
이 잠금은
JL_LOCK
및JL_UNLOCK
에 의해 암묵적으로 획득된다는 점에 유의하십시오. 레벨 1 잠금의 경우 이를 피하려면_NOGC
변형을 사용하십시오.이 잠금을 유지하는 동안, 코드는 어떤 할당도 하지 않거나 안전 지점에 도달해서는 안 됩니다. 할당을 수행할 때, GC를 활성화/비활성화할 때, 예외 프레임에 들어가거나 복원할 때, 잠금을 잡거나 해제할 때 안전 지점이 있다는 점에 유의하세요.
공유_지도
최종자
pagealloc
gcpermlock
flisp
jlinstackwalk (윈도우32)
ResourcePool<?>::mutex
RLST_mutex
llvm인쇄뮤텍스
jl잠금스트림::뮤텍스
debuginfo_asyncsafe
추론타이밍뮤텍스
ExecutionEngine::SessionLock
flisp 자체는 이미 스레드 안전하며, 이 잠금은
jl_ast_context_list_t
풀만 보호합니다. 마찬가지로, ResourcePool<?>::mutexes는 관련 리소스 풀만 보호합니다.
다음은 리프 잠금(레벨 2)이며, 내부적으로는 레벨 1 잠금(세이프포인트)만 획득합니다:
- globalrootslock
- 모듈->잠금
- JLDebuginfoPlugin::PluginMutex
- newlyinferredmutex
다음은 레벨 3 잠금 장치로, 내부적으로 레벨 1 또는 레벨 2 잠금 장치만 획득할 수 있습니다:
- 방법->쓰기 잠금
- 타입캐시
다음은 레벨 4 잠금 장치로, 레벨 1, 2 또는 3 잠금을 얻기 위해서만 재귀할 수 있습니다:
- MethodTable->writelock
이 지점 위에서 잠금을 유지하는 동안 Julia 코드를 호출할 수 없습니다.
orc::ThreadSafeContext (TSCtx) 잠금은 잠금 계층에서 특별한 위치를 차지합니다. 이들은 LLVM의 전역 비스레드 안전 상태를 보호하는 데 사용되지만, 이들의 수는 임의일 수 있습니다. 기본적으로, 이러한 모든 잠금은 나머지 계층과 비교할 때 레벨 5 잠금으로 간주될 수 있습니다. TSCtx를 획득하는 것은 JIT의 TSCtx 풀에서만 수행해야 하며, 해당 TSCtx에 대한 모든 잠금은 풀에 반환하기 전에 해제되어야 합니다. 여러 TSCtx 잠금을 동시에 획득해야 하는 경우(재귀 컴파일로 인해) 잠금은 풀에서 빌린 TSCtx의 순서대로 획득해야 합니다.
다음은 레벨 5 잠금장치입니다.
- JuliaOJIT::EmissionMutex
다음은 레벨 6 잠금으로, 하위 레벨에서 잠금을 획득하기 위해서만 재귀할 수 있습니다:
- 코드 생성
- jl모듈뮤텍스
다음은 거의 루트 잠금(레벨 끝-1)으로, 이를 획득하려고 할 때 루트 잠금만 유지될 수 있음을 의미합니다:
타입인프
이것은 아마도 가장 까다로운 것 중 하나일 수 있습니다. 왜냐하면 타입 추론이 여러 지점에서 호출될 수 있기 때문입니다.
현재 잠금은 코드 생성 잠금과 병합되어 있으며, 서로 재귀적으로 호출합니다.
다음 잠금은 IO 작업을 동기화합니다. 위에 나열된 다른 잠금을 보유한 상태에서 경고 메시지나 디버그 정보를 인쇄하는 등의 I/O 작업을 수행하면 해로운 및 찾기 어려운 교착 상태가 발생할 수 있습니다. 매우 주의하십시오!
iolock
개별 ThreadSynchronizers 잠금
이것은 iolock을 해제한 후에도 계속 유지될 수 있으며, iolock 없이도 획득할 수 있지만, 절대 iolock을 들고 있는 상태에서 획득하려고 시도하지 않도록 매우 주의해야 합니다.
Libdl.LazyLibrary 잠금
다음은 루트 잠금으로, 이를 획득하려고 할 때 다른 잠금을 보유해서는 안 됩니다:
최상위
이것은 새로운 유형을 만들거나 새로운 메서드를 정의하는 것과 같은 최상위 작업을 시도하는 동안 수행되어야 합니다: 단계화된 함수 내에서 이 잠금을 얻으려고 하면 교착 상태가 발생합니다!
추가적으로, 어떤 코드가 임의의 최상위 표현식과 안전하게 병렬 실행될 수 있는지는 불확실하므로, 모든 스레드가 먼저 안전 지점에 도달해야 할 수도 있습니다.
Broken Locks
다음 자물쇠가 고장났습니다:
최상위
현재 존재하지 않습니다.
수정: 그것을 생성하다
모듈->잠금
이것은 순서대로 획득되었는지 확실하지 않기 때문에 교착 상태에 취약합니다. 일부 작업(예:
import_module
)은 잠금이 누락되어 있습니다.수정:
jl_modules_mutex
로 교체?loading.jl:
require
및register_root_module
이 파일에는 잠재적으로 여러 문제가 있습니다.
수정: 잠금 장치 필요
Shared Global Data Structures
이 데이터 구조들은 공유되는 가변 전역 상태로 인해 각각 잠금이 필요합니다. 이는 위의 잠금 우선 순위 목록에 대한 역순 목록입니다. 이 목록에는 그 단순성으로 인해 레벨 1 리프 리소스가 포함되어 있지 않습니다.
MethodTable 수정 (def, cache) : MethodTable->writelock
타입 선언 : 최상위 잠금
타입 애플리케이션 : 타입캐시 잠금
전역 변수 테이블 : 모듈->잠금
모듈 직렬 변환기 : 최상위 잠금
JIT 및 타입 추론 : 코드 생성 잠금
MethodInstance/CodeInstance 업데이트: Method->writelock, codegen lock
- 이들은 건설 시 설정되며 변경할 수 없습니다:
- specTypes
- sparam_vals
- def
- 소유자
- 이것들은
jl_type_infer
에 의해 설정됩니다 (코드 생성 잠금을 유지하는 동안):
- 캐시
- 죄송하지만, "rettype"에 대한 추가 정보가 필요합니다. 어떤 내용을 번역해 드릴까요?
- 유추된
* valid ages
inInference
플래그:
jl_type_infer
가 이미 실행 중일 때 반복적으로 발생하는 것을 빠르게 피하기 위한 최적화- 실제 상태(
inferred
설정 후fptr
)는 코드 생성 잠금에 의해 보호됩니다.
함수 포인터:
- 이러한 전환은 코드 생성 잠금이 유지되는 동안
NULL
에서 값으로 한 번 발생합니다.코드 생성기 캐시(
functionObjectsDecls
의 내용):
- 이들은 코드 생성 잠금이 유지되는 동안에만 여러 번 전환될 수 있습니다.
- 이것의 이전 버전을 사용하거나 이의 새로운 버전을 차단하는 것은 유효하므로, 코드가 메서드 인스턴스의 다른 데이터(예:
rettype
)를 참조하지 않고 조정된 것으로 가정하지 않는 한 경쟁 조건은 무해합니다. 코드 생성 잠금을 보유하고 있지 않는 한 말이죠.
LLVMContext : 코드 생성 잠금
방법 : Method->writelock
- 루트 배열 (직렬화기 및 코드 생성기)
- 호출 / 전문화 / tfunc 수정