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