openlang

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

부분 검토

가상의 C 라이브러리가 포인터와 길이를 반환한다고 가정합니다. 단일 스레드에서 호출을 순서대로 할 때, 버퍼는 다음 라이브러리 호출 전까지 읽을 수 있고 유효합니다. 소유권 이전은 없다고 가정합니다. 다른 핸들, 콜백, 백그라운드 스레드도 버퍼를 무효로 만들 수 있는지는 아직 미확인입니다.

가리키는 바이트에 const가 붙었다면 그 포인터를 통한 변경이 제한됩니다. 하지만 버퍼의 수명을 보장하거나 다른 경로의 변경까지 막지는 않습니다. 따라서 라이브러리 핸들을 살려 두는 일이 필요하더라도 그것만으로는 부족할 수 있습니다.

다른 호출을 허용하기 전에 바이트를 복사해서 소유권을 가진 Vec<u8>로 만드는 래퍼를 생각할 수 있습니다. 포인터와 길이가 가리키는 범위는 복사 내내 유효하고 읽을 수 있어야 하며, 외부 코드에서 충돌하는 변경도 없어야 합니다. 이미 무효인 포인터를 복사로 고칠 수는 없습니다.

빌려 쓰는 뷰를 제공한다면 그 뷰가 있는 동안 무효화를 일으킬 수 있는 모든 호출을 막아야 합니다. 핸들 하나에 묶인 대여로 충분하려면 관련된 무효화 경로가 모두 그 핸들을 거쳐야 합니다. 라이브러리가 전역 저장 공간을 공유한다면 다른 핸들도 고려해야 합니다. 동시 호출과 재진입하는 콜백도 이 근거에 포함해야 합니다.

저라면 소유권을 가진 복사본부터 제공하고, 그래도 외부 코드에서 보장해야 할 사항을 문서에 적겠습니다. 이는 경계를 이렇게 정하자는 제안이며, 외부 코드가 그 약속을 지킨다는 증거는 아닙니다.

이어지는 대화 2

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

    소유권을 가진 복사본을 만드는 경계에서도 준비가 끝나기 전까지 라이브러리 핸들을 맡을 소유자가 필요하다. C++ 래퍼라면 예외가 날 수 있는 복사를 시작하기 전에, 생성이 완전히 끝난 handle_owner 멤버에 핸들을 맡기겠다. 복사 도중 실패하면 생성이 완료된 멤버는 생성 완료 순서의 역순으로 파괴된다. 생성이 끝나지 않은 바깥 객체의 소멸자는 실행되지 않는다. 이로써 실패 시 핸들을 해제하는 문제는 다룬다. 복사하는 동안 원본 버퍼가 유효했다는 근거까지 생기는 것은 아니다.

  2. 고유 링크

    콜백을 저장한다면 핸들을 살려 두는 일에도 소유 관계를 그려 볼 필요가 있어요. Swift 클래스의 인스턴스 하나가 핸들과 콜백을 소유하고, 저장된 그 콜백은 바로 그 인스턴스를 강하게 캡처한다고 해 봐요. 그 고리 때문에 핸들을 계속 살려 둘 수는 있지만, 다음 호출이 바이트를 무효로 만드는 건 전혀 막지 못해요. 약하게 캡처하면 그 연결은 끊어져요. 다만 다른 곳에 보관된 콜백이라면 소유자가 사라졌을 때의 동작도 정해야 해요. 어느 쪽도 말씀하신 버퍼 유효성 규칙을 대신하지는 못해요.