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 extern "C" {
10 #include <windows.h>
11 }
12
13 #include <map>
14 #include <set>
15 #include <system_error>
16 #include <vector>
17
18 #include <boost/assert.hpp>
19
20 #ifdef BOOST_HAS_ABI_HEADERS
21 # include BOOST_ABI_PREFIX
22 #endif
23
24 namespace {
25
26 class procinfo_iterator {
27 private:
28 using SLPI = SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
29
30 SLPI * buffer_{ nullptr };
31 SLPI * procinfo_{ nullptr };
32 DWORD length_{ 0 };
33
34 public:
35 procinfo_iterator() = default;
36
procinfo_iterator(LOGICAL_PROCESSOR_RELATIONSHIP relship)37 procinfo_iterator( LOGICAL_PROCESSOR_RELATIONSHIP relship) {
38 if ( ::GetLogicalProcessorInformationEx( relship, nullptr, & length_) ) {
39 return;
40 }
41 if ( BOOST_UNLIKELY( ERROR_INSUFFICIENT_BUFFER != ::GetLastError() ) ) {
42 throw std::system_error{
43 std::error_code{ static_cast< int >( ::GetLastError() ), std::system_category() },
44 "::GetLogicalProcessorInformation() failed" };
45 }
46 buffer_ = reinterpret_cast< SLPI * >( LocalAlloc( LMEM_FIXED, length_) );
47 if ( BOOST_UNLIKELY( nullptr == buffer_) ) {
48 throw std::bad_alloc();
49 }
50 if ( BOOST_UNLIKELY( ! ::GetLogicalProcessorInformationEx( relship, buffer_, & length_) ) ) {
51 throw std::system_error{
52 std::error_code{ static_cast< int >( ::GetLastError() ), std::system_category() },
53 "::GetLogicalProcessorInformation() failed" };
54 }
55 procinfo_ = buffer_;
56 }
57
operator ++()58 procinfo_iterator & operator++() noexcept {
59 if ( nullptr != procinfo_) {
60 length_ -= procinfo_->Size;
61 if ( 0 != length_) {
62 procinfo_ = reinterpret_cast< SLPI * >( reinterpret_cast< BYTE * >( procinfo_) + procinfo_->Size);
63 } else {
64 LocalFree( buffer_);
65 buffer_ = nullptr;
66 procinfo_ = nullptr;
67 }
68 }
69 return * this;
70 }
71
operator ++(int)72 procinfo_iterator operator++( int) {
73 procinfo_iterator tmp( * this);
74 operator++();
75 return tmp;
76 }
77
operator ->()78 SLPI * operator->() noexcept {
79 return procinfo_;
80 }
81
operator ==(procinfo_iterator const & other) const82 bool operator==( procinfo_iterator const& other) const noexcept {
83 return other.buffer_ == buffer_ && other.procinfo_ == procinfo_ && other.length_ == length_;
84 }
85
operator !=(procinfo_iterator const & other) const86 bool operator!=( procinfo_iterator const& other) const noexcept {
87 return ! ( * this == other);
88 }
89 };
90
compute_cpu_set(WORD group_id,KAFFINITY mask)91 std::set< std::uint32_t > compute_cpu_set( WORD group_id, KAFFINITY mask) {
92 std::set< std::uint32_t > cpus;
93 for ( int i = 0; i < sizeof( mask) * 8; ++i) {
94 if ( mask & ( static_cast< KAFFINITY >( 1) << i) ) {
95 cpus.insert( static_cast< std::uint32_t >( 64 * group_id + i) );
96 }
97 }
98 return cpus;
99 }
100
101 }
102
103 namespace boost {
104 namespace fibers {
105 namespace numa {
106
topology()107 std::vector< node > topology() {
108 std::vector< node > topo;
109 procinfo_iterator e;
110 for ( procinfo_iterator i{ RelationNumaNode }; i != e; ++i) {
111 node n;
112 n.id = static_cast< std::uint32_t >( i->NumaNode.NodeNumber);
113 auto cpus = compute_cpu_set( i->NumaNode.GroupMask.Group, i->NumaNode.GroupMask.Mask);
114 for ( auto cpu_id : cpus) {
115 n.logical_cpus.insert( static_cast< std::uint32_t >( cpu_id) );
116 }
117 topo.push_back( n);
118 }
119 // fake NUMA distance
120 std::size_t size = topo.size();
121 for ( auto & n : topo) {
122 for ( std::size_t i = 0; i < size; ++i) {
123 n.distance.push_back( n.id == i ? 10 : 20);
124 }
125 }
126 return topo;
127 }
128
129 }}}
130
131 #ifdef BOOST_HAS_ABI_HEADERS
132 # include BOOST_ABI_SUFFIX
133 #endif
134