![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
AV в ntdll.dll...?
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." благополучно умолчали.