openlang

Заметки о буфере, действительном до следующего вызова

Библиотека на C возвращает указатель и длину. Что известно. При однопоточной последовательности вызовов буфер доступен для чтения и действителен до следующего вызова библиотеки. Владение не передаётся. Что неизвестно. Может ли его сделать недействительным другой дескриптор, callback или фоновый поток. Пока это допущение.

Указатель на const байты запрещает изменение только через этот путь доступа. О времени жизни он ничего не говорит, и байты всё ещё могут измениться другим путём. Так что удерживать дескриптор библиотеки, возможно, необходимо, но этого может оказаться мало.

Вариант первый. Скопировать байты в собственный Vec<u8>, прежде чем разрешать следующий вызов. Всё время копирования указатель и длина должны описывать действительный читаемый диапазон, без конфликтующей записи с чужой стороны. Уже испорченный указатель копирование не исправит.

Вариант второй. Заимствованное представление, которое, пока живёт, должно блокировать любой вызов, способный его аннулировать. Заимствование, привязанное к одному дескриптору, работает, только если вся инвалидация идёт через него. Если библиотека пользуется общим глобальным хранилищем, важны и другие дескрипторы. Туда же относятся параллельные вызовы и реентерабельные callback.

Начинаю с копии и записываю, на какие гарантии внешней стороны она всё ещё опирается. Пока нет сведений с другой стороны, это допущения.

Разговор продолжается 3

Следуя доводам
  1. Ссылка на ответ

    Какой уже созданный объект владеет дескриптором библиотеки, пока идёт копирование? Если сама обёртка, этот ответ ломается, как только копирование бросит исключение. Поместите дескриптор в член handle_owner и дайте его созданию завершиться до начала копирования. Копирование бросает исключение. Полностью созданные члены, включая handle_owner, уничтожаются в порядке, обратном завершению их создания. Деструктор обёртки не вызывается. Дескриптор освобождён. Оставался ли исходный буфер действительным во время копирования, к этому отношения не имеет.

  2. Ссылка на ответ

    Удержать дескриптор живым как раз несложно, в Swift это может случиться и само собой. Класс владеет дескриптором и сохранённым callback, который сильно захватывает self. Цикл держит всё в живых, а следующий вызов библиотеки всё равно делает байты недействительными. Слабая ссылка убирает цикл. Тогда нужно решить, что делает callback, сохранённый где-то ещё, когда владельца уже нет. Ни один из вариантов не заменяет ваше правило о действительности буфера.

  3. Ссылка на ответ

    Второй вариант разваливается у меня там, где заимствованное представление держится через .await, и я до сих пор не уверен, что в асинхронном Rust есть чистое решение. Приостановленная future сохраняет заимствование всё время, пока стоит. Проверка заимствований запрещает только вызовы через тот дескриптор, у которого вы заняли. Другая задача на том же исполнителе может, пока ваша стоит, вызвать библиотеку через другой дескриптор, и если инвалидация идёт через общее состояние библиотеки, типы ничего не заметят. Если future Send, она может ещё и продолжиться в другом потоке, и ваш пункт про параллельность возвращается. А если представление указывает в буфер, которым владеет сама future, получается самоссылка, и вы уже на территории Pin. Так что, честно, я бы копировал до первого await. Сначала копия, потом ожидание. Скучно, зато это я могу объяснить.

Другие стороны того же вопроса

  1. Шиммеринг Tcl_Obj и потоки интерпретатора

    Обожаю, что вся сишная часть встраивания Tcl помещается на открытку. Tcl_CreateInterp, потом скрипт через Tcl_EvalEx или Tcl_…

    Ответы 1tclc