• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 //          Copyright Oliver Kowalke 2017.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 #include "boost/fiber/numa/topology.hpp"
8 
9 #include <exception>
10 #include <map>
11 #include <regex>
12 #include <set>
13 #include <string>
14 #include <utility>
15 
16 #include <boost/algorithm/string.hpp>
17 #include <boost/assert.hpp>
18 #include <boost/config.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/filesystem/fstream.hpp>
21 #include <boost/format.hpp>
22 
23 #include "boost/fiber/exceptions.hpp"
24 
25 #ifdef BOOST_HAS_ABI_HEADERS
26 # include BOOST_ABI_PREFIX
27 #endif
28 
29 #if !defined(BOOST_NO_CXX11_HDR_REGEX)
30 
31 namespace al = boost::algorithm;
32 namespace fs = boost::filesystem;
33 
34 namespace {
35 
36 class directory_iterator {
37 private:
38     fs::directory_iterator          i_;
39     fs::directory_iterator          e_;
40     std::regex                      exp_;
41     std::pair< std::uint32_t, fs::path > idx_;
42 
eval_(fs::directory_entry const & entry)43     bool eval_( fs::directory_entry const& entry) {
44         std::string filename( entry.path().filename().string() );
45         std::smatch what;
46         if ( ! std::regex_search( filename, what, exp_) ) {
47             return false;
48         }
49         idx_ = std::make_pair( std::stoul( what[1].str() ), entry.path() );
50         return true;
51     }
52 
53 public:
54     typedef std::input_iterator_tag iterator_category;
55     typedef const std::pair< std::uint32_t, fs::path > value_type;
56     typedef std::ptrdiff_t difference_type;
57     typedef value_type * pointer;
58     typedef value_type & reference;
59 
directory_iterator()60     directory_iterator() :
61         i_(), e_(), exp_(), idx_() {
62     }
63 
directory_iterator(fs::path const & dir,std::string const & exp)64     directory_iterator( fs::path const& dir, std::string const& exp) :
65         i_( dir), e_(), exp_( exp), idx_() {
66         while ( i_ != e_ && ! eval_( * i_) ) {
67             ++i_;
68         }
69     }
70 
operator ==(directory_iterator const & other)71     bool operator==( directory_iterator const& other) {
72         return i_ == other.i_;
73     }
74 
operator !=(directory_iterator const & other)75     bool operator!=( directory_iterator const& other) {
76         return i_ != other.i_;
77     }
78 
operator ++()79     directory_iterator & operator++() {
80         do {
81             ++i_;
82         } while ( i_ != e_ && ! eval_( * i_) );
83         return * this;
84     }
85 
operator ++(int)86     directory_iterator operator++( int) {
87         directory_iterator tmp( * this);
88         ++*this;
89         return tmp;
90     }
91 
operator *() const92     reference operator*() const {
93         return idx_;
94     }
95 
operator ->() const96     pointer operator->() const {
97         return & idx_;
98     }
99 };
100 
ids_from_line(std::string const & content)101 std::set< std::uint32_t > ids_from_line( std::string const& content) {
102     std::set< std::uint32_t > ids;
103     std::vector< std::string > v1;
104     al::split( v1, content, al::is_any_of(",") );
105     for ( std::string entry : v1) {
106         al::trim( entry);
107         std::vector< std::string > v2;
108         al::split( v2, entry, al::is_any_of("-") );
109         BOOST_ASSERT( ! v2.empty() );
110         if ( 1 == v2.size() ) {
111             // only one ID
112             ids.insert( std::stoul( v2[0]) );
113         } else {
114             // range of IDs
115             std::uint32_t first = std::stoul( * v2.begin() );
116             std::uint32_t last = std::stoul( * v2.rbegin() );
117             for ( std::uint32_t i = first; i <= last; ++i) {
118                 ids.insert( i);
119             }
120         }
121     }
122     return ids;
123 }
124 
distance_from_line(std::string const & content)125 std::vector< std::uint32_t > distance_from_line( std::string const& content) {
126     std::vector< std::uint32_t > distance;
127     std::vector< std::string > v1;
128     al::split( v1, content, al::is_any_of(" ") );
129     for ( std::string entry : v1) {
130         al::trim( entry);
131         BOOST_ASSERT( ! entry.empty() );
132         distance.push_back( std::stoul( entry) );
133     }
134     return distance;
135 }
136 
137 }
138 
139 namespace boost {
140 namespace fibers {
141 namespace numa {
142 
143 BOOST_FIBERS_DECL
topology()144 std::vector< node > topology() {
145     std::vector< node > topo;
146     // 1. parse list of CPUs which are online
147     fs::ifstream fs_online{ fs::path("/sys/devices/system/cpu/online") };
148     std::string content;
149     std::getline( fs_online, content);
150     std::set< std::uint32_t > cpus = ids_from_line( content);
151     if ( cpus.empty() ) {
152         // parsing cpus failed
153         return topo;
154     }
155     std::map< std::uint32_t, node > map;
156     // iterate list of logical cpus
157     for ( std::uint32_t cpu_id : cpus) {
158         fs::path cpu_path{
159             boost::str(
160                 boost::format("/sys/devices/system/cpu/cpu%d/") % cpu_id) };
161         BOOST_ASSERT( fs::exists( cpu_path) );
162         // 2. to witch NUMA node the CPU belongs to
163         directory_iterator e;
164         for ( directory_iterator i{ cpu_path, "^node([0-9]+)$" };
165               i != e; ++i) {
166             std::uint32_t node_id = i->first;
167             map[node_id].id = node_id;
168             map[node_id].logical_cpus.insert( cpu_id);
169             // assigned to only one NUMA node
170             break;
171         }
172     }
173     if ( map.empty() ) {
174         // maybe /sys/devices/system/cpu/cpu[0-9]/node[0-9] was not defined
175         // put all CPUs to NUMA node 0
176         map[0].id = 0;
177         for ( std::uint32_t cpu_id : cpus) {
178             map[0].logical_cpus.insert( cpu_id);
179         }
180     }
181     for ( auto entry : map) {
182         // NUMA-node distance
183         fs::path distance_path{
184             boost::str(
185                 boost::format("/sys/devices/system/node/node%d/distance") % entry.second.id) };
186         if ( fs::exists( distance_path) ) {
187             fs::ifstream fs_distance{ distance_path };
188             std::string content;
189             std::getline( fs_distance, content);
190             entry.second.distance = distance_from_line( content);
191             topo.push_back( entry.second);
192         } else {
193             // fake NUMA distance
194             entry.second.distance.push_back( 10);
195             topo.push_back( entry.second);
196         }
197     }
198     return topo;
199 }
200 
201 }}}
202 
203 #else
204 
205 namespace boost {
206 namespace fibers {
207 namespace numa {
208 
209 #if BOOST_COMP_CLANG || \
210     BOOST_COMP_GNUC || \
211     BOOST_COMP_INTEL ||  \
212     BOOST_COMP_MSVC
213 # pragma message "topology() not supported without <regex>"
214 #endif
215 
216 BOOST_FIBERS_DECL
topology()217 std::vector< node > topology() {
218     throw fiber_error{
219         std::make_error_code( std::errc::function_not_supported),
220             "boost fiber: topology() not supported without <regex>" };
221     return std::vector< node >{};
222 }
223 
224 }}}
225 
226 #endif
227 
228 #ifdef BOOST_HAS_ABI_HEADERS
229 # include BOOST_ABI_SUFFIX
230 #endif
231