Как сделать lua_pushstring и избежать исключения setjmp из нехватки памяти
Иногда я хочу использовать lua_pushstring местами после того, как я выделил некоторые ресурсы, которые мне нужно будет очистить в случае сбоя. Однако, как видно из документации, функции lua_push * всегда могут привести к исключению нехватки памяти. Но это исключение мгновенно выходит из моей области видимости C и не позволяет мне очистить то, что я мог временно выделить, что, возможно, придется освободить в случае ошибки.
Пример кода для иллюстрации ситуации:
void* blubb = malloc(20);
...some other things happening here...
lua_pushstring(L, "test"); //how to do this call safely so I can still take care of blubb?
...possibly more things going on here...
free(blubb);
Могу ли я заранее проверить, не произойдет ли такое исключение, а затем избегать нажатия и запуска собственных ошибок, как только я благополучно очистил свои собственные ресурсы? Или я могу как-то просто деактивировать setjmp, а затем проверить некоторую "волшебную переменную" после выполнения push, чтобы увидеть, действительно ли она сработала или вызвала ошибку?
Я подумал, что pcall'у свою собственную функцию, но даже простое добавление функции в стек, которую я хочу безопасно вызвать через pcall, может дать мне нехватку памяти, не так ли?
Чтобы прояснить ситуацию, я специально прошу об этом для комбинированного использования с пользовательскими распределителями памяти, которые будут препятствовать тому, чтобы Lua выделял слишком много памяти, поэтому предположим, что это не тот случай, когда вся система исчерпала память.
3 ответа
Если вы не зарегистрировали обработчик пользовательской памяти в Lua, когда создавали состояние Lua, ошибка нехватки памяти означает, что во всем приложении не хватает памяти. Восстановление из этого состояния, как правило, невозможно. Или, по крайней мере, не выполнимо во многих случаях. Это может зависеть от вашего приложения, но, вероятно, нет.
Короче говоря, если это когда-нибудь произойдет, у вас есть более важные вещи, о которых стоит беспокоиться;)
Единственный вид очистки, который должен повлиять на вас, - это вещи, которые не относятся к вашему приложению. Если у вас есть какая-то глобальная память процесса, которую вам нужно освободить или установить какое-либо состояние. Вы осуществляете межпроцессное взаимодействие, и у вас есть файл сопоставленной памяти, о котором вы говорите. Или что-то типа того.
В противном случае, вероятно, лучше просто убить ваш процесс.
Вы можете собрать Lua как библиотеку C++. Когда вы делаете это, ошибки становятся фактическими исключениями, которые вы можете либо перехватить, либо просто использовать объекты RAII для обработки.
Если вы застряли с C... ну, вы мало что можете сделать.
Я особенно заинтересован в специальном распределителе, который выйдет из памяти намного раньше, чтобы Lua не съел слишком много памяти.
Тогда вы должны справиться с этим по-другому. Сигнализировать об ошибке нехватки памяти означает сказать: "Я хочу, чтобы Lua прекратил работу прямо сейчас".
Способ не дать Lua съесть память - периодически проверять память состояния Lua и собирать мусор, если он использует слишком много. И если это не освобождает достаточно памяти, то вы должны завершать состояние Lua вручную, но только тогда, когда это безопасно.
Недавно я снова увлекся песочницей Lua, и теперь я думаю, что ответ, который я принял ранее, - плохая идея. Я еще об этом подумал:
Почему периодической проверки недостаточно
Периодическая проверка большого потребления памяти и прекращение Lua "только тогда, когда это безопасно" кажется плохой идеей, если учесть, что одна огромная таблица может поглотить большую часть вашей памяти с одной инструкцией VM, о которой вы будете узнайте только после того, как это произошло - где ваша программа уже может умирать от этого, и тогда у вас действительно будут гораздо большие проблемы, которых вы могли бы полностью избежать, если бы сначала остановили это распределение вовремя.
Поскольку в Lua уже есть встроенная исключительная ситуация, связанная с нехваткой памяти, я просто хотел бы использовать ее, так как это позволяет мне выполнять минимальные необходимые действия (не позволяя сценарию выделять больше материала, но, возможно, позволяя его восстановить) без моего Срыв кода с него.
Поэтому мой текущий план для Lua песочницы с ограничением памяти:
Используйте пользовательский распределитель, который возвращает NULL с ограничением
Разработайте все функции C, чтобы справиться с этим без утечки памяти или других поломок
Но как безопасно проектировать функции C?
Как это сделать, поскольку lua_pushstring и другие всегда могут устранить ошибку с ошибкой, и я не знаю заранее, произойдет ли это заранее? (это был изначально мой вопрос)
Я думаю, что нашел рабочий подход:
Я добавил средство для регистрации указателей, когда я их распределяю, и где я отменяю их регистрацию после того, как с ними покончено. Это означает, что если Lua внезапно выведет меня из моего C-кода, и у меня не будет возможности вычистить, у меня есть все в глобальном списке, который мне понадобится, чтобы исправить этот беспорядок позже, когда я вернусь к контролю.
Это некрасиво или как?
Да, это довольно взломать. Но, скорее всего, это сработает, и, в отличие от "периодической проверки", на самом деле позволит мне установить действительно жесткий лимит и избежать проблем с самим приложением из-за агрессивной атаки.
lua_atpanic() может быть одним из решений для вас, в зависимости от того, какую очистку вам нужно сделать. Это никогда не выдаст ошибку.
В вашем конкретном примере вы также можете создать blubb в качестве пользовательских данных. Тогда Lua автоматически освободит его, когда он покинет стек.