Я закодировал относительно простой протокол связи с использованием общей памяти и общих мьютексов. Но затем я захотел расширить поддержку для связи между двумя .dll, имеющими разное время выполнения. Совершенно очевидно, что если у вас есть несколько std::vector<__int64>
и две dll - одна vs2010, одна vs2015 - они не будут вежливо работать друг с другом. Затем я подумал - почему я не могу сериализовать структуру в стиле ipc с одной стороны и десериализовать ее с другой - тогда vs runtimes будут работать гладко друг с другом.
Короче говоря, я создал отдельный интерфейс для отправки следующего фрагмента данных и для запроса следующего фрагмента данных. Оба работают во время декодирования - это означает, что если у вас есть вектор с 10 записями, каждая строка 1 МБ, а общая память составляет 10 КБ, то для передачи всех данных потребуется 1 * 10 * 1024/10 раз. За каждым следующим запросом следует несколько ожидающих вызовов функций — либо SendChunk, либо GetNextChunk, в зависимости от направления передачи.
Теперь - я хотел, чтобы кодирование и декодирование происходило одновременно, но без какой-либо потоковой передачи - тогда я придумал решение с использованием setjmp и longjmp. Я прикрепляю часть кода ниже, просто для того, чтобы вы поняли, что происходит во всем механизме.
#include "..."
#include <setjmp.h> //setjmp
class Jumper: public IMessageSerializer
{
public:
char lbuf[ sizeof(IpcCommand) + 10 ];
jmp_buf jbuf1;
jmp_buf jbuf2;
bool bChunkSupplied;
Jumper() :
bChunkSupplied(false)
{
memset( lbuf, 0 , sizeof(lbuf) );
}
virtual bool GetNextChunk( bool bSend, int offset )
{
if( !bChunkSupplied )
{
bChunkSupplied = true;
return true;
}
int r = setjmp(jbuf1);
((_JUMP_BUFFER *)&jbuf1)->Frame = 0;
if( r == 0 )
longjmp(jbuf2, 1);
bChunkSupplied = true;
return true;
}
virtual bool SendChunk( bool bLast )
{
bChunkSupplied = false;
int r = setjmp(jbuf2);
((_JUMP_BUFFER *)&jbuf2)->Frame = 0;
if( r == 0 )
longjmp(jbuf1, 1);
return true;
}
bool FlushReply( bool bLast )
{
return true;
}
IpcCommand* getCmd( void )
{
return (IpcCommand*) lbuf;
}
int bufSize( void )
{
return 10;
}
}; //class Jumper
Jumper jumper;
void main(void)
{
EncDecCtx enc(&jumper, true, true);
EncDecCtx dec(&jumper, false, false);
CString s;
if( setjmp(jumper.jbuf1) == 0 )
{
alloca(16*1024);
enc.encodeString(L"My testing my very very long string.");
enc.FlushBuffer(true);
} else {
dec.decodeString(s);
}
wprintf(L"%s\r\n", s.GetBuffer() );
}
Здесь есть пара проблем. После первого вызова setjmp я использую alloca(), которая выделяет память из стека и автоматически освобождает ее по возвращении. alloca может произойти только во время первого перехода, потому что любой вызов функции всегда использует стек вызовов (для сохранения адреса возврата), и это может повредить стек вызовов второго «потока».
Есть несколько статей, в которых обсуждается, насколько опасны setjmp и longjmp, но теперь это какое-то работающее решение. Размер стека (16 Кб) является резервом для следующих вызовов функций - decodeString и т. д. - его можно увеличить, если его недостаточно.
Попробовав этот код, я заметил, что код x86 работает нормально, а 64-но не работает - у меня возникла проблема, аналогичная описанной здесь:
Во время раскрутки обнаружен недопустимый или невыровненный стек операция
Как и предлагалось в статье, я добавил ((_JUMP_BUFFER *)&jbuf1)->Frame = 0;
вид сброса - и после этого 64-битный код начал работать. В настоящее время библиотека не использует какой-либо механизм исключений, и я не планирую его использовать (постараюсь поймать все, если это необходимо, в вызовах функций encode* decode*).
Итак, вопросы:
Является ли приемлемым решением отключить раскручивание в коде? (
((_JUMP_BUFFER *)&jbuf1)->Frame = 0;
) Что на самом деле означает раскручивание в контексте setjmp / longjmp ?Видите ли вы какие-либо потенциальные проблемы с данным фрагментом кода?
setjmp/longjmp
код; Мне еще предстоит найти проблему, для которой это стоящий подход. Такого рода проблемы только подкрепляют эту оценку. Это очень большая причиненная самому себе боль. - person MSalters   schedule 20.09.2016setjmp()
иlongjmp()
(в основном все, что я знаю о них, является причиной не их использовать), но если вам нужны сопрограммы, возможно ли использоватьboost::coroutine
, C ++17 coroutine TS или библиотеку COROUTINE? Или я что-то здесь не понимаю? - person Justin Time - Reinstate Monica   schedule 21.09.2016but isn't my approach a little bit simpler than coroutines
видя проблемы, которые у вас есть сейчас, я бы, вероятно, не согласился. Я ничего не вижу в текущем коде, но вы должны проверить свои типы. Похоже, где-то вы неверно указали на прыжки. Имейте в виду, что указатели вдвое больше в 64-битной версии, поэтому вам следует проверить там. И поставьте уровень предупреждений на максимум и пройдитесь по ним. Возможно, они покажут вам ошибку. - person Hayt   schedule 28.10.2016