MADNESS 0.10.1
thread_specific.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program 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 2 of the License, or
9 (at your option) any later version.
10
11 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30*/
31
32#ifndef MADNESS_WORLD_THREAD_SPECIFIC_H__INCLUDED
33#define MADNESS_WORLD_THREAD_SPECIFIC_H__INCLUDED
34
35/**
36 \file thread_specific.h
37 \brief Reclaimable thread-specific storage (a `thread_local` you can free).
38 \ingroup threads
39*/
40
42
43#include <pthread.h>
44
45#include <atomic>
46#include <cerrno>
47#include <cstdint>
48#include <map>
49#include <memory>
50#include <mutex>
51#include <thread>
52#include <utility>
53
54namespace madness {
55 namespace detail {
56
57 /// Thread-specific storage of one \c Item per touching thread that,
58 /// unlike a \c thread_local, can be reclaimed on demand.
59
60 /// The items are owned by this object — a \c std::map keyed on
61 /// \c std::thread::id, guarded by a mutex, with a per-thread
62 /// \c pthread_key caching the item pointer so the steady-state
63 /// \c local() is lock-free (one \c pthread_getspecific plus a
64 /// generation check). Because ownership lives in the object rather
65 /// than in thread-static storage, destroying — or \c clear()ing — the
66 /// pool frees every item at a defined point.
67 ///
68 /// This matters for large scratch buffers: a plain \c thread_local
69 /// pins its storage for the life of the OS thread (≈ the process), so
70 /// under the TBB / PaRSEC task backends — whose arenas can touch more
71 /// distinct threads than \c MAD_NUM_THREADS — it leaks one full item
72 /// per touching thread with no way to reclaim it. Modeled on
73 /// \c tbb::enumerable_thread_specific, trimmed to \c local()/\c clear().
74 ///
75 /// \c Item must be copy-constructible; the seed passed to the
76 /// constructor is copied to initialize each thread's instance.
77 /// \c local() is safe to call concurrently from different threads.
78 /// \c clear() is \em not safe to call concurrently with \c local() (or
79 /// while any reference previously returned by \c local() is still in
80 /// use); call it only at a quiescent point, e.g. after a fence.
81 template <typename Item>
83 public:
84 explicit thread_specific(Item init = Item())
85 : init_(std::move(init)), gen_(0),
87 const int rc = pthread_key_create(key_ptr_.get(), &slot_deleter);
88 if (rc == EAGAIN) {
89 MADNESS_EXCEPTION("thread_specific: PTHREAD_KEYS_MAX reached", rc);
90 } else if (rc == ENOMEM) {
91 MADNESS_EXCEPTION("thread_specific: out of memory creating pthread key", rc);
92 } else if (rc != 0) {
93 MADNESS_EXCEPTION("thread_specific: pthread_key_create failed", rc);
94 }
95 }
96
97 // Non-copyable and non-movable: holds a pthread key and an atomic,
98 // and is meant to live where it is constructed (e.g. a static).
103
104 /// \return reference to the calling thread's \c Item, creating it on
105 /// first use. Lock-free once the thread has been seen at the
106 /// current generation.
108 Slot* s = static_cast<Slot*>(pthread_getspecific(key()));
109 if (s != nullptr && s->gen == gen_.load(std::memory_order_acquire))
110 return *s->item;
111 return local_slow(s);
112 }
113
114 /// Free every thread's \c Item. A thread that later calls
115 /// \c local() gets a freshly seeded one. Not safe to call
116 /// concurrently with \c local(); see the class note.
117 void clear() {
118 std::lock_guard<std::mutex> lock(mtx_);
119 items_.clear();
120 gen_.fetch_add(1, std::memory_order_acq_rel);
121 }
122
123 /// \return number of live per-thread items (diagnostic; takes the lock).
124 std::size_t size() const {
125 std::lock_guard<std::mutex> lock(mtx_);
126 return items_.size();
127 }
128
129 private:
130 /// Per-thread fast-path cache stored in the pthread key. \c gen
131 /// stamps which \c clear() generation \c item is valid for.
132 struct Slot {
133 std::uint64_t gen;
135 };
136
137 pthread_key_t& key() { return *key_ptr_; }
138
140 std::lock_guard<std::mutex> lock(mtx_);
141 const auto tid = std::this_thread::get_id();
142 auto it = items_.find(tid);
143 if (it == items_.end())
144 it = items_.emplace(tid, std::make_unique<Item>(init_)).first;
145 if (s == nullptr) {
146 s = new Slot;
148 }
149 s->item = it->second.get();
150 s->gen = gen_.load(std::memory_order_relaxed);
151 return *s->item;
152 }
153
154 static void slot_deleter(void* p) { delete static_cast<Slot*>(p); }
156 if (key_ptr) {
158 delete key_ptr;
159 }
160 }
161
162 Item init_; ///< seed for new items
163 std::atomic<std::uint64_t> gen_; ///< bumped by clear()
164 mutable std::mutex mtx_; ///< guards items_
165 std::map<std::thread::id, std::unique_ptr<Item>> items_;
166 std::unique_ptr<pthread_key_t, decltype(&pthread_key_deleter)> key_ptr_;
167 };
168
169 } // namespace detail
170} // namespace madness
171
172#endif // MADNESS_WORLD_THREAD_SPECIFIC_H__INCLUDED
Definition thread_specific.h:82
Item init_
seed for new items
Definition thread_specific.h:162
thread_specific(const thread_specific &)=delete
Item & local_slow(Slot *s)
Definition thread_specific.h:139
void clear()
Definition thread_specific.h:117
thread_specific(thread_specific &&)=delete
Item & local()
Definition thread_specific.h:107
std::mutex mtx_
guards items_
Definition thread_specific.h:164
std::unique_ptr< pthread_key_t, decltype(&pthread_key_deleter)> key_ptr_
Definition thread_specific.h:166
std::atomic< std::uint64_t > gen_
bumped by clear()
Definition thread_specific.h:163
thread_specific(Item init=Item())
Definition thread_specific.h:84
std::size_t size() const
Definition thread_specific.h:124
static void slot_deleter(void *p)
Definition thread_specific.h:154
static void pthread_key_deleter(const pthread_key_t *key_ptr)
Definition thread_specific.h:155
thread_specific & operator=(thread_specific &&)=delete
thread_specific & operator=(const thread_specific &)=delete
std::map< std::thread::id, std::unique_ptr< Item > > items_
Definition thread_specific.h:165
pthread_key_t & key()
Definition thread_specific.h:137
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
Defines madness::MadnessException for exception handling.
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
Definition potentialmanager.cc:41
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
Definition mraimpl.h:51
Definition thread_specific.h:132
std::uint64_t gen
Definition thread_specific.h:133
Item * item
Definition thread_specific.h:134