Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

AV в ntdll.dll...?


Страницы:   1 2

istok ©   (2011-03-16 22:46:00) 

есть такой код для записи в файл:


CS: SyncObjs.TCriticalSection;
....

procedure WriteLog(Str: string; AFileName: string);
var
 f: TextFile;
begin
 CS.Enter;
 try
   AssignFile(f, AFileName);
   try
     if FileExists(AFileName) then
       Append(f)
     else
       Rewrite(f);
     Writeln(f, Format('[%s](%d): %s', [DateTimeToStr(Now), GetCurrentThreadId, str]));
   finally
     CloseFile(f);
   end;  
 finally
   CS.Leave;
 end;
end;


при интенсивном использовании этого кода разными потоками иногда получаю вот это (eurekalog):

Access violation at address 00000000. Read of address 00000000.

---------------------------------------------------------------------------------------------------------------
|Address |Module        |Unit                |Class                    |Procedure/Method             |Line    |
---------------------------------------------------------------------------------------------------------------
|Running Thread: ID=10680; Priority=??; Class=                                                                |
|-------------------------------------------------------------------------------------------------------------|
|775E7760|ntdll.dll     |                    |                         |RtlLeaveCriticalSection      |        |
|775E6A22|ntdll.dll     |                    |                         |NtWaitForSingleObject        |        |
|775E7012|ntdll.dll     |                    |                         |KiUserExceptionDispatcher    |        |
|0053F1CA|wecmsg.exe    |WebFilterUnit.pas   |TWebFilter               |InStrings                    |2016[3] |
|0053F17C|wecmsg.exe    |WebFilterUnit.pas   |TWebFilter               |InStrings                    |2013[0] |
|0053F655|wecmsg.exe    |WebFilterUnit.pas   |TWebFilter               |CheckForBlockedUrl           |2131[53]|

Метод InStrings через цепочку событий (синхронизируются) в итоге вызывает WriteLog:

function TWebFilter.InStrings(AStr: string; AStrings: TStrings): Boolean;
var
 i: Integer;
begin
 Result := False;

 DoMessage('InStrings, AStr='+AStr+', AStrings.Text='+AStrings.Text);

procedure TWebFilter.DoMessage(AMessage: string; ADebugOnly: Boolean = True);
begin
 if Assigned(FOnMessage) then
   FOnMessage(Self, AMessage, ADebugOnly);
end;

procedure TWebFilterThread.DoOnThrdMessage(Sender: TObject;
 AMessage: string; ADebugOnly: Boolean = True);
begin
 DoSyncMessage(AMessage, ADebugOnly);
end;

procedure TWebFilterThread.DoSyncMessage(AMessage: string; ADebugOnly: Boolean = True);
begin
 FMessage := AMessage;
 FDebugOnly := ADebugOnly;
 Synchronize(DoMessage);
end;

procedure TWebFilterThread.DoMessage;
begin
 if Assigned(FOnThrdMessage) then
   FOnThrdMessage(Self, FMessage, FDebugOnly);
end;

procedure TThreadedWebFilter.DoOnThrdMessage(Sender: TObject;
 AMessage: string; ADebugOnly: Boolean = True);
begin
 if Assigned(FOnMessage) then
   FOnMessage(Sender, AMessage, ADebugOnly);
end;

procedure TweClientMessengerService.DoOnWebFilterMessage(Sender: TObject;
 AMessage: string; ADebugOnly: Boolean = True);
begin
 AddLog('WebFilter, DoOnMessage: '+AMessage, ADebugOnly);
end;

procedure TweClientMessengerService.AddLog(AStr: string; ADebugOnly: Boolean = True);
begin
 if FDebugMode then
 try
   WriteLog(AStr, FweFolders.dirCurrent + 'weClientMessengerLog_DEBUG.txt');
 except
 end;
end;


я в нужном месте ищу баг? что может значить данный AV в ntdll.dll   RtlLeaveCriticalSection ? Критич. секция не срабатывает почему-то или дело в чем-то другом?


han_malign   (2011-03-18 16:51:00) [1]


> следует вызов типа InStrings(Form.Memo1.Lines);
> VCL не потоко-безопасно

- при чтениииз неконсистентного композитного объекта, может возникнуть AV чтения- не разрушающее ни кучи, ни стека... При этом at address - ну никак не будет нулевым.

А говорил я - не ` что "этого" от "этого" быть не может`, а что если верить Эврике, и в том случае, если повезло с адекватностью снимка стека - в методе CheckForBlockedUrl, или в каком то методе ниже по списку лога, надо искать вызов вложенной функции в которой разрушается стек...
Этим я всего лишь отвечал АПП - что Эврика в некоторых случаях может сузить область поиска "странных ошибок", если ума хватит...

DIXI


istok   (2011-03-18 16:26:00) [2]

вобщем я закрыл дырки в виде пары вызовов методов потока twebfilter другими потоками с помощью критич. секций и всё стало ок.

т.е. в методы типа twebfilter.InStrings реально входили несколько потоков без синхронизации.

а прелесть в том, что без логирования не юзается tstrings в паре мест и соответственно не возникает ошибки - оптимизация компиляции или что тут причина "невылета" ошибки мне не понять, да и не важно)

всем спасибо за помощь!


sniknik   (2011-03-18 15:55:00) [3]

> - что я делаю не так?
теоретизируешь и вносишь сумятицу в абсолютно, кристально, ясную ситуацию - есть "дырка"? закрыть!

указаны уже две. а автор до сих пор "менжуется" - считается ли "дырка" "дыркой" если, во первых очень хочется  "чтобы не считалось", о во вторых тут еще "опровергатели" разные смуту вносят, говорят что "этого" от "этого" быть не может.

> "в абстрактном VMT[]" лежит @_AbstractError, а не nil...
а ты вообще понимаешь о чем речь? при чем тут абстрактный базовый класс? он всегда указывается, как подходящий для "передач потомков".
а после следует вызов типа InStrings(Form.Memo1.Lines); (Lines объявлен абстрактный ну надо же) у которого есть события, а передается в поток...
вот о чем речь, а не о том что там используется абстрактный, и значит будет абстракт еррор, класс. VCL не потоко-безопасно, все, четко и ясно, вопрос закрыт, и никакие манипуляции с цифрами этого не изменят.
хотите еще "потрещать" на эту тему? с автором. он похоже желает чтобы его переубедили, что все у него ок, менять ничего не надо, и это все винда виновата...


han_malign   (2011-03-18 15:36:00) [4]


> sl не объявлена.

- сначала только nil отработал, а после отработки AbstractError - лень было все заново копировать... и инкрементальное обновление сбойнуло :))


RWolf   (2011-03-18 15:26:00) [5]


>  [27]

переменная sl не объявлена.


han_malign   (2011-03-18 15:22:00) [6]


> > function TWebFilter.InStrings(AStr: string; AStrings: TStrings): Boolean;

program av;
{$APPTYPE CONSOLE}
uses Classes, SysUtils;
procedure _fail(sl: TStrings);
begin
  Writeln(sl.Text);
end;

begin
 try
  _fail(nil);
 except
 on E: Exception do Writeln(E.Message);
 end;
 sl:= TStrings.Create;
 try
  _fail(sl);
 except
 on E: Exception do Writeln(E.Message + ' at ' + IntToHex(Cardinal(ExceptAddr), 8));
 end;
 sl.Free;
end.

---------
Access violation at address 004126CBin module 'av.exe'. Read of address 00000000
Abstract Error at 0040AEEF

- что я делаю не так?

Если что - то(в отличие от плюсов) "в абстрактном VMT[]" лежит @_AbstractError, а не nil...


sniknik   (2011-03-18 14:11:00) [7]

> и даже не ссылки на экземпляр класса...
> function TWebFilter.InStrings(AStr: string; AStrings: TStrings): Boolean;


han_malign   (2011-03-18 14:01:00) [8]


> > FOn...(... здесь...);

- а at address 00000000- здесь(...) - потому что это адрес инструкции- а не параметра, и даже не ссылки на экземпляр класса...

Такая ситуация может возникать только в двух случаях:
1. при параметризованном переходе (call [], jmp [])
2. (для x86) при выходе из процедуры(ret) с запоротой точкой возврата


Loginov Dmitry   (2011-03-17 23:19:00) [9]


> Критич. секция не срабатывает почему-то или дело в чем-то
> другом?


Критическая секция - самый примитивный объект синхронизации. Он не глючит. Естественно, критическая секция должна быть создана (проинициализирована) до использования. А вот если ее не проинициализировать, то можно нарваться на какие угодно проблемы :)

Насчет "потоконебезопасности" DateTimeToStr. Здесь подразумевается, что результат работы данной функции может оказаться неожиданным, т.к. переменная, определяющая формат перевода даты в строку является глобальной, а значит теоретическиизменить ее может параллельно выполняющийся код в любой момент времени. Привести к AV она не может. Не так давно где-то это обсуждалось.


Дмитрий Белькевич   (2011-03-17 21:17:00) [10]

Закоменть подозрительный кусок - DateTimeToStr, либо еще что-то. Поможет - значит там проблему нужно искать. Нет - значит еще что-то.


sniknik   (2011-03-17 19:41:00) [11]

> -если пишет в tstrings только один поток, а читают несколько.. то ок или нет?
блин, прямо мучения латентного голубого, типа "ну один раз не п. это понятно, но вот если только с одним, любимым, это же не считается... ведь правда? ок, или нет?"

по справке того же DateTimeToStr (для которого "понятно, что not thread-safe") которая не безопасна, и только потому что основной поток, один!, в используемые ей данные что то пишет.
если бы это "не считалось", то весь VCL тут же стал бы потоко безопасным, наполовину, для чтения. что не так.

> как сделать так, чтобы method1 (из-под thread1) вызывал method2 таким образом, чтобы method2 выполнялся в контексте thread2  ?
никак, код всегда работает в контексте того потока в котором вызывается. но можно -
> а данные на запись ему посылать в сообщении. ИМХО конечно.


Anatoly Podgoretsky   (2011-03-17 15:05:00) [12]

> istok  (17.03.2011 14:49:20)  [20]

Теоритичекий говоришь?
Пусть было "Мама мыла раму" и в некоторый момент раму заменили на папу. Вот
и разрушение.
Так что требуется синхронизация или ThreadList


istok   (2011-03-17 14:49:00) [13]


> А как же тогда там записи оказались?


-если пишет в tstrings только один поток, а читают несколько.. то ок или нет?
(вопрос больше теоретический)

практическая часть свелась к следующему:

есть method1 в thread1 и  method2 в thread2.
насколько я вижу, если method1 (из-под thread1) вызывает method2, то method2 выполняется в контексте thread1.

как сделать так, чтобы method1 (из-под thread1) вызывал method2 таким образом, чтобы method2 выполнялся в контексте thread2  ?


Anatoly Podgoretsky   (2011-03-17 14:41:00) [14]

> istok  (17.03.2011 14:21:17)  [17]

> stringlist безусловно непотокобезопасен при записи, но если речь о чтении,
> то как?

А как же тогда там записи оказались?


istok   (2011-03-17 14:36:00) [15]

кажется я всё понял..

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


istok   (2011-03-17 14:21:00) [16]

пока пришел к выводу что не синхронизировался DoMessage, он потом вызывает методы\события которые синхронятся через synchronize и естественно в writelog всё что идет по этому пути выдаёт один threadid

значит причина несинхрона где-то ДО этого..  

поставлю секции на DoMessage, и ряд общих методов для потоков его вызывающих..  когда добавил вывод threadid в DoMessage, то увидел что разные threadid выводятся..

я уже запутсля в понимании того, что можно делать потокам без синхрона, а что нельзя...  vcl-то понятно что низя, но тут одни переменные, часто tstringlist..  причем локальные... есть tstringlist у одного потока, есть у другого.. они выводят их через domessage на печать, передавая параметром tstringlist.text, а синхрон идёт уже после этого вызова.. криминально?

но даже если tstringlist переменная есть глобальная, доступная всем потокам - _ЧИТАТЬ_ одновременно всем можно или синхронить надо??

stringlist безусловно непотокобезопасен при записи, но если речь о чтении, то как?


sniknik   (2011-03-17 12:09:00) [17]

> FOn...( ...здесь...);


sniknik   (2011-03-17 12:08:00) [18]

>> аргумент равен NIL

> if Assigned(FOn...) then FOn...(...);
> - у автора делается честно, больше callback-ов там вроде не видать...
это к вопросу об:
это не аргумент, это проверка метода обработчика, аргумент это FOn...( ...); здесь.


han_malign   (2011-03-17 10:48:00) [19]


> аргумент равен NIL

if Assigned( FOn...) then FOn...(...);
- у автора делается честно, больше callback-ов там вроде не видать...
это к вопросу об:
> как это поможет в случае "Access violation at address 00000000

- раскрутка стека помогает(иногда) локализовать место, где надо искать вызов функции, в которой осуществляется выход за границу локальной( размещенной на стеке) структуры.
Причем 1-4 младших байта " at address" совпадает с тем, что за эту границу писалось...


Anatoly Podgoretsky   (2011-03-17 09:08:00) [20]

Ничего криминального, только аргумент равен NIL
Недавно был аналогичный вопрос, тоже предложили восспользоваться Еврикой или подобным, а на уточняющий вопрос, как это поможет в случае "Access violation at address 00000000. Read of address 00000000." благополучно умолчали.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо