summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/threads/thread_specific_data_extension.h
blob: 0b5339200e03be450afa27afa9613b3d7fafc988 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (C) 2006  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_
#define DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_

#include "thread_specific_data_extension_abstract.h"
#include "threads_kernel_abstract.h"
#include "../binary_search_tree.h"
#include "auto_mutex_extension.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------

    template <
        typename T
        >
    class thread_specific_data
    {
        /*!
            CONVENTION
                - for all valid ID:
                  (*items[ID]) == pointer to the data for thread with id ID
        !*/
    public:

        thread_specific_data (
        )
        {
            thread_end_handler_calls_left = 0;
        }

        ~thread_specific_data (
        )
        {
            // We should only call the unregister_thread_end_handler function if there are
            // some outstanding callbacks we expect to get.  Otherwise lets avoid calling it
            // since the dlib state that maintains the registered thread end handlers may have
            // been destructed already (since the program might be in the process of terminating).
            bool call_unregister = false;
            m.lock();
            if (thread_end_handler_calls_left > 0)
                call_unregister = true;
            m.unlock();

            if (call_unregister)
                unregister_thread_end_handler(const_cast<thread_specific_data&>(*this),&thread_specific_data::thread_end_handler);

            auto_mutex M(m);
            items.reset();
            while (items.move_next())
            {
                delete items.element().value();
            }
        }

        inline T& data (
        ) { return get_data(); }

        inline const T& data (
        ) const { return get_data(); }

    private:

        T& get_data (
        ) const
        {
            thread_id_type id = get_thread_id();
            auto_mutex M(m);

            T** item = items[id];
            if (item)
            {
                return **item;
            }
            else
            {
                // register an end handler for this thread so long as it is a dlib created thread.
                T* new_item = new T;

                bool in_tree = false;
                try
                {
                    T* temp_item = new_item;
                    thread_id_type temp_id = id;
                    items.add(temp_id,temp_item);
                    in_tree = true;

                    if (is_dlib_thread(id))
                    {
                        register_thread_end_handler(const_cast<thread_specific_data&>(*this),&thread_specific_data::thread_end_handler);
                        ++thread_end_handler_calls_left;
                    }
                }
                catch (...)
                {
                    if (in_tree)
                    {
                        items.destroy(id);
                    }
                    delete new_item;
                    throw;
                }

                return *new_item;
            }
        }

        void thread_end_handler (
        )
        {
            const thread_id_type id = get_thread_id();
            thread_id_type junk = 0;
            T* item = 0;
            auto_mutex M(m);
            --thread_end_handler_calls_left;
            if (items[id])
            {
                items.remove(id,junk,item);
                delete item;
            }
        }

        mutable typename binary_search_tree<thread_id_type,T*>::kernel_2a items;
        mutex m;
        mutable long thread_end_handler_calls_left;

        // restricted functions
        thread_specific_data(thread_specific_data&);        // copy constructor
        thread_specific_data& operator=(thread_specific_data&);    // assignment operator
    };

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_