Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Использование CRT в CallBack. Чужой поток. 
V
    Опции темы
Alexeis
Дата 15.11.2010, 14:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Насколько я знаю, для правильного функционирования CRT в потоках требуется инициализация потока при помощи _beginthread(ex) .
Т.е. дополнительно создается вот такая структура, которая хранит все глобальные переменные, которые использует CRT. 
Код

typedef struct _thread_data
{
    struct _thread_data *thread_link; /* next struct on free list */
    void    *thread_arglist;        /* argument list to pass to thread_func */
    HANDLE  thread_handle;          /* handle of thread object */
    int     thread_errno;           /* errno */
    int     thread_doserrno;        /* _doserrno */
    void    (_USERENTRY *thread_func)(void *); /* thread starting address */
    char    *thread_token;          /* pointer to next strtok() token */
    char    *thread_template;       /* pointer to temp filename template */
    int     thread_mbshift;         /* shift state for mbtowc() */
    int     thread_wcshift;         /* shift state for wctomb() */
    void    (_USERENTRY **thread_sig)();/* pointer to signal table */
    void    *thread_excep;          /* exception registration pointer */
    void    *thread_time;           /* data used by time functions */
    void    *thread_cvt;            /* array used by fcvt() and ecvt() */
    void    *thread_strbuf;         /* array used by strerror() */
    void    *thread_wstrbuf;        /* array used by wcserror() */
    void    *thread_passbuf;        /* array used by getpass() */
    void    *thread_pathbuf;        /* array used by searchpath() */
    __seed_t   thread_seed;         /* random number seed */
    void    *thread_exceptvars;
#if defined(_MBCS)
    int     thread_lead_byte;       /* pending leadbyte data used by putch() */
#endif
    int     thread_ex_mode;         /* 1 means _beginthreadex was called */
    int     thread_xtra;            /* extra place holder. unused for now */
} THREAD_DATA;


Теперь ситуация. Функция вызывается калбеком из чужого модуля (из дополнительного потока), который, не вызывал нужной _beginthread (каждый CRT, как я понимаю, требует свой _beginthread). К чему приведет в этом случае использование функций CRT? 



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Sartorius
Дата 15.11.2010, 14:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: 1
Всего: 37



ИМХО если функции не используют общих объектов, то все Ок будет. Если используют (по комментам видно, что это за функции) то могут сработать некорректно конкурируя за данные или пытаясь посмотреть CRT контекст потока. 

Это сообщение отредактировал(а) Sartorius - 15.11.2010, 14:33
PM MAIL ICQ   Вверх
Alexeis
Дата 15.11.2010, 14:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Цитата(Sartorius @  15.11.2010,  15:32 Найти цитируемый пост)
Если используют (по комментам видно, что это за функции) то могут сработать некорректно конкурируя за данные или пытаясь посмотреть CRT контекст потока. 

  Допустим что можно отказаться от явного использования указанных функций (хотя это плохое решение), но ведь их вполне может использовать стандартная библиотека STL .


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
ama_kid
Дата 15.11.2010, 15:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

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



Цитата(Alexeis @  15.11.2010,  15:22 Найти цитируемый пост)
К чему приведет в этом случае использование функций CRT?

Ну, видимо, к тому, от чего призвана защитить функция _beginthreadex: всякие внутренние СRT-шные переменные типа errno,_doserrno, strtok, _wcstok, strerror и легион им подобных могут начать показывать неактуальные значения, что может привести к неуловимым глюкам, зависящим от расположения звёзд в созвездии Плеяды.


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Sartorius
Дата 15.11.2010, 15:28 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: 1
Всего: 37



Alexeis, а откуда ты beginthread взял этот? Используй CreateThread
Посмотри исходники CRT. Там всякие strtok-и вызывают первым делом _getpdt. ptd создает контекст потока, если его еще нет. Все будет Ок, CRT не дураки писали.

ЗЫ
 В общем никогда не встречал проблем с вызовом CRT функций из любых потоков.


Это сообщение отредактировал(а) Sartorius - 15.11.2010, 15:32
PM MAIL ICQ   Вверх
Alexeis
Дата 15.11.2010, 16:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Цитата(Sartorius @  15.11.2010,  16:28 Найти цитируемый пост)
Alexeis, а откуда ты beginthread взял этот? Используй CreateThread

  MSDN рекомендует обратное.
  
Цитата(Sartorius @  15.11.2010,  16:28 Найти цитируемый пост)
Посмотри исходники CRT. Там всякие strtok-и вызывают первым делом _getpdt. ptd создает контекст потока, если его еще нет.

  Проверил. Действительно, и билдер и студия создают блок переменных потока по первой необходимости. Единственная проблема, это утечка памяти.

  Вероятно следует вызвать _freeptd() по окончанию пользования этим потоком, иначе ведь возникнет утечка памяти. 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
xvr
Дата 15.11.2010, 16:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Вот что написано в MSDN по поводу CreateProcess:
Цитата

A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multi-threaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.
Раньше еще было написано, что использование CreateProcess вместо _beginthread* может привести к утечкам памяти

PM MAIL   Вверх
Alexeis
Дата 15.11.2010, 16:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Цитата(xvr @  15.11.2010,  17:11 Найти цитируемый пост)
If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

  Насколько я понял, проблема еще в том, что любое аппаратное исключение типа AV или деление на 0 приведет к краху, поскольку SEH фрейм не будет создан.
  Кроме того, оказывается следует вызывать не _freeptd() , а специально созданную _endthreadex() .

Добавлено через 4 минуты и 11 секунд
P.S. в моей ситуации SEH фрейм создаст билдер (точнее библиотеки delphi), так что этой проблемы не будет. С другой стороны будут созданы 2е копии переменных. Одна для CRT билдера, другая для CRT студии. Билдер свою почистит, а ту что создаст студия придется очищать руками.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Sartorius
Дата 15.11.2010, 16:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: 1
Всего: 37



Цитата(Alexeis @  15.11.2010,  17:23 Найти цитируемый пост)
Насколько я понял, проблема еще в том, что любое аппаратное исключение типа AV или деление на 0 приведет к краху, поскольку SEH фрейм не будет создан

Не совсем понял. Это почему? Любой поток содержит SEH list. Вроде ничего не мешает использвать SEH исключения
PM MAIL ICQ   Вверх
Alexeis
Дата 15.11.2010, 16:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Sartorius, цитирую рихтера
Цитата

Как это ни фантастично, но Ваш поток будет работать почти без глюков. Хотя
некоторые проблемы все же появятся. Во-первых, если этот поток воспользуется биб-
лиотечной функцией signal, весь процесс завершится, так как SEH-фрейм не подго-
товлен. Во-вторых, если поток завершится, не вызвав _endthreadex, его блок данных
не высвободится и произойдет утечка памяти.

  Рихтер обманывать не будет. 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
GremlinProg
Дата 16.11.2010, 07:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



в принципе, нам же ни кто не мешает правильно инициализировать поток CRT:
Код

static void _callthreadstartex(void)
{
    _ptiddata ptd;           /* pointer to thread's _tiddata struct */

    /* must always exist at this point */
    ptd = _getptd();

    /*
        * Guard call to user code with a _try - _except statement to
        * implement runtime errors and signal support
        */
    __try {
            _endthreadex (
                ( (unsigned (__CLR_OR_STD_CALL *)(void *))(((_ptiddata)ptd)->_initaddr) )
                ( ((_ptiddata)ptd)->_initarg ) ) ;
    }
    __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
    {
            /*
                * Should never reach here
                */
            _exit( GetExceptionCode() );

    } /* end of _try - _except */

}

это код из threadex.c,
_initaddr - это и есть сама процедура потока

но смотреть надо конечно не _callthreadstartex, а _threadstartex, в которой и происходит инициализация переменных

а приоткрыть внутренние функции CRT не так и сложно, достаточно их просто объявить


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Alexeis
Дата 16.11.2010, 11:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



GremlinProg, такой код неприменим касательно в контексте CallBack вызовов. Как уже сказано выше, _getptd() и так вызывается по первому запросу. А блок __try{}__except(){} придется вставлять в каждую из функций обратного вызова, которые будут вызваны из дополнительного потока. Один такой блок можно организовать только внутри функции потока, а она физически расположена с другом модуле. Но это работать все равно не будет, и вот почему
функция _beginthread производит инициализацию SEH для каждого потока. Без этого __try{}__except(){} будет работать неправильно. 
Смотрим фрагмент
Код

    /* Store the thread's stack base in the thread local storage.
     */
    VirtualQuery((void *)&info, &info, sizeof(info));
    _stkindex = ((DWORD)info.AllocationBase + extra);

    _ExceptInit(t->thread_exceptvars);

    /* Set up the RTL exception handler.  Save a pointer to
     * its registration record in the thread data, so that
     * _endthread() can remove it.
     */
    _setexc(&hand);
    t->thread_excep = &hand;

    /* Reset the fpu control word since the OS has probably changed it on us */
    _fpreset();



Выводы следующие.
1) Вызовы CRT безопасны в функциях обратного вызова вызванных из дополнительного потока, даже если поток не создавался с _beginthread(ex).
2) Вызовы CRT в случае №1 могут приводить к утечке ресурсов, что впрочем решается дополнительным вызовом _endthreadex(), который явно освобождает ресурсы .
3) Вызовы CRT в случае №1, также не могут использовать функцию signal / __try{}__except(){}, по крайней мере до тех пор пока SEH не будет инициализирован явно.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
GremlinProg
Дата 16.11.2010, 12:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(Alexeis @  16.11.2010,  13:13 Найти цитируемый пост)
А блок __try{}__except(){} придется вставлять в каждую из функций обратного вызова, которые будут вызваны из дополнительного потока

ну конечно, в этом и смысл: завернуть работу CRT в твоем плагине в SEH со стандартным фильтром CRT: _XcptFilter,

ничего криминального тут нет: хочешь CRT - оберни исключения в CRT-фильтр
хочешь еще и переменные CRT - инициируй поток, как в _threadstartex, это и есть отправная точка любого CRT-потока,
и не важно, что ты это сделаешь только в функции своей библиотеки
Цитата(Alexeis @  16.11.2010,  13:13 Найти цитируемый пост)

Но это работать все равно не будет, и вот почему
функция _beginthread производит инициализацию SEH для каждого потока. Без этого __try{}__except(){} будет работать неправильно.

SEH организует _callthreadstartex в самом потоке, я же его показал,
а _beginthread производит инициализацию _ptiddata, которую подчищает _endthreadex
Цитата(Alexeis @  16.11.2010,  13:13 Найти цитируемый пост)
Смотрим фрагмент

угу, смотрим, к чему он?


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Alexeis
Дата 16.11.2010, 12:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Цитата(GremlinProg @  16.11.2010,  13:05 Найти цитируемый пост)
угу, смотрим, к чему он? 

  Это код аналогичный _threadstartex, но из билдера.
Смутила фраза
Цитата(GremlinProg @  16.11.2010,  08:47 Найти цитируемый пост)
в принципе, нам же ни кто не мешает правильно инициализировать поток CRT:

  И дальше идет код претендующий на полноту инициализации потока, хотя таковым не является. Полная инициализация потребует вызовов из _threadstartex и из _callthreadstartex. Причем, вероятно этот код будет непереносим на последующие версии студии. А это нехорошо. Лучше обойтись документированными средствами.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
GremlinProg
Дата 16.11.2010, 13:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(Alexeis @  16.11.2010,  14:28 Найти цитируемый пост)
И дальше идет код претендующий на полноту инициализации потока, хотя таковым не является.

это был ответ на твою последнюю цитату,
ты просто сам зачем-то разделил понятия SEH с __try-__except,
хотя последнее как раз этот SEH и ставит, вот я и сделал на нем акцент, раз уж тебе важно было использовать signal
Цитата(Alexeis @  16.11.2010,  14:28 Найти цитируемый пост)
Полная инициализация потребует вызовов из _threadstartex и из _callthreadstartex

именно это я и отметил
Цитата(GremlinProg @  16.11.2010,  09:47 Найти цитируемый пост)
но смотреть надо конечно не _callthreadstartex, а _threadstartex, в которой и происходит инициализация переменных

был смысл придираться?
Цитата(Alexeis @  16.11.2010,  14:28 Найти цитируемый пост)
Это код аналогичный _threadstartex, но из билдера.

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

RTL exception handler - это видимо подразумевается все таки простая функция, но не SEH (билдер не использую, точнее не скажу)


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




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


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

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