openlang

다음 호출 전까지만 유효한 버퍼에 관한 메모

C 라이브러리가 포인터와 길이를 돌려줍니다. 지금까지 아는 것. 단일 스레드 호출 순서라면 다음 라이브러리 호출 전까지 버퍼를 읽을 수 있고 유효합니다. 소유권 이전은 없습니다. 모르는 것. 다른 핸들이나 콜백, 백그라운드 스레드가 이 버퍼를 무효로 만들 수 있는지. 이 부분은 아직 가정입니다.

const 바이트를 가리키는 포인터는 그 접근 경로로 고치는 것만 막습니다. 수명에 대해서는 아무것도 말하지 않고, 다른 경로로는 바이트가 여전히 바뀔 수 있습니다. 그러니 라이브러리 핸들을 살려 두는 일은 필요하더라도 충분하지는 않을 수 있습니다.

첫째 안. 다른 호출을 허용하기 전에 소유한 Vec<u8>로 복사합니다. 복사하는 내내 포인터와 길이가 읽을 수 있는 유효한 범위를 가리켜야 하고, 그동안 외부에서 충돌하는 쓰기가 없어야 합니다. 이미 잘못된 포인터는 복사로 고칠 수 없습니다.

둘째 안. 빌린 뷰. 뷰가 살아 있는 동안 그것을 무효로 만들 수 있는 호출을 모두 막아야 합니다. 대여를 핸들 하나에 묶는 방식은 모든 무효화가 그 핸들을 거칠 때만 통합니다. 라이브러리가 전역 공유 저장소를 쓴다면 다른 핸들도 문제가 됩니다. 동시 호출과 재진입 콜백도 같은 목록에 들어갑니다.

복사부터 시작하고, 그래도 기대고 있는 외부 쪽 보장은 적어 둡니다. C 쪽에서 근거가 나오기 전까지 그것들은 가정입니다.

이어지는 대화 3

서로의 근거를 따라
  1. 고유 링크

    복사가 진행되는 동안 라이브러리 핸들은 생성이 끝난 어느 객체가 소유하는가? 래퍼 자신이라고 답하면 복사가 예외를 던지는 순간 그 답은 틀린다. 핸들은 handle_owner 멤버에 넣고, 복사를 시작하기 전에 그 멤버의 생성을 끝낸다. 복사가 예외를 던진다. handle_owner를 포함해 생성이 끝난 멤버는 생성 완료의 역순으로 파괴된다. 래퍼의 소멸자는 실행되지 않는다. 핸들은 해제된다. 복사하는 동안 원본 버퍼가 유효했는지는 이것과 무관하다.

  2. 고유 링크

    핸들을 살려 두는 건 쉬운 쪽이고, Swift에서는 의도치 않게 그렇게 되기도 해요. 클래스가 핸들과 저장된 콜백을 함께 들고 있는데, 그 콜백이 self를 강하게 캡처하는 경우예요. 순환 참조 덕분에 전부 살아 있지만, 다음 라이브러리 호출이 바이트를 무효로 만드는 건 그대로예요. 약한 참조로 고리는 끊겨요. 그럼 다른 곳에 남아 있는 콜백이 소유자가 사라진 뒤 어떻게 동작할지 정해야 해요. 어느 쪽도 버퍼 유효성 규칙을 대신하지는 못해요.

  3. 고유 링크

    빌린 뷰를 .await 너머까지 들고 있는 데서 두 번째 방법이 무너지는데, async Rust에 깔끔한 답이 있는지 저도 아직 잘 모르겠어요. 멈춰 있는 동안 내내 future가 그 빌림을 살려 둬요. 빌림 검사기는 빌려 온 그 핸들을 통한 호출만 막아요. 같은 실행기의 다른 태스크가 내 future가 멈춘 사이에 다른 핸들로 라이브러리를 부를 수 있고, 무효화가 라이브러리의 공유 상태를 거친다면 타입은 아무것도 눈치채지 못해요. future가 Send라면 다른 스레드에서 재개될 수도 있어서, 말씀하신 동시성 항목이 다시 돌아와요. 게다가 뷰가 future 자신이 가진 버퍼를 가리키면 자기 참조가 되고, 그때부터는 Pin의 영역이에요. 그래서 솔직히 첫 await 전에 복사하겠어요. 복사하고 나서 기다리기는 지루하지만, 제가 설명할 수 있거든요.

같은 질문의 다른 갈래

  1. Tcl_Obj 시머링과 인터프리터 스레드

    Tcl을 임베드할 때 C 쪽 코드가 엽서 한 장에 다 들어간다는 게 정말 좋아요. Tcl_CreateInterp로 만들고, Tcl_EvalEx나 Tcl_EvalObjEx로 스크립트를 평가하고, Tcl_GetObjResult로…

    답글 1tclc