ПРЕДПОСЫЛКИ
Убедившись, что сопрограммы C ++ без стеков довольно классные . Я реализовал сопрограммы для моей кодовой базы и понял странность в final_suspend.
CONTEXT
Допустим, у вас есть следующая final_suspend функция:
final_awaitable final_suspend() noexcept
{
return {};
}
И, final_awaitable был реализован следующим образом:
struct final_awaitable
{
bool await_ready() const noexcept
{
return false;
}
default_handle_t await_suspend( promise_handle_t h ) const noexcept
{
return h.promise().continuation();
}
void await_resume() const noexcept {}
};
Если продолжение здесь было получено атомарно из очереди задач и задачи очередь может быть пустой (что может произойти в любое время между await_ready и await_suspend ), тогда await_suspend должна иметь возможность вернуть пустое продолжение.
Насколько я понимаю, когда await_suspend возвращает дескриптор, возвращенный дескриптор немедленно возобновляется (5.1 в N4775 черновик). Таким образом, если здесь не было доступного продолжения, любое приложение завершается сбоем, так как резюме вызывается на недопустимом дескрипторе сопрограммы после получения его от await_suspend .
Ниже приведен порядок выполнения:
final_suspend Constructs final_awaitable.
final_awaitable::await_ready Returns false, triggering await_suspend.
final_awaitable::await_suspend Returns a continuation (or empty continuation).
continuation::resume This could be null if a retrieved from an empty work queue.
По-видимому, не задана проверка для действительного дескриптора (как, если await_suspend возвращает bool).
ВОПРОС
- Как вы предполагаете добавить рабочую очередь в await_suspend без блокировки в этом случае? Ищите масштабируемое решение.
- Почему базовая реализация сопрограммы не проверяет действительный дескриптор.
Придуманный пример, вызывающий cra sh, здесь .
ИДЕИ РЕШЕНИЯ
Использование фиктивного задания с бесконечным l oop из co_yield . Это напрасно потраченные впустую циклы, и я предпочел бы не делать этого, также мне нужно было бы создать отдельные дескрипторы для фиктивной задачи для каждого потока выполнения, и это просто кажется глупым.
Создание специализации std :: coroutine_handle , где резюме ничего не делает, возвращая экземпляр этого дескриптора. Я бы предпочел не специализироваться на стандартной библиотеке. Это также не работает, потому что coroutine_handle <> не имеет done () и resume () как виртуальные.
РЕДАКТИРОВАТЬ 1 16/03/2020 Вызовите продолжение () для атомарного извлечения продолжения и сохранения результата в структуре final_awaitable , await_ready мировое возвращение истина, если не было доступного продолжения. Если бы было доступно продолжение, await_ready вернуло бы false, тогда был бы вызван await_suspend , и продолжение вернулось (немедленно возобновив его). Это не работает, потому что значение, возвращаемое задачей, сохраняется в фрейме сопрограммы, и если значение все еще необходимо, фрейм сопрограммы не должен уничтожаться. В этом случае он уничтожается после вызова await_resume для final_awaitable . Это проблема, только если задача является последней в цепочке продолжений.
РЕДАКТИРОВАТЬ 2 - 20/03/2020 Игнорировать возможность возврата работоспособного Дескриптор обычной процедуры от await_suspend . Возобновить продолжение только из программы высшего уровня. Это не кажется эффективным.
01/04/2020
Я до сих пор не нашел решение, которое не имеет существенные недостатки. Я полагаю, что причина, по которой я это понял, заключается в том, что await_suspend, по-видимому, предназначен для решения именно этой проблемы (возможность возврата corountine_handle). Я просто не могу понять шаблон, который был задуман.