xref: /glogg/src/data/threadprivatestore.h (revision 6e2e573c451ec24d720c967fab68538eaa3f3ab5)
1 /*
2  * Copyright (C) 2015 Nicolas Bonnefon and other contributors
3  *
4  * This file is part of glogg.
5  *
6  * glogg is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glogg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef THREADPRIVATESTORE_H
21 #define THREADPRIVATESTORE_H
22 
23 #include <atomic>
24 #include <thread>
25 #include <cassert>
26 
27 #include "log.h"
28 
29 // This template stores data that is private to the calling thread
30 // in a completely wait-free manner.
31 template <typename T, int MAX_THREADS>
32 class ThreadPrivateStore
33 {
34   public:
35     // Construct an empty ThreadPrivateStore
36     ThreadPrivateStore() : nb_threads_( 0 ) {
37         // Last one is a guard
38         for ( int i=0; i<(MAX_THREADS+1); ++i )
39             thread_ids_[i] = 0;
40     }
41 
42     // Conversion to a T
43     operator T() const
44     { return get(); }
45 
46     // Getter (thread-safe, wait-free)
47     T get() const
48     { return data_[threadIndex()]; }
49 
50     T* getPtr()
51     { return &data_[threadIndex()]; }
52 
53     // Setter (thread-safe, wait-free)
54     void set( const T& value )
55     { data_[threadIndex()] = value; }
56 
57   private:
58     // Nb of threads that have registered
59     int nb_threads_;
60 
61     // The actual data array (one element per thread from 0 to nb_threads_)
62     T data_[MAX_THREADS];
63 
64     mutable std::atomic<size_t> thread_ids_[MAX_THREADS+1];
65 
66     int threadIndex() const {
67         int i;
68         for ( i=0; thread_ids_[i]; ++i ) {
69             if ( thread_ids_[i].load() ==
70                     std::hash<std::thread::id>()(std::this_thread::get_id() ) ) { return i; }
71         }
72 
73         // Current thread is missing, let's add it
74         size_t thread_id = std::hash<std::thread::id>()( std::this_thread::get_id() );
75         while ( i < MAX_THREADS ) {
76             size_t expected = 0;
77             if ( thread_ids_[i++].compare_exchange_weak( expected, thread_id ) ) {
78                 LOG(logDEBUG) << "Created thread for " << thread_id << " at index " << i-1;
79                 return i-1;
80             }
81         }
82 
83         assert( 1 );
84         return 0;
85     }
86 };
87 
88 #endif
89