Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > удаление данных из TLS


Автор: Lazin 15.4.2009, 08:47
Добрый день.
У меня есть такой код, и я недавно заметил, что здесь есть утечка памяти, а именно, не вызывается деструктор объекта после завершения не основного потока.
Код

            template <typename T>
            class thread_local
            {
            private:
                DWORD threadLocalIndex;

                thread_local(thread_local const&);

                T *get_pointer(void)
                {
                    return static_cast<T*>(::TlsGetValue(threadLocalIndex));
                }

                void set_pointer(T *value)
                {
                    ::TlsSetValue(threadLocalIndex, static_cast<void*>(value));
                }
            public:
                void set_value(const T &value)
                {
                    T* currentPointer = get_pointer();
                    if (currentPointer == NULL)
                    {
                        set_pointer(new T(value));
                    }
                    else
                    {
                        *currentPointer = value;
                    }
                }

                T &get_value()
                {
                    T* currentPointer = get_pointer();
                    if (currentPointer == NULL)
                    {
                        set_pointer(new T());
                    }
                    return *get_pointer();
                }

                operator T() 
                {
                    return get_value();
                }

                thread_local<T>& operator = (const T& value)
                {
                    set_value(value);
                    return *this;
                }

                void delete_value()
                {
                    T* currentPointer = get_pointer();
                    if (currentPointer != NULL)
                    {
                        delete currentPointer;
                        set_pointer(NULL);
                    }
                }

                thread_local(const T& value)
                {
                    threadLocalIndex = ::TlsAlloc();
                    set_value(value);
                }

                thread_local()
                {
                    threadLocalIndex = ::TlsAlloc();
                }

                ~thread_local()
                {
                    delete_value();
                    ::TlsFree(threadLocalIndex);
                }
            };

Здесь суть в следующем. Создается глобальный объект класса thread_local, который при создании вызывает TlsAlloc, и получает индекс. При первом обращении из потока, он создает новый объект и помещает указатель на него в TLS, потом возвращает все время его. При удалении объекта thread_local, удаляется объект размещенный в том потоке, в котором происходит удаление, но объекты созданные в других потоках остаются, потом вызывается ф-я tlsFree. Как можно удалить объект, указатель на который хранится в tls перед во время завершения потока?

Автор: Lazin 15.4.2009, 09:11
в принципе, можно вызвать delete_value перед выходом из потока, можно использовать для этого RAII, но не хотелось-бы...

Автор: GremlinProg 15.4.2009, 11:46
можно в отдельном watch-потоке (или в пуле потоков) вызвать функцию ожидания, передав ей дескриптор потока, данные которого нужно удалить по завершению, ну и при ее срабатывании, удалить глобальные указатели потока, который завершился

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

можно конечно использовать DLL и отслеживать моменты создания и удаления потоков в DLLMain'е, тогда и watch-потоки не нужны, но по-моему, этот вариант слишком накладный: таскать за собой DLL ради автоматического удаления потоковых объектов

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

Автор: Lazin 15.4.2009, 12:57
нашел в бусте класс thread_specific_ptr, обещают что объект будет удаляться с помощью delete

Автор: Lazin 15.4.2009, 13:12
boost::thread_specific_storage работает так как мне нужно, правда я не понимаю как оно это делает, нужно будет это выяснить smile 

Автор: GremlinProg 15.4.2009, 14:29
это как раз вариант с оберткой _beginthreadex:
Код

    void thread::start_thread()
    {
        uintptr_t const new_thread=_beginthreadex(0,0,&thread_start_function,thread_info.get(),CREATE_SUSPENDED,&thread_info->id);
        if(!new_thread)
        {
            throw thread_resource_error();
        }
        intrusive_ptr_add_ref(thread_info.get());
        thread_info->thread_handle=(detail::win32::handle)(new_thread);
        ResumeThread(thread_info->thread_handle);
    }


 и функции потока:
Код

        unsigned __stdcall thread_start_function(void* param)
        {
            detail::thread_data_base* const thread_info(reinterpret_cast<detail::thread_data_base*>(param));
            set_current_thread_data(thread_info);
            try
            {
                thread_info->run();
            }
            catch(thread_interrupted const&)
            {
            }
// Removed as it stops the debugger identifying the cause of the exception
// Unhandled exceptions still cause the application to terminate
//             catch(...)
//             {
//                 std::terminate();
//             }
            run_thread_exit_callbacks();
            return 0;
        }


set_current_thread_data - инициализация
run_thread_exit_callbacks - вызов всех ассоциированных callback'ов ну и попутное освобождение потоковых данных, если таковые были
очевидно, есть свободные методы регистрации callback'ов
отсюда можно плясать дальше, только для реализации такого механизма привязываться к boost'у совсем не обязательно

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)