Raku의 atomic-fetch — 네이티브 정수·Scalar 원자적 읽기

Raku의 atomic-fetch — 네이티브 정수·Scalar 원자적 읽기

여러 스레드가 값을 바꾸는 동안, 단순히 변수를 읽는 것만으로는 최신 값을 보장받지 못할 때가 있어요. 컴파일러가 루프에서 변수가 안 바뀐다고 판단해 읽기를 밖으로 끌어올려 버릴 수도 있고요. atomic-fetch는 제대로 된 배리어와 함께 값을 원자적으로 읽어서, 다른 스레드의 최신 갱신을 확실히 보이게 해요.

atomicint — 네이티브 정수 읽기

multi atomic-fetch(atomicint $ is rw)

렉시컬, 속성, 또는 네이티브 배열 요소 어디든 있는 네이티브 정수를 원자적으로 읽어요. 변수를 그냥 쓰는 대신 이 루틴을 쓰면, 다른 스레드가 바꾼 최신 값이 보이도록 필요한 하드웨어 배리어를 수행하고 컴파일러가 읽기를 끌어올리지 못하게 막아요. 예를 들어:

my atomicint $i = 0;
start { atomic-assign($i, 1) }
while atomic-fetch($i) == 0 { }

이 코드는 반드시 종료해요. 반면에:

my atomicint $i = 0;
start { atomic-assign($i, 1) }
while $i == 0 { }

이 경우에는 컴파일러가 루프 안에서 $i가 갱신되지 않는다고 판단해 읽기를 루프 밖으로 끌어올릴 수 있고, 그러면 프로그램이 영원히 끝나지 않게 돼요.

Scalar — Scalar 값 읽기

multi atomic-fetch($target is rw)

Scalar$target에 들어 있는 값을 원자적으로 읽고 그 값을 돌려줘요. 네이티브 정수 형태와 마찬가지로, 하드웨어 배리어를 수행하고 컴파일러의 읽기 끌어올림을 막아 다른 스레드의 최신 갱신을 보이게 해요.

my $started = False;
start { atomic-assign($started, True) }
until atomic-fetch($started) { }

이 코드는 반드시 종료해요. 반면에:

my $started = False;
start { atomic-assign($started, True) }
until $started { }

이 경우에는 컴파일러가 루프 안에서 $started가 갱신되지 않는다고 판단해 읽기를 끌어올릴 수 있고, 그러면 프로그램이 영원히 끝나지 않게 돼요.

출처: Raku 공식 문서 — atomic-fetch

본문

  • 네이티브 정수(atomicint)와 Scalar 두 대상에 대해 원자적 읽기를 지원해요.
  • 변수를 그냥 읽는 것과 달리, 하드웨어 배리어를 수행하고 컴파일러의 읽기 끌어올림을 방지해 최신 값을 보장해요.
  • 스핀 대기(spin-wait) 루프처럼 다른 스레드의 갱신을 기다릴 때 특히 중요해요.