0x80010100: системный вызов не выполнен, исключение ContextSwitchDeadlock - PullRequest
0 голосов
/ 21 февраля 2012

Короче говоря: в приложении C #, которое работает с COM inproc-сервером (dll), я сталкиваюсь с исключением "0x80010100: системный вызов не выполнен", а в режиме отладки также исключение ContextSwitchDeadlock.

Теперь подробнее:

1) Приложение C # инициализирует STA, создает COM-объект (зарегистрированный как «Квартира»); затем подписывается на свою точку подключения и начинает работать с объектом.

2) На некотором этапе COM-объект генерирует множество событий, передавая в качестве аргумента очень большую коллекцию COM-объектов, которые создаются в одной квартире.

3) Обработчик событий на стороне C # обрабатывает вышеуказанную коллекцию, иногда вызывая некоторые методы объектов. На некотором этапе последние вызовы начинают терпеть неудачу с вышеуказанными исключениями.

На стороне COM квартира использует скрытое окно, чей winproc выглядит так:

typedef std::function<void(void)> Functor;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)   
{   
  switch(msg)   
  {   
    case AM_FUNCTOR:
    {
      Functor *f = reinterpret_cast<Functor *>(lParam);
      (*f)();
      delete f;
    }
    break;   
    case WM_CLOSE:   
      DestroyWindow(hwnd);   
    break;   
    default:   
      return DefWindowProc(hwnd, msg, wParam, lParam);   
  }   
  return 0;   
} 

События публикуются в этом окне из других частей COM-сервера:

void post(const Functor &func)
{
  Functor *f = new Functor(func);
  PostMessage(hWind_, AM_FUNCTOR, 0, reinterpret_cast<LPARAM>(f));
}

События - это стандартные реализации ATL CP, связанные с фактическими параметрами, и они сводятся к чему-то вроде этого:

pConnection->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);

В C # обработчик выглядит так:

private void onEvent(IMyCollection objs)
{
  int len = objs.Count; // usually 10000 - 25000
  foreach (IMyObj obj in objs)
  {
    // some of the following calls fail with 0x80010100
    int id = obj.id;
    string name = obj.name;
    // etc...
  }
}

==================

Итак, может ли описанная выше проблема возникнуть только потому, что очередь сообщений в квартире слишком загружена событиями, которые она пытается доставить? Или цикл сообщений должен быть полностью заблокирован, чтобы вызвать такое поведение?

Предположим, что в очереди сообщений есть 2 последовательных события, которые оцениваются как вызов onEvent. Первый вводит управляемый код C #, который пытается повторно ввести неуправляемый код той же квартиры. Обычно это разрешено, и мы делаем это много. Когда, при каких обстоятельствах он может потерпеть неудачу?

Спасибо.

1 Ответ

2 голосов
/ 21 февраля 2012

Это должно работать даже с несколькими квартирами при условии, что:

  • Только один из потоков отвечает на внешние события, такие как сетевой трафик, таймеры, отправленные сообщения и т. Д.
  • Другие потоки обслуживают только COM-запросы (даже если они перезванивают основному потоку во время обработки).

И

  • ни одна из цепочек потоков никогда не заполняется, что препятствует обмену данными с потоком через COM.

Во-первых: Похоже, что некоторые объекты не находятся в той же квартире, что и другие объекты. Вы уверены, что все объекты создаются в STA?

То, что вы описываете, представляет собой классический тупик - два независимых потока, каждый ожидает другого. Это то, что я ожидаю, что произойдет с этим проектом, работающим со сторонами C # и COM в разных потоках.

С вами должно быть все в порядке, если все объекты находятся в одном потоке, а также скрытое окно находится в этом потоке, поэтому я думаю, что вам нужно это проверить. (Очевидно, что это включает любые другие объекты, которые создаются на стороне COM и передаются на сторону C #.)

Вы можете попробовать отладить это, нажав «pause» в отладчике и проверив, какой код был в каждом потоке (если вы видите RPCRT * .DLL, это означает, что вы смотрите на прокси). Вы также можете DebugPrint идентифицировать текущий поток из различных критических точек как на C #, так и на стороне COM, а также на WndProc - все они должны быть одинаковыми.

Во-вторых: он должен работать с несколькими потоками при условии, что только один из потоков генерирует рабочие элементы, а другой ничего не делает, кроме размещения COM-объектов, которые отвечают на вызовы (т.е. не генерирует вызовы из таймеры, сетевой трафик, отправленные сообщения и т. д.), в этом случае может быть переполнена очередь потока и COM не может ответить на вызов.

Вместо использования очереди потоков следует использовать деку, защищенную критической секцией.

  • http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx

    Существует ограничение в 10 000 опубликованных сообщений на одну очередь сообщений. Этот предел должен быть достаточно большим. Если ваше приложение превышает лимит, оно должно быть переработано, чтобы избежать использования большого количества системных ресурсов.

Вы можете сохранить счетчик элементов в / из очереди, чтобы увидеть, если это проблема.
...