Хотя асинхронный ввод-вывод (неблокирующие дескрипторы с select / poll / epoll / kqueue и т. Д.) - не самая документированная вещь в Интернете, есть несколько хороших примеров.
Однако во всех этих примерах, определив дескрипторы, возвращаемые при вызове, просто есть заглушка 'do_some_io(fd)
'. Они действительно не объясняют, как наилучшим образом приблизиться к фактическому асинхронному вводу-выводу в таком методе.
Блокировка ввода-вывода очень удобна и проста для чтения кода. Неблокирующее асинхронное IO, с другой стороны, волосатое и грязное.
Какие есть подходы? Что является надежным и читаемым?
void do_some_io(int fd) {
switch(state) {
case STEP1:
... async calls
if(io_would_block)
return;
state = STEP2;
case STEP2:
... more async calls
if(io_would_block)
return;
state = STEP3;
case STEP3:
...
}
}
или, возможно, (ab), используя вычисленные goto GCC:
#define concatentate(x,y) x##y
#define async_read_xx(var,bytes,line) \
concatentate(jmp,line): \
if(!do_async_read(bytes,&var)) { \
schedule(EPOLLIN); \
jmp_read = &&concatentate(jmp,line); \
return; \
}
// macros for making async code read like sync code
#define async_read(var,bytes) \
async_read_xx(var,bytes,__LINE__)
#define async_resume() \
if(jmp_read) { \
void* target = jmp_read; \
jmp_read = NULL; \
goto *target; \
}
void do_some_io() {
async_resume();
async_read(something,sizeof(something));
async_read(something_else,sizeof(something_else));
}
Или, возможно, исключения C ++ и конечный автомат, чтобы рабочие функции могли запускать бит отмены / возобновления, или, возможно, конечный автомат, управляемый таблицей?
Дело не в том, чтобы заставить его работать, а в том, чтобы сделать его обслуживаемым, что я гоняюсь!