Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2007.10.14;
Скачать: [xml.tar.bz2];

Вниз

Очередной баг или так и задумано   Найти похожие ветки 

 
oxffff ©   (2007-07-29 23:26) [0]

Существует непустая глобальная переменная

b:Iunknown;

Представьте два потока, которые одновременно пытаются выполнить следующий код

b:=nil;

Вот непосредственно код соответствия на Asm

function _IntfClear(var Dest: IInterface): Pointer;
asm
       MOV     EDX,[EAX]
       TEST    EDX,EDX
       JE      @@1
       MOV     DWORD PTR [EAX],0
       PUSH    EAX
       PUSH    EDX
       MOV     EAX,[EDX]
       CALL    DWORD PTR [EAX] + VMTOFFSET IInterface._Release
       POP     EAX
@@1:
end;

Я его слегка упрощу, чтобы более легче понять его. Уберем проверку на nil и сохранение eax;

function _IntfClear(var Dest: IInterface): Pointer;
asm
       MOV     EDX,[EAX]
       MOV     DWORD PTR [EAX],0
        PUSH    EDX
       MOV     EAX,[EDX]
       CALL    DWORD PTR [EAX] + VMTOFFSET IInterface._Release
end;

Теперь вернемся к нашем двум потокам.
Вообразите ситуацию, когда первый поток еще не выполнил _IntfClear.
Происходит переключение на второй поток, который выполняет _IntfClear и выделяет память по потому же адресу и обнуляет ее.

Вот сериализованный поток иструкций с комментариями

procedure TForm1.Button1Click(Sender: TObject);
var a:TinterfacedObject;
begin
a:=TinterfacedObject.Create;
b:=a;
asm
push ebx; //Сохраняем ebx.

//Первый поток
 mov eax,b;

   push eax;   //Сохранение контекста потока и переключение на второй поток.

   //Второй поток

        mov eax,b; //
        mov b,0;
        push eax;
        push eax;
        mov eax,[eax];
        call [eax+$08];

        pop eax;      // Была выделена память на том же самом месте, где и объект и была очищена.
        mov [eax],0;

    //обратное переключение на первый поток
    pop eax;    //Восстановление контекста потока .

 mov b,0;
 push eax;
 mov eax,[eax];
 call [eax+$08]; //    Здесь мы получаем AV.  

pop ebx;//Восстанавливаем ebx
end;
b:=nil;
end;

Получение AV вызвано тем, что  текущая реализация _IntfClear расчитана на один поток.

Вопрос такой

это баг. Или As designed?


 
oxffff ©   (2007-07-29 23:28) [1]

Такая ситуация может возникнуть и для  _IntfCopy


 
jack128_   (2007-07-29 23:35) [2]

А с чего ты взял, что дельфя такие вещи должна синхронизировать??


 
oxffff ©   (2007-07-29 23:40) [3]


> jack128_   (29.07.07 23:35) [2]
> А с чего ты взял, что дельфя такие вещи должна синхронизировать?
> ?


В смысле?

Я привел пример, который  демонстрирует потенциальную возможность AV при одновременной очистке интерфейсной переменной из двух разных потоков.

Собственно _IntfCopy и _IntfClear можно поправить, чтобы подобной ситуации не возникло.


 
oxffff ©   (2007-07-29 23:41) [4]


> jack128_   (29.07.07 23:35) [2]
> А с чего ты взял, что дельфя такие вещи должна синхронизировать?
> ?


А взял я это с того, что interface это тип Delphi. И код финализации должен быть потокозащищенным.

:)


 
oxffff ©   (2007-07-29 23:50) [5]

Для строк тоже самое. См. _LStrClr


 
Lacmus ©   (2007-07-29 23:50) [6]

Почему бы не продолжить :)

b: string;
b: array of ...;

Потокозащищенным, наверно, должен быть код работы с глобальными переменными.


 
oxffff ©   (2007-07-29 23:58) [7]


> Потокозащищенным, наверно, должен быть код работы с глобальными
> переменными.


Ну не только глобальными. А с разделяемыми.

Вы конечно скажите, так это и есть защищаемый ресурс.

Только, если внимательно рассмотреть.
Операция присваивания должна быть атомарна.
И защищать ее не следуют.

Собственно спор здесь не уместен.

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


 
Lacmus ©   (2007-07-30 00:06) [8]

>Я думаю вы не откажитесь, от того чтобы операция присваивания  была атомарна?

Речь идет об этом ?

MOV     DWORD PTR [EAX],0


 
jack128_   (2007-07-30 00:32) [9]


> Операция присваивания должна быть атомарна.

Ну..В свете того, что неявное приведение типов у нас теперь может быть перегружено, это более чем спорное утверждение.


 
Anatoly Podgoretsky ©   (2007-07-30 00:34) [10]

Я бы ни чему, кроме ассемблера не доверял.


 
oxffff ©   (2007-07-30 08:51) [11]


> Речь идет об этом ?
>
> MOV     DWORD PTR [EAX],0


Вот об этом

MOV     EDX,[EAX]
TEST    EDX,EDX
 JE      @@1
 MOV     DWORD PTR [EAX],0

Заменить либо lock cmpxсhg, либо просто xchg


 
Lacmus ©   (2007-07-30 11:08) [12]

>oxffff ©   (30.07.07 08:51) [11]

Предлагаете заменить все присвоения в программе или только это ?


 
oxffff ©   (2007-07-30 11:23) [13]


> Lacmus ©   (30.07.07 11:08) [12]
> >oxffff ©   (30.07.07 08:51) [11]
>
> Предлагаете заменить все присвоения в программе или только
> это ?


Предлагаю сделать threadSafe assignment

http://qc.codegear.com/wc/qcmain.aspx?d=49745

Все Ref Counting типы (string, DynArray, Interface).

Т.е модифицировать по аналогии _IntfClear, _IntfCopy и др.


 
Lacmus ©   (2007-07-30 11:30) [14]

>oxffff ©   (30.07.07 11:23) [13]

Если переменная объявлена как

var
 gPtr: Pointer = nil;

то с ее присвоением делать ничего не надо, так ?


 
oxffff ©   (2007-07-30 11:38) [15]


> Lacmus ©   (30.07.07 11:30) [14]
> >oxffff ©   (30.07.07 11:23) [13]
>
> Если переменная объявлена как
>
> var
>  gPtr: Pointer = nil;
>
> то с ее присвоением делать ничего не надо, так ?


Именно так.

Ближе к делу. :)


 
Lacmus ©   (2007-07-30 11:56) [16]

т.е. код


 if gPtr = nil then
   gPtr := TMyClass.Create(nil)


будет threadSafe ?


 
oxffff ©   (2007-07-30 12:19) [17]


> Lacmus ©   (30.07.07 11:56) [16]
> т.е. код
>
>
>  if gPtr = nil then
>    gPtr := TMyClass.Create(nil)
>
>
> будет threadSafe ?


Нет. Не будет.

Т.е. для указателя выполняется код

mov [ebp-$..],eax.

С предыдущим значением нет манипуляций.

Я предлагаю сделать безопасное присваивание для string, DynArray, Interface.

А для ref counting типов выполняется еще и Counting.

Разница лишь в том, что операция изъятия и изменения значения должна быть атомарна.

Что не приведет к повторному counting для другого потока.

Хотя я тут подумал и нашел проблему посерьезней. :)

PUSH    EAX
MOV     EAX,[EDX]
CALL    DWORD PTR [EAX] + VMTOFFSET Method

Это никак не защитишь.

Вопрос я думаю закрыт. Мое предложение некорректно.

:)


 
umbra ©   (2007-07-30 12:30) [18]

так IInterface, скорее всего и не задумывался как потокобезопасный. Это основа для поддержки СОМ, а там просто так ссылку на интерфейс в другой поток не передашь.


 
oxffff ©   (2007-07-30 12:38) [19]


> PUSH    EDX
> MOV     EAX,[EDX]
> CALL    DWORD PTR [EAX] + VMTOFFSET Method



> umbra ©   (30.07.07 12:30) [18]
> так IInterface, скорее всего и не задумывался как потокобезопасный.
>  Это основа для поддержки СОМ, а там просто так ссылку на
> интерфейс в другой поток не передашь.


Да я тут хотел выдумать threadsafe работу c ref counting типами.
Только работа с ними к сожалению (точнее к счастью) не ограничивается
исключительно операциями присваивания. Об этом я что то не подумал.

:)


 
oxffff ©   (2007-07-30 12:52) [20]

Хотя можно усовершенствовать саму операцию присваивания, таким способом, чтобы она была бы threadSafe.

А вот другие операции требуют guard section.

;)


 
umbra ©   (2007-07-30 12:56) [21]

так может эти методы потокобезопасны :) DВ случае с IInterface, например, в TInterfacedObject._Release для уменьшения счетчика ссылок используется потокобезопасная InterlockedDecrement


 
umbra ©   (2007-07-30 13:07) [22]

а в Вашем примере вы вообще не имеете дела с интерфейсом, Вы работаете с объектом.


 
oxffff ©   (2007-07-30 13:47) [23]


> umbra ©   (30.07.07 12:56) [21]
> так может эти методы потокобезопасны :) DВ случае с IInterface,
>  например, в TInterfacedObject._Release для уменьшения счетчика
> ссылок используется потокобезопасная InterlockedDecrement


Вы меня не поняли.

Я имею ввиду сам interface method invoking и сама работа метода в случае shared variable небезопасен , а именно код вызова

PUSH    EDX
MOV     EAX,[EDX]
CALL    DWORD PTR [EAX] + VMTOFFSET Method

и работа метода _Release. и указанный вами InterlockedDecrement небезопасен. По причине, того что другой поток может "отпустить" интерфейс в shared variable и разрушить объект.
Таким образом другой поток разрушил объект. а Первый еще работает с ним через тот же shared variable. А InterlockedDecrement отработает по убитому объекту. Последствия могут быть разные.


> umbra ©   (30.07.07 13:07) [22]
> а в Вашем примере вы вообще не имеете дела с интерфейсом,
>  Вы работаете с объектом.


Так по порядку. См [0].
Global

b:Iunknown;

Local

a:TinterfacedObject;

a:=TinterfacedObject.Create;
b:=a;

...
       mov eax,b;
       mov b,0;
       push eax;
       mov eax,[eax];
       call [eax+$08];

...

Так что все же это интерфейс.


 
oxffff ©   (2007-07-30 13:53) [24]


> umbra ©   (30.07.07 12:56) [21]
> так может эти методы потокобезопасны :) DВ случае с IInterface,
>  например, в TInterfacedObject._Release для уменьшения счетчика
> ссылок используется потокобезопасная InterlockedDecrement


И еще до самого вызова может дело не дойти.

PUSH    EDX
 вмешивается второй поток и "отпускает" интерфейс shared variable. Далее выделяется   память по тому же месту. И обнуляется.

 переключается обратно  
MOV     EAX,[EDX] //теперь EAX=0
CALL    DWORD PTR [EAX+VMTOFFSET Method]   //Здесь AV



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2007.10.14;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.042 c
4-1176294294
ujin2
2007-04-11 16:24
2007.10.14
Столбец "i/o read bytes" в Task Manager e.


4-1175926105
brother
2007-04-07 10:08
2007.10.14
чужое контекстное меню :)


15-1189279993
vasIZmax
2007-09-08 23:33
2007.10.14
Традиции программирования


2-1189699967
ММК
2007-09-13 20:12
2007.10.14
Файлы


15-1189670205
Галинка
2007-09-13 11:56
2007.10.14
Сортировка односвязных списков





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский