Модераторы: Poseidon, Snowy, bems, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Странное падение, Никак не пойму 
V
    Опции темы
CynicRus
Дата 25.3.2015, 21:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 248
Регистрация: 31.5.2012

Репутация: нет
Всего: 5



Приветствую уважаемых форумчан. Если сервак, написанный на Delphi XE 3. Чистые сокеты + IOCompltitionPorts. Работает быстро, всё вроде хорошо. Но, проработав несколько часов - раз и зависон. Интервал между зависонами абсолютно рандомный, от 2 часов...а может сутки-двое простоять. 

При этом при отладке - отладчик показывает вообще какую-то ерунду. Эксепшн при дисконнекте пользователя, где следующий код:
Код

procedure TTCPServer.Disconnect(ID: Integer; FlushData: Boolean);
var
  Conn: TTCPConnection;
begin
  Lock('Disconnect');
  try

    Conn := FConnections.ItemByID(ID);
    if Conn = nil then Exit;
    FConnections.Remove(Conn);
  finally
    Unlock('Disconnect');
  end;
  try
  if FlushData then
    uWinSock2.shutdown(Conn.Socket, SD_BOTH);
  Conn.CloseSocket;
  ClientDisconnected(Conn);
  Conn.Free;
  except
   on E: Exception do
    raise Exception.Create('TTCPServer.Disconnet');
  end;
end;


Начинаем падение отсюда, со строки Conn.free - разрушение объекта, далее следуем в деструктор коннекта:
Код

destructor TTCPConnection.Destroy;
begin
  Self.CloseSocket;
  if FBuffer <> nil then
    FBuffer.Free;
end;


Оттуда - идём в очистку Buffer'а коннекта :
Код

destructor TFastStream.Destroy;
begin
  Clear;
  FChunks.Free;
  //inherited;
end;

procedure TFastStream.Clear;
var
  I: Integer;
begin
  for I := 0 to FChunks.Count - 1 do
    TFastStreamChunk(FChunks[I]).Free;
  FChunks.Clear;
end;

затем следуем в обработчик уже самой винды, и в конце концов получаем AccessViolation, а после нажатия на Ok - OSError 5, access denied. Есть у кого какие-нибудь мысли, как победить это дело? Я уже весь мозг себе сломал за неделю боёв с отладчиком smile 

PM MAIL   Вверх
kami
Дата 25.3.2015, 22:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 23
Всего: 72



Цитата(CynicRus @  25.3.2015,  21:26 Найти цитируемый пост)
 for I := 0 to FChunks.Count - 1 do
    TFastStreamChunk(FChunks[I]).Free;

Глюк здесь. Сделай

for i:=FChunks.Count-1 downto 0



Добавлено @ 22:22
Это раз.
Второе - что такое FChunks?
Если TObjectList - то вообще эти телодвижения лишние, сделать OwnObjects:=True  и все само уйдет.
А так - еще смущает после цикла 

Цитата

FChunks.Clear;


Неее, это у меня глюк smile

Добавлено через 6 минут и 31 секунду
Цитата(CynicRus @  25.3.2015,  21:26 Найти цитируемый пост)
if Conn = nil then Exit;

Вот всегда интересовало, но руки не доходят попробовать, т.к. сам такое стараюсь не писать. При Exit отработает ли finally-секция? Или стек будет порушен?

Это сообщение отредактировал(а) kami - 25.3.2015, 22:23
PM MAIL WWW   Вверх
kami
Дата 25.3.2015, 22:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 23
Всего: 72



Ну и помимо того, что в деструкторах нужно inherited, иначе память не освободится окончательно, мысля про "некрасивый exit из try-finally" мне кажется привлекательной:  TTCPServer.Disconnect -> вызов деструктора TTCPConnection -> в нем CloseSocket -> опять по цепочке в TTCPServer.Disconnect, но уже с отрабатывающим exit -> глюк
PM MAIL WWW   Вверх
CynicRus
Дата 26.3.2015, 07:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 248
Регистрация: 31.5.2012

Репутация: нет
Всего: 5



А вот эту цепочку то я и не узрел smile Ну-с поправил, посмотрим как вести себя будет.

UPD: да всё так же. +-. Причём, когда этот тред клинит - выходит такая картина. Сервер подключения принимает, и на этом всё.

Это сообщение отредактировал(а) CynicRus - 26.3.2015, 09:06
PM MAIL   Вверх
CynicRus
Дата 26.3.2015, 11:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 248
Регистрация: 31.5.2012

Репутация: нет
Всего: 5



При падении - вот как выглядит стек:
user posted image

TTCPServerBuffered.disconnet:
Код

procedure TTCPServerBuffered.Disconnect(ID: Integer; FlushData: Boolean);
var
  Conn: TTCPConnection;
begin
  if FlushData then
    repeat
      Lock('Disconnect');
      try
       try
        Conn := FConnections.ItemByID(ID);
        if (Conn = nil) or (not Conn.Sending) then Break;
       except
         on E: Exception do
          raise;
       end;
      finally
        Unlock('Disconnect');
      end;

      Sleep(10);
    until False;

  inherited;
end;



А ClientThread:
Код

procedure TTCPServer.ClientThread;
var
  {$IfDef DELPHI_XE_UP}
  ID: NativeUInt;
  {$Else}
  ID: NativeUInt;
  {$EndIf}
  Error, BytesTransferred: Cardinal;
  pOvr: POverlapped;
  pOper: PTCPOperation;
begin
  while not FThreadPoolAborted do
  begin
    ID := 0;
    BytesTransferred := 0;
    if not GetQueuedCompletionStatus(FCompletionPort, BytesTransferred, ID,
         POverlapped(pOvr), 250) then
    begin
      Error := GetLastError;
      if (Error = WAIT_TIMEOUT) or (ID = 0) then
        Continue;
    end;

    // Клиент отключился
    if (BytesTransferred = 0) or (pOvr = nil) then
    begin
      if pOvr <> nil then
        OperationDestroy(pOvr, True);
      Disconnect(ID);
      Continue;
    end;

    pOper := Pointer(pOvr^.Offset);
    case pOper^.OperationType of
      ServerOperationRecv: // Приём
        begin
          InterlockedDecrement(FOperationsPending);
          if BytesTransferred > 0 then
            DataReceived(ID, pOper^.Buffer.buf, BytesTransferred);
          try
            if ID and $FFFFF000 <> 0 then Exit;
            ReceiveDataPending(pOvr);
          except
            OperationDestroy(pOvr, True);
            Disconnect(ID);
          end;
        end;

      ServerOperationSend:
        begin
          InterlockedDecrement(FOperationsPending);
          pOper^.Success := BytesTransferred = pOper^.Buffer.len;
          // Отправитель ожидает результат?
          if pOper^.OperationDone <> 0 then
          begin
            SetEvent(pOper^.OperationDone);
            WaitForSingleObject(pOper^.ReadyToDestroy, INFINITE);
          end;
          if pOper^.Success then
            DataSended(ID, pOper^.Buffer.buf, BytesTransferred)
          else
            Disconnect(ID);
          OperationDestroy(pOvr, True);
        end;
    end;
  end;

  InterlockedDecrement(FActiveThreadsCount);
end;


Это сообщение отредактировал(а) CynicRus - 26.3.2015, 11:17
PM MAIL   Вверх
kami
Дата 26.3.2015, 19:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 23
Всего: 72



Есть подозрение, что раз непонятно, почему возникет ошибка, значит мы видим следствие. А причина совершенно в другом месте. Например - кто-то портит память, которую потом пытаемся освободить, двойной вызов Free и т.п.
Подключи FastMM в FullDebugMode, если подозрения имеют основание - он покажет всю подноготную
PM MAIL WWW   Вверх
CynicRus
Дата 26.3.2015, 19:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 248
Регистрация: 31.5.2012

Репутация: нет
Всего: 5



Цитата(kami @ 26.3.2015,  19:21)
Есть подозрение, что раз непонятно, почему возникет ошибка, значит мы видим следствие. А причина совершенно в другом месте. Например - кто-то портит память, которую потом пытаемся освободить, двойной вызов Free и т.п.
Подключи FastMM в FullDebugMode, если подозрения имеют основание - он покажет всю подноготную

А дедлок не может вести к такому поведению? Или наоборот, где-то не хватает критсекции? Потому что я с эврикой весь код облизал, ни единой утечки:(
PM MAIL   Вверх
kami
Дата 26.3.2015, 21:51 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 23
Всего: 72



Цитата

А дедлок не может вести к такому поведению?

Дед Лок к AV не приведет smile
Эврикой не пользовался, да и утечки сейчас ни при чем - от утечек может быть OutOfMemory, но не AV.  А эврика умеет находить такие вещи, как попытки обращения к освобожденной памяти?
К примеру, у FastMM это выглядит так (перевод далеко не дословный):
"произведена попытка обращения к памяти, которая ранее была освобождена. Сейчас будет AV"
"Ранее эта память была задействована: " (тут стек методов, приведший к вызову конструктора или GetMem и т.п."
"Потом память была освобождена:" (тут стек вызовов до деструктора и т.п.)
"Текущий стек, который приведет к AV:"... 

Это сообщение отредактировал(а) kami - 26.3.2015, 21:56
PM MAIL WWW   Вверх
CynicRus
Дата 27.3.2015, 19:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 248
Регистрация: 31.5.2012

Репутация: нет
Всего: 5



Помедитировал ещё с отладчиком. И возникло у меня подозрение на рукописную хэш таблицу. Досталась от коллеги, который видимо писал этот класс во времена Delphi 7, когда о дженериках в Delphi ещё не мечтали. Так вот, подумал я - а может быть его просто выпилить, и заменить на дженерики? Выпилил, заменил. 6 часов - полёт нормальный, заодно сэкономил 11 мегабайт памяти в рантайме. В этом классе содержались указатели на объект, бывало, что содержался 1 и тот же указатель, под разными ключами. Так вот похоже, что из-за этой ерунды - я и получал неуловимый глюк. Когда объект уже освободился, а в хэш-таблице где-то чего-то недотиралось. Всем спасибо за внимание.

PS: TDictionary рвёт ту самоделку в клочья-)
PM MAIL   Вверх
kami
Дата 29.3.2015, 10:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 23
Всего: 72



Цитата(CynicRus @  27.3.2015,  19:46 Найти цитируемый пост)
может быть его просто выпилить, и заменить на дженерики? Выпилил, заменил.

Уже, наверное, нет возможности проверить, но было бы интересно - отловил бы FastMM это "недотирание" или нет...
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0486 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.