Здравствуйте, eao197, Вы писали:
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
Здравствуйте, eao197, Вы писали:
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
htmlayout static lib имеет Initialize/Finalize методы по двум соображениям:
1) не определен порядок статических конструкторов/деструкторов в lib, а надо.
2) тулзы проверющие расход памяти очень себя неадекватно ведут.
3) очень умный линковщик может выкинуть объекты инициализированные статически но которые нужны.
Например файл behavior_path.cpp
struct path: public behavior
{
// ctor
path(): behavior(HANDLE_DRAW, "path") {}
...
};
// instantiating and attaching it to the global list
path path_instance;
включенный в статическую библиотеку будет викинут линковщиком хотя инстанс:
path path_instance; // instantiating and attaching it to the global list
uzhas пишет:
> Лично я считаю наличие таких функций моветоном. > Инициализация _библиотеки_ — крайне бессмысленное понятие и должно быть > спрятано от пользователя этой библиотеки.
Абсолютно не согласен. В С++ дофига проблем с инициализациий
статических глобальных данных (взять например проблему
потокобезопасной инициализации singleton-ов). А наличие такой функции
инициализации легко и просто разрешает проблему. И является
чуть ли не единственным способом сделать эту инициализацию вообще.
> Идеально было бы получать нотификации о создании/удалении потоков, но не с > помощью отдельной динамической библиотеки. Но как это захачить под Win32, > я не видел... Поищу в форуме Win32 — может кто знает...
IMHO, в случае статической либы это можно захачить только если CRT тоже
прилинкована статически. Подробности можно посмотреть в boost::thread, в
реализации очистки tss. Смысл метода в том, что для очистки связаных с
потоком вещей сама CRT должна откуда-то получать нотификации. Ну она их и
получает, и при желании туда можно вклинится — там насколько помню есть
специальным образом названная PE-секция, и все что в ней лежит CRT считает
коллбэками на функции зачистки. То ли статья, то ли развернутый пост в
форуме про этот способ были на кодепроджекте. Другие способы широкой
общественности вроде как не известны. Я правда года два назад этим вопросом
интересовался, может чего и появилось с тех пор.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
> Да. Действительно. Я ведь там смотрел. Но куда-то не туда посмотрел, и > решил, что они всё ещё требуют чтобы thread был как dll под win32. > Вот статья: > http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c6945__2/ > > Реализация в libs\thread\src\tss_pe.cpp > > То, что работает только со статическим ран-таймом я пока не нашёл.
Это мои личные наблюдения. Авторы библиотеки об этой проблеме видимо не в
курсе. Я не помню, писал я про такую багу в бустогруппы или не писал. Если
не лень — убедитесь, что оно не работает и напишите в comp.lib.boost.devel.
> И это очень многообещающе.
Нифига. Оно реально не работает. CRT ищет эту секцию у себя, а не во всех
принадлежащих процессу модулях. Соответственно, в случае статической
линковки все замечательно, в случае динамической — не работает.
> Там только написано: > #if defined(BOOST_HAS_WINTHREADS) && defined(BOOST_THREAD_BUILD_LIB) && > defined(_MSC_VER) > > И в статье написано только: > Environment: Visual C++ 6 and above (may work on earlier versions too), > for .exe's and DLL's running under Windows 95 and later, including console > apps. > >
Таки смотреть надо в код CRT, мало ли кто чего на кодепроджекте напишет.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, remark, Вы писали:
R>Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА? R>Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду?
В винде в статической библиотеке — повесьте отдельный поток, который следит за завершением тех потоков, о которых знает Ваша библиотека, и подчищает за ними.
В виндовой DLL функции DllMain сообщают о приходе/уходе потоков. Реально эти нотификации можно получить и в статической библиотеке, но я не очень знаю, как.
В юниксе можно создать TLS-объект с деструктором, его позовут при завершении потока.
Здравствуйте, eao197, Вы писали:
E>Столкнулся с библиотекой OTL, для которой в начале программы нужно сделать явную инициализацию посредством вызова otl_initialize, а затем -- otl_terminate: E>
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
Здравствуйте, eao197, Вы писали:
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
По-моему это очень распространённая практика. Вот что дал беглый проход по библиотекам для работы с XML:
Более того, иногда требуется, что бы функция инициализации вызывалась глобально *и* для каждого потока в отдельности. Например: COMInitialize(). Я лично тоже так зачастую делаю.
Здравствуйте, eao197, Вы писали:
E>Доброго дня!
E>Столкнулся с библиотекой OTL, для которой в начале программы нужно сделать явную инициализацию посредством вызова otl_initialize, а затем -- otl_terminate: E>
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
E>Спасибо.
Фактически так и сделано в Standard I/O library, только с подсчётом ссылок
27.4.2.1.6/1 The class Init describes an object whose construction ensures the construction of the eight objects
declared in <iostream> (27.3) that associate file stream buffers with the standard C streams provided for
by the functions declared in <cstdio> (27.8.2).
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Столкнулся с библиотекой OTL, для которой в начале программы нужно сделать явную инициализацию посредством вызова otl_initialize, а затем -- otl_terminate:
int main() {
otl_connect::otl_initialize();
...
otl_connect::otl_terminate();
}
Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
Спасибо.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
E>Спасибо.
Лично я считаю наличие таких функций моветоном.
Инициализация _библиотеки_ — крайне бессмысленное понятие и должно быть спрятано от пользователя этой библиотеки. Это наследие си (где полно глобальных функций).
Здравствуйте, eao197, Вы писали:
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
Да хотя бы WSAInitialize/WSACleanup из Winsock.
А вот <iostream> инициализирует свои объекты правильно.
Здравствуйте, uzhas, Вы писали:
E>>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
E>>Спасибо.
U>Лично я считаю наличие таких функций моветоном. U>Инициализация _библиотеки_ — крайне бессмысленное понятие и должно быть спрятано от пользователя этой библиотеки. Это наследие си (где полно глобальных функций).
Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА?
Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду?
Здравствуйте, eao197, Вы писали:
E>Столкнулся с библиотекой OTL, для которой в начале программы нужно сделать явную инициализацию посредством вызова otl_initialize, а затем -- otl_terminate: E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
здесь упоминали про <iostream> (cout, cin..)
в главе 21(вроде. НО про потоки) у Страутсрупа (3изд) есть пара страниц на метод инициализации глобальных объектов
Спасибо всем, кто привел свои примеры в данной теме. Стало понятно, что подобная инициализация широко используется и, как говорит c-smile, временами только она и работает надежно.
Мне эта информация нужна была для решения одной задачки: у меня есть программы, написанные с использованием плагинов. Т.е. существует некая стандартная реализация main(), в которой запускается плагинная система. Эта система затем грузит DLL-ки с плагинами и стартует сами плагины на основании информации из конфигурационных файлов. Одному из плагинов потребовалось использовать OTL и встал вопрос: как и когда вызывать otl_initialize/otl_terminate? Этот вопрос сложен еще и тем, что плагины загружаются и стартуют не на главной нити приложения, а на каких-то рабочих нитях.
В результате появилась следующая идея. Штатная реализация функции main получает вид:
int main( int argc, char ** argv ) {
try {
custom_app_init( argc, argv );
... остальные действия для системы плагинов ...
}
catch( const std::exception & x ) {
...
}
}
Где, по умолчанию, функция custom_app_init() пустая, не содержит никаких действий. Когда появляется необходимость инициализации 3rd party библиотек, программист пишет свою реализацию custom_app_init() вида:
Получится, что main вызовет custom_app_init, там будет создан экземпляр вспомогательного класса otl_starter_stopper_t, в конструкторе которого будет инициализированна OTL. При завершении приложения будет вызван деструктор этой статической переменной, в котором OTL будет деинициализированна.
Если приложению потребуется инициализация нескольких библиотек, то для каждой библиотеки в custom_app_init() должна быть своя статическая переменная:
С моей точки зрения, преимущества этого подхода в том, что если какая-то библиотека не проинициализировалась корректно, и было выброшено исключение, то деструкторы для всех предшествовавших ей объектов-инициализаторов все равно будут вызваны (т.е. произойдет корректная деинициализация уже инициализированных библиотек).
Смущает то, что не понятно, в какой именно момент будут вызываться деструкторы для подобных инициализаторов. И могут ли с этим быть связаны какие-то проблемы?
Может кто-нибудь подсказать, какие здесь есть подводные камни?
Более подробную информацию о том, зачем мне это понадобилось, можно найти здесь
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, remark, Вы писали:
R>>Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА? R>>Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду?
Pzz>В винде в статической библиотеке — повесьте отдельный поток, который следит за завершением тех потоков, о которых знает Ваша библиотека, и подчищает за ними.
Pzz>В виндовой DLL функции DllMain сообщают о приходе/уходе потоков. Реально эти нотификации можно получить и в статической библиотеке, но я не очень знаю, как.
Pzz>В юниксе можно создать TLS-объект с деструктором, его позовут при завершении потока.
Pzz>Так что непреодолимых проблем нет
Согласен — с помошью отдельного потока можно более-менее решить.
Но останется та же засада, что и в WinSock (где работа с сетью вынесена в отдельный поток) — невозможно отлаживать в отладчике. Плюс — не понятно с какой частотой запускать процесс чистки. Синхронно при удалении потока нотифицировать не получится, а отложенно — непонятно сколько мусора накопится. Я, например, могу сделать ограничение для пользователя — что бы одновременно существовало не более N потоков работающих с библиотекой. С асинхронной сборкой это условие придётся переписать как — что бы одновременно существовало не более N потоков работающих с библиотекой + не создавайте потоки *слишком часто*, что такое *слишком часто* определяйте на практике методом тыка
Ну в общем не очень красиво получается.
С динамической библиотекой — с одной стороны лучше, но с другой свои проблемы. Заставять пользователя таскать за собой отдельную библиотеку...
Идеально было бы получать нотификации о создании/удалении потоков, но не с помощью отдельной динамической библиотеки. Но как это захачить под Win32, я не видел... Поищу в форуме Win32 — может кто знает...
Здравствуйте, remark, Вы писали:
R>Согласен — с помошью отдельного потока можно более-менее решить. R>Но останется та же засада, что и в WinSock (где работа с сетью вынесена в отдельный поток) — невозможно отлаживать в отладчике.
Что именно отлаживать? Поток, подчищающий за другими потоками?
R> Плюс — не понятно с какой частотой запускать процесс чистки. Синхронно при удалении потока нотифицировать не получится, а отложенно — непонятно сколько мусора накопится.
Завершение потока можно ждать с помощью WaitFor...(). Совершенно не обязательно устраивать поллинг.
R>С динамической библиотекой — с одной стороны лучше, но с другой свои проблемы. Заставять пользователя таскать за собой отдельную библиотеку...
R>Идеально было бы получать нотификации о создании/удалении потоков, но не с помощью отдельной динамической библиотеки. Но как это захачить под Win32, я не видел... Поищу в форуме Win32 — может кто знает...
Нотификации есть, но хорошо запрятаны. За 10 минут поиска по MSDN'у я не смог их найти
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, remark, Вы писали:
R>>Согласен — с помошью отдельного потока можно более-менее решить. R>>Но останется та же засада, что и в WinSock (где работа с сетью вынесена в отдельный поток) — невозможно отлаживать в отладчике.
Pzz>Что именно отлаживать? Поток, подчищающий за другими потоками?
Да вообще всё.
Вот проходишь отладчиком по функции send(), а ничего на самом деле не отослалось. И когда отошлётся — не понятно.
R>> Плюс — не понятно с какой частотой запускать процесс чистки. Синхронно при удалении потока нотифицировать не получится, а отложенно — непонятно сколько мусора накопится.
Pzz>Завершение потока можно ждать с помощью WaitFor...(). Совершенно не обязательно устраивать поллинг.
Да, сам уже подумал, что если мы инициализировали поток, то получили его хендл. Теперь вспомогательный поток может ждать на этом хендле.
R>>С динамической библиотекой — с одной стороны лучше, но с другой свои проблемы. Заставять пользователя таскать за собой отдельную библиотеку...
R>>Идеально было бы получать нотификации о создании/удалении потоков, но не с помощью отдельной динамической библиотеки. Но как это захачить под Win32, я не видел... Поищу в форуме Win32 — может кто знает...
Pzz>Нотификации есть, но хорошо запрятаны. За 10 минут поиска по MSDN'у я не смог их найти
Да. Я тоже ничего не нахожу. Вообще глупо — понятно, что эта функциональность есть в Win, но почему она работает только для dll?! Почему нельзя сделать что-то типа RegisterThreadMonitor()? Или на худой конец — at_thread_exit(), что б можно было эмулировать объекты с деструкторами в TLS...
>> Идеально было бы получать нотификации о создании/удалении потоков, но не с >> помощью отдельной динамической библиотеки. Но как это захачить под Win32, >> я не видел... Поищу в форуме Win32 — может кто знает...
S>IMHO, в случае статической либы это можно захачить только если CRT тоже S>прилинкована статически. Подробности можно посмотреть в boost::thread, в S>реализации очистки tss. Смысл метода в том, что для очистки связаных с S>потоком вещей сама CRT должна откуда-то получать нотификации. Ну она их и S>получает, и при желании туда можно вклинится — там насколько помню есть S>специальным образом названная PE-секция, и все что в ней лежит CRT считает S>коллбэками на функции зачистки. То ли статья, то ли развернутый пост в S>форуме про этот способ были на кодепроджекте. Другие способы широкой S>общественности вроде как не известны. Я правда года два назад этим вопросом S>интересовался, может чего и появилось с тех пор.
То, что работает только со статическим ран-таймом я пока не нашёл. И это очень многообещающе.
Там только написано:
#if defined(BOOST_HAS_WINTHREADS) && defined(BOOST_THREAD_BUILD_LIB) && defined(_MSC_VER)
И в статье написано только:
Environment: Visual C++ 6 and above (may work on earlier versions too), for .exe's and DLL's running under Windows 95 and later, including console apps.
Здравствуйте, remark, Вы писали:
Pzz>>Что именно отлаживать? Поток, подчищающий за другими потоками?
R>Да вообще всё. R>Вот проходишь отладчиком по функции send(), а ничего на самом деле не отослалось. И когда отошлётся — не понятно.
Ну это относится к многопоточности вообще, а не только к рассматриваемому нами случаю.
Pzz>>Завершение потока можно ждать с помощью WaitFor...(). Совершенно не обязательно устраивать поллинг.
R>Да, сам уже подумал, что если мы инициализировали поток, то получили его хендл. Теперь вспомогательный поток может ждать на этом хендле.
Если мы говорим о библиотеке, то собственно создание пользовательских потоков мы не контроллируем. Но хендл текущего потока всегда можно получить, вот таким примерно образом:
Pzz>>Нотификации есть, но хорошо запрятаны. За 10 минут поиска по MSDN'у я не смог их найти
R>Да. Я тоже ничего не нахожу. Вообще глупо — понятно, что эта функциональность есть в Win, но почему она работает только для dll?! Почему нельзя сделать что-то типа RegisterThreadMonitor()? Или на худой конец — at_thread_exit(), что б можно было эмулировать объекты с деструкторами в TLS...
Чтобы жизнь мёдом не казалась
В Висте наконец добавили FlsAlloc(), который аллоцирует fiber local storage с деструктором. Вроде обещают звать эту функцию при завершении как фибера, так и потока.
Здравствуйте, eao197, Вы писали:
E>Мне интересно, насколько это распространенная практика для C/C++ библиотек. Буду признателен за перечисление известных вам библиотек, которым требуется явный init/deinit.
У libxml, насколько я помню, тоже такие функции есть. Но она сишная.
Вообще, такие функции в кроссплатформенных библиотеках вещь неплохая. Чтобы была возможность все под контролем держать. Инициализация/деинициализация в конструкторах/деструкторах глобальных переменных — вещь опасная иногда.
It's kind of fun to do the impossible (Walt Disney)
>> И это очень многообещающе.
S>Нифига. Оно реально не работает. CRT ищет эту секцию у себя, а не во всех S>принадлежащих процессу модулях. Соответственно, в случае статической S>линковки все замечательно, в случае динамической — не работает.
Непонятно. Что значит, "CRT ищет эту секцию у себя"? Ведь тот же механизм используется для вызова конструкторов/деструкторов глобальных объектов? Как же там все это находится?
Кроме того, для того, чтобы нас уведомлять о создании/смерти потока, CRT сама должна получить эти нотификации откуда-то? Т.е. у нее должен быть свой DllMain? Или уведомления придут только в случае другие потоки создаются через _beginthread/_beginthreadex?
It's kind of fun to do the impossible (Walt Disney)
По-моему, это не то. Вообще, использование atexit для вызовов дестракторов вроде бы и в MSDN где-то документировано было, и другие примеры встречаются. Но вызывается это при завершении первичного треда, да и то, если оно не аварийное.
Нотификаторы на создание\уничтожение треда (согдасно MSDN) есть только в ядре. Однако, есть и "полудокументированный" механизм — см. DLL_THREAD_DETACH в DllMain. Загрузчик создаёт список загруженных модулей, где хранятся и их точки входа. Здесь можно подумать о разных вариантах: от ручного добавления ldr_data_table_entry, до вполне легального — создания и загрузки прокси-dll.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Sergey, Вы писали:
>> Да. Действительно. Я ведь там смотрел. Но куда-то не туда посмотрел, и >> решил, что они всё ещё требуют чтобы thread был как dll под win32. >> Вот статья: >> http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c6945__2/ >> >> Реализация в libs\thread\src\tss_pe.cpp >> >> То, что работает только со статическим ран-таймом я пока не нашёл.
S>Это мои личные наблюдения. Авторы библиотеки об этой проблеме видимо не в S>курсе. Я не помню, писал я про такую багу в бустогруппы или не писал. Если S>не лень — убедитесь, что оно не работает и напишите в comp.lib.boost.devel.
>> И это очень многообещающе.
S>Нифига. Оно реально не работает. CRT ищет эту секцию у себя, а не во всех S>принадлежащих процессу модулях. Соответственно, в случае статической S>линковки все замечательно, в случае динамической — не работает.
Проверил. Их версия у меня не работает только под release, т.к. линкер выкидывает некоторые переменные. А статический или динамический ран-тайм не влияет.
Здравствуйте, Alex Alexandrov, Вы писали:
AA>Здравствуйте, Sergey, Вы писали:
>>> И это очень многообещающе.
S>>Нифига. Оно реально не работает. CRT ищет эту секцию у себя, а не во всех S>>принадлежащих процессу модулях. Соответственно, в случае статической S>>линковки все замечательно, в случае динамической — не работает.
AA>Непонятно. Что значит, "CRT ищет эту секцию у себя"? Ведь тот же механизм используется для вызова конструкторов/деструкторов глобальных объектов? Как же там все это находится?
AA>Кроме того, для того, чтобы нас уведомлять о создании/смерти потока, CRT сама должна получить эти нотификации откуда-то? Т.е. у нее должен быть свой DllMain? Или уведомления придут только в случае другие потоки создаются через _beginthread/_beginthreadex?
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, remark, Вы писали:
R>>http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c6945__2/
GN>По-моему, это не то. Вообще, использование atexit для вызовов дестракторов вроде бы и в MSDN где-то документировано было, и другие примеры встречаются. Но вызывается это при завершении первичного треда, да и то, если оно не аварийное.
GN>Нотификаторы на создание\уничтожение треда (согдасно MSDN) есть только в ядре. Однако, есть и "полудокументированный" механизм — см. DLL_THREAD_DETACH в DllMain. Загрузчик создаёт список загруженных модулей, где хранятся и их точки входа. Здесь можно подумать о разных вариантах: от ручного добавления ldr_data_table_entry, до вполне легального — создания и загрузки прокси-dll.
Здравствуйте, remark, Вы писали:
R>Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА?
Деинициализировать при выходе, почему бы нет?;)) Сделать булевский флаг...
Ну или разрешить многократную инициализацию и деинициализацию.
R>Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду?
А это вообще диверсия (если речь не идёт про явно специализированные случаи типа эрланга). Используйте пулы тредов.;)
Ага, не дочитал Хорошее решение, особенно в плане того, что способ как бы для этого и присутствует в ОС. Только вот лучше ли оно документировано... Традиционно TLS используют в протекторах, что бы проверить, не прицеплен ли отладчик...
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, remark, Вы писали:
R>>Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА?
N>Деинициализировать при выходе, почему бы нет?) Сделать булевский флаг...
N>Ну или разрешить многократную инициализацию и деинициализацию.
Если я правильно понял, то речь не об этом. Речь о том, как инициализировать/деинициализировать ресурсы, выделенные для конкретного потока, а не глобальные.
R>>Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду?
N>А это вообще диверсия (если речь не идёт про явно специализированные случаи типа эрланга). Используйте пулы тредов.
Т.е. предлагаешь не инженерное решение, а организационное — автор библиотеки должен постоянно обходить всех клиентов и говорить "Используйте пулы тредов. Пожалуйста."
Здравствуйте, remark, Вы писали:
R>>>Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА? N>>Деинициализировать при выходе, почему бы нет?;)) Сделать булевский флаг... R>Как отловить момент завершения потока?
R>Ну хотя сейчас я уже знаю: R>http://www.rsdn.ru/forum/message/2895408.1.aspx
Как завершается тред? Если форсированным убийством без права пискнуть, то всё плохо по-любому. Если обычным путём, управляемым средой языка (return или исключение), то достаточно объекта со счётчиком и логикой "init/deinit только на переходах счётчика между 0 и 1", устанавливаемого в тред в стиле RAII.
N>>Ну или разрешить многократную инициализацию и деинициализацию.
R>Если я правильно понял, то речь не об этом. Речь о том, как инициализировать/деинициализировать ресурсы, выделенные для конкретного потока, а не глобальные.
О выполнении init/deinit в контексте именно данного треда, насколько я понимаю, заботится библиотека.
R>>>Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду? N>>А это вообще диверсия (если речь не идёт про явно специализированные случаи типа эрланга). Используйте пулы тредов.;) R>Т.е. предлагаешь не инженерное решение, а организационное — автор библиотеки должен постоянно обходить всех клиентов и говорить "Используйте пулы тредов. Пожалуйста." :)))
Ему как раз пофиг. Не пофиг — автору конечной программы.
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, remark, Вы писали:
R>>>>Ну хорошо, допустим функцию инициализации для процесса можно заменить ленивой инициализацией, заплатив дополнительной проверкой на основном пути. Допустим, функцию деинициализации для процесса можно вообще опустить, или зарегистрировать atexit(). Допустим инициализацию для потока можно тоже сделать ленивой. НО КАК БЫТЬ С ДЕИНИЦИАЛИЗАЦИЕЙ ДЛЯ ПОТОКА? N>>>Деинициализировать при выходе, почему бы нет?) Сделать булевский флаг... R>>Как отловить момент завершения потока?
R>>Ну хотя сейчас я уже знаю: R>>http://www.rsdn.ru/forum/message/2895408.1.aspx
N>Как завершается тред? Если форсированным убийством без права пискнуть, то всё плохо по-любому. Если обычным путём, управляемым средой языка (return или исключение), то достаточно объекта со счётчиком и логикой "init/deinit только на переходах счётчика между 0 и 1", устанавливаемого в тред в стиле RAII.
Тогда я не понял твоего тезиса... Ты с чем-то несогласен? Или что-то предлагаешь?
Можешь пояснить?
N>>>Ну или разрешить многократную инициализацию и деинициализацию.
R>>Если я правильно понял, то речь не об этом. Речь о том, как инициализировать/деинициализировать ресурсы, выделенные для конкретного потока, а не глобальные.
N>О выполнении init/deinit в контексте именно данного треда, насколько я понимаю, заботится библиотека.
О библиотеке как раз и идёт речь. Т.е. ответы типа "а библиотека как-то там это сама сделает" не катят
R>>>>Как ты предлагаешь делать? Не освобождать ресурсы, связанные с потоком? А если приложение создаёт и рушит по 100 потоков в секунду? N>>>А это вообще диверсия (если речь не идёт про явно специализированные случаи типа эрланга). Используйте пулы тредов. R>>Т.е. предлагаешь не инженерное решение, а организационное — автор библиотеки должен постоянно обходить всех клиентов и говорить "Используйте пулы тредов. Пожалуйста."
N>Ему как раз пофиг. Не пофиг — автору конечной программы.
Т.е. ты считаешь нормальным, если документация по Windows или Linux содержала бы фразы типа "Вы не должны создавать более 100 потоков в секунду", "Вы не должны делать более 1000 аллокаций памяти в секунду", "Вы не должны выделать блоки памяти более 10 Мб" ("... потому что мы там кое-чо не доделали, поэтому у Вас может кое-где сбоить") ?!
N>>Как завершается тред? Если форсированным убийством без права пискнуть, то всё плохо по-любому. Если обычным путём, управляемым средой языка (return или исключение), то достаточно объекта со счётчиком и логикой "init/deinit только на переходах счётчика между 0 и 1", устанавливаемого в тред в стиле RAII. R>Тогда я не понял твоего тезиса... Ты с чем-то несогласен? Или что-то предлагаешь? :xz: R>Можешь пояснить?
Я говорю, что мне кажется, что проблемы тут по сути нет и не было.
N>>>>Ну или разрешить многократную инициализацию и деинициализацию. R>>>Если я правильно понял, то речь не об этом. Речь о том, как инициализировать/деинициализировать ресурсы, выделенные для конкретного потока, а не глобальные. N>>О выполнении init/deinit в контексте именно данного треда, насколько я понимаю, заботится библиотека. R>О библиотеке как раз и идёт речь. Т.е. ответы типа "а библиотека как-то там это сама сделает" не катят :)))
Ну?
N>>Ему как раз пофиг. Не пофиг — автору конечной программы. R>Т.е. ты считаешь нормальным, если документация по Windows или Linux содержала бы фразы типа "Вы не должны создавать более 100 потоков в секунду", "Вы не должны делать более 1000 аллокаций памяти в секунду", "Вы не должны выделать блоки памяти более 10 Мб" ("... потому что мы там кое-чо не доделали, поэтому у Вас может кое-где сбоить") ?! :xz:
Мне не хочется отвечать на непонятно откуда почему возникшие додумки типа "мы там кое-чо не доделали", извини уж. Хочешь фантазировать в пространство — пожалуйста, но я в этом не участник.
N>>>Как завершается тред? Если форсированным убийством без права пискнуть, то всё плохо по-любому. Если обычным путём, управляемым средой языка (return или исключение), то достаточно объекта со счётчиком и логикой "init/deinit только на переходах счётчика между 0 и 1", устанавливаемого в тред в стиле RAII. R>>Тогда я не понял твоего тезиса... Ты с чем-то несогласен? Или что-то предлагаешь? R>>Можешь пояснить?
N>Я говорю, что мне кажется, что проблемы тут по сути нет и не было.
Если обязать пользователя вызывать global_init()/global_deinit()/thread_init()/thread_deinit(), то, да, всё замечательно. И мне лично такое решение очень нравится.
Но дискуссия пошла в русло, что типа ручная инициализация/деинициализация — сакс, и библиотека должна эти вещи сама у себя внутри как-то разруливать.
Если ты за ручную инициализацию/деинициализацию, то тебе наверное лучше ответить не мне, а сюда: http://www.rsdn.ru/forum/message/2888124.1.aspx
> S>Нифига. Оно реально не работает. CRT ищет эту секцию у себя, а не во > всех > S>принадлежащих процессу модулях. Соответственно, в случае статической > S>линковки все замечательно, в случае динамической — не работает. > > Проверил. Их версия у меня не работает только под release, т.к. линкер > выкидывает некоторые переменные. А статический или динамический ран-тайм > не влияет.
Очень странно, что работает, потому что я этим вопросом заинересовался
именно в контексте "а почему не чистятся boost::thread_specific_ptr, если
boost::thread прилинковать статически; вроде ж обещали что теперь работает".
Конфигурация у меня была чуть сложнее, чем простой exe — наличествовали
несколько dll, поторые статически линковались с boost::thread.
> Пофиксил это. Выложил рабочую версию: > http://www.rsdn.ru/forum/message/2895408.1.aspx
Действительно работает. Разница с бустом, насколько я понимаю — наличие
static __declspec(thread) int volatile use_tls;
В бусте (1.34.1) "__declspec(thread)" мне удалось обнаружить единственный
раз, в комментах. Так же не используется #pragma comment(linker,
"/INCLUDE:__tls_used")
Видимо, желающим линковать статически boost::thread и не иметь при этом
проблем с разрушением tss можно посоветовать вставлять в код #pragma
comment(linker, "/INCLUDE:__tls_used")
Самое смешное, что без __declspec(thread) или __tls_used не заработал даже
вариант со статически прилинкованным рантаймом (компилятор правда под рукой
оказался только от VC2008).
Что интересно — нотификация DLL_THREAD_ATTACH похоже присылается без участия
CRT:
A.exe!on_tls_callback(HINSTANCE__ * __formal=0x002d0000, unsigned long
dwReason=0x00000002, HINSTANCE__ * __formal=0x002d0000) Line 41 C++
ntdll.dll!_LdrpCallInitRoutine@16() + 0x14 bytes
ntdll.dll!_LdrpCallTlsInitializers@8() + 0x5d1ee bytes
ntdll.dll!_LdrpInitializeThread@4() — 0x304b5 bytes
ntdll.dll!__LdrpInitialize@8() + 0x7c bytes
ntdll.dll!_LdrInitializeThunk@8() + 0x10 bytes
Стек при завершении нитки тоже забавный: > A.exe!on_tls_callback(HINSTANCE__ * __formal=0x002d0000, unsigned long > dwReason=0x00000003, HINSTANCE__ * __formal=0x002d0000) Line 41 C++
ntdll.dll!_LdrpCallInitRoutine@16() + 0x14 bytes
ntdll.dll!_LdrpCallTlsInitializers@8() + 0x5d1ee bytes
ntdll.dll!_LdrShutdownThread@0() — 0x602da bytes
ntdll.dll!_RtlExitUserThread@4() + 0x2a bytes
A.exe!_endthreadex(unsigned int retcode=0x00000000) Line 414 C
A.exe!_callthreadstartex() Line 348 + 0x15 bytes C
A.exe!_threadstartex(void * ptd=0x00164848) Line 331 C
kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes
ntdll.dll!__RtlUserThreadStart@8() + 0x27 bytes
Замена _beginthreadex на CreateThread показала, что on_tls_callback все
равно вызывается, а значит CRT в вызове колбэков участвует только косвенно —
помогая форировать необходимые структуры. О чем, собственно и написано в
комментах в tlssup.c:
/* Start section for TLS callback array examined by the OS loader code.
* If dynamic TLS initialization is used, then a pointer to __dyn_tls_init
* will be placed in .CRT$XLC by inclusion of tlsdyn.obj. This will cause
* the .CRT$XD? array of individual TLS variable initialization callbacks
* to be walked.
*/
_CRTALLOC(".rdata$T")
const IMAGE_TLS_DIRECTORY _tls_used =
{
(ULONG)(ULONG_PTR) &_tls_start, // start of tls data
(ULONG)(ULONG_PTR) &_tls_end, // end of tls data
(ULONG)(ULONG_PTR) &_tls_index, // address of tls_index
(ULONG)(ULONG_PTR) (&__xl_a+1), // pointer to call back array
(ULONG) 0, // size of tls zero fill
(ULONG) 0 // characteristics
};
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.