add Makefile and instructions to run programs
[c11concurrency-benchmarks.git] / silo / core.h
1 #pragma once
2
3 #include <atomic>
4 #include <sys/types.h>
5 #include "macros.h"
6 #include "util.h"
7
8 /**
9  * XXX: CoreIDs are not recyclable for now, so NMAXCORES is really the number
10  * of threads which can ever be spawned in the system
11  */
12 class coreid {
13 public:
14   static const unsigned NMaxCores = NMAXCORES;
15
16   static inline unsigned
17   core_id()
18   {
19     if (unlikely(tl_core_id == -1)) {
20       // initialize per-core data structures
21       tl_core_id = g_core_count.fetch_add(1, std::memory_order_acq_rel);
22       // did we exceed max cores?
23       ALWAYS_ASSERT(unsigned(tl_core_id) < NMaxCores);
24     }
25     return tl_core_id;
26   }
27
28   /**
29    * Since our current allocation scheme does not allow for holes in the
30    * allocation, this function is quite wasteful. Don't abuse.
31    *
32    * Returns -1 if it is impossible to do this w/o exceeding max allocations
33    */
34   static int
35   allocate_contiguous_aligned_block(unsigned n, unsigned alignment);
36
37   /**
38    * WARNING: this function is scary, and exists solely as a hack
39    *
40    * You are allowed to set your own core id under several conditions
41    * (the idea is that somebody else has allocated a block of core ids
42    *  and is assigning one to you, under the promise of uniqueness):
43    *
44    * 1) You haven't already called core_id() yet (so you have no assignment)
45    * 2) The number you are setting is < the current assignment counter (meaning
46    *    it was previously assigned by someone)
47    *
48    * These are necessary but not sufficient conditions for uniqueness
49    */
50   static void
51   set_core_id(unsigned cid)
52   {
53     ALWAYS_ASSERT(cid < NMaxCores);
54     ALWAYS_ASSERT(cid < g_core_count.load(std::memory_order_acquire));
55     ALWAYS_ASSERT(tl_core_id == -1);
56     tl_core_id = cid; // sigh
57   }
58
59   // actual number of CPUs online for the system
60   static unsigned num_cpus_online();
61
62 private:
63   // the core ID of this core: -1 if not set
64   static __thread int tl_core_id;
65
66   // contains a running count of all the cores
67   static std::atomic<unsigned> g_core_count CACHE_ALIGNED;
68 };
69
70 // requires T to have no-arg ctor
71 template <typename T, bool CallDtor = false, bool Pedantic = true>
72 class percore {
73 public:
74
75   percore()
76   {
77     for (size_t i = 0; i < size(); i++) {
78       using namespace util;
79       new (&(elems()[i])) aligned_padded_elem<T, Pedantic>();
80     }
81   }
82
83   ~percore()
84   {
85     if (!CallDtor)
86       return;
87     for (size_t i = 0; i < size(); i++) {
88       using namespace util;
89       elems()[i].~aligned_padded_elem<T, Pedantic>();
90     }
91   }
92
93   inline T &
94   operator[](unsigned i)
95   {
96     INVARIANT(i < NMAXCORES);
97     return elems()[i].elem;
98   }
99
100   inline const T &
101   operator[](unsigned i) const
102   {
103     INVARIANT(i < NMAXCORES);
104     return elems()[i].elem;
105   }
106
107   inline T &
108   my()
109   {
110     return (*this)[coreid::core_id()];
111   }
112
113   inline const T &
114   my() const
115   {
116     return (*this)[coreid::core_id()];
117   }
118
119   // XXX: make an iterator
120
121   inline size_t
122   size() const
123   {
124     return NMAXCORES;
125   }
126
127 protected:
128
129   inline util::aligned_padded_elem<T, Pedantic> *
130   elems()
131   {
132     return (util::aligned_padded_elem<T, Pedantic> *) &bytes_[0];
133   }
134
135   inline const util::aligned_padded_elem<T, Pedantic> *
136   elems() const
137   {
138     return (const util::aligned_padded_elem<T, Pedantic> *) &bytes_[0];
139   }
140
141   char bytes_[sizeof(util::aligned_padded_elem<T, Pedantic>) * NMAXCORES];
142 };
143
144 namespace private_ {
145   template <typename T>
146   struct buf {
147     char bytes_[sizeof(T)];
148     inline T * cast() { return (T *) &bytes_[0]; }
149     inline const T * cast() const { return (T *) &bytes_[0]; }
150   };
151 }
152
153 template <typename T>
154 class percore_lazy : private percore<private_::buf<T>, false> {
155   typedef private_::buf<T> buf_t;
156 public:
157
158   percore_lazy()
159   {
160     NDB_MEMSET(&flags_[0], 0, sizeof(flags_));
161   }
162
163   template <class... Args>
164   inline T &
165   get(unsigned i, Args &&... args)
166   {
167     buf_t &b = this->elems()[i].elem;
168     if (unlikely(!flags_[i])) {
169       flags_[i] = true;
170       T *px = new (&b.bytes_[0]) T(std::forward<Args>(args)...);
171       return *px;
172     }
173     return *b.cast();
174   }
175
176   template <class... Args>
177   inline T &
178   my(Args &&... args)
179   {
180     return get(coreid::core_id(), std::forward<Args>(args)...);
181   }
182
183   inline T *
184   view(unsigned i)
185   {
186     buf_t &b = this->elems()[i].elem;
187     return flags_[i] ? b.cast() : nullptr;
188   }
189
190   inline const T *
191   view(unsigned i) const
192   {
193     const buf_t &b = this->elems()[i].elem;
194     return flags_[i] ? b.cast() : nullptr;
195   }
196
197   inline const T *
198   myview() const
199   {
200     return view(coreid::core_id());
201   }
202
203 private:
204   bool flags_[NMAXCORES];
205   CACHE_PADOUT;
206 };