• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.29
/examples/quickstart/component_with_custom_heap.cpp
1
//  Copyright (c) 2019-2025 Hartmut Kaiser
2
//
3
//  SPDX-License-Identifier: BSL-1.0
4
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
5
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6

7
// The purpose of this example is to demonstrate how to control the memory
8
// placement of newly created component instances. Here we use a simple
9
// free-list allocator to make sure all created components are placed
10
// consecutively in memory.
11
// After creating 1000 component instances this example directly accesses those
12
// in local memory without having to go through the AGAS address resolution.
13

14
// make inspect happy: hpxinspect:noinclude:HPX_ASSERT
15

16
#include <hpx/config.hpp>
17
#if !defined(HPX_COMPUTE_DEVICE_CODE)
18
#include <hpx/hpx_main.hpp>
19
#include <hpx/include/actions.hpp>
20
#include <hpx/include/components.hpp>
21
#include <hpx/include/lcos.hpp>
22
#include <hpx/include/util.hpp>
23
#include <hpx/iostream.hpp>
24

25
#include <array>
26
#include <cstddef>
27
#include <cstdint>
28
#include <cstdlib>
29
#include <memory>
30
#include <mutex>
31
#include <type_traits>
32
#include <vector>
33

34
///////////////////////////////////////////////////////////////////////////////
35
// Implementation of a free_list allocator, this has no bearings on the example
36
// component below.
37
namespace allocator {
38
    ///////////////////////////////////////////////////////////////////////////
39
    constexpr std::size_t BLOCK_ALIGNMENT = 8;
40
    constexpr std::size_t PAGE_SIZE_ = 16384;
41

42
    struct alloc_page;
43

44
    ///////////////////////////////////////////////////////////////////////////
45
    struct alloc_block_header
46
    {
47
        constexpr HPX_FORCEINLINE alloc_block_header(
48
            alloc_page* p = nullptr) noexcept
49
          : page(p)
1,022✔
50
        {
51
        }
52

53
        ~alloc_block_header() = default;
54

55
        alloc_block_header(alloc_block_header const&) = delete;
56
        alloc_block_header(alloc_block_header&&) = delete;
57

58
        alloc_block_header& operator=(alloc_block_header const&) = delete;
59
        alloc_block_header& operator=(alloc_block_header&&) = delete;
60

61
        alloc_page* page;
62
    };
63

64
    template <typename T>
65
    struct alloc_block : alloc_block_header
66
    {
67
        static constexpr std::size_t allocation_size =
68
            ((sizeof(T) + BLOCK_ALIGNMENT - 1) & ~(BLOCK_ALIGNMENT - 1)) +
69
            sizeof(alloc_block_header);
70

71
        constexpr HPX_FORCEINLINE alloc_block(
1,022✔
72
            alloc_page* page = nullptr) noexcept
73
          : alloc_block_header(page)
74
          , next_free(nullptr)
2✔
75
        {
76
        }
77

78
        ~alloc_block() = default;
79

80
        alloc_block(alloc_block const&) = delete;
81
        alloc_block(alloc_block&&) = delete;
82

83
        alloc_block& operator=(alloc_block const&) = delete;
84
        alloc_block& operator=(alloc_block&&) = delete;
85

86
        HPX_FORCEINLINE alloc_block* operator[](std::size_t i) noexcept
87
        {
88
            return reinterpret_cast<alloc_block*>(
89
                reinterpret_cast<char*>(this) + i * allocation_size);
1,020✔
90
        }
91
        HPX_FORCEINLINE alloc_block const* operator[](
92
            std::size_t i) const noexcept
93
        {
94
            return reinterpret_cast<alloc_block const*>(
95
                reinterpret_cast<char const*>(this) + i * allocation_size);
1,000✔
96
        }
97

98
        alloc_block* next_free;
99
    };
100

101
    ///////////////////////////////////////////////////////////////////////////
102
    template <typename T>
103
    class free_list_allocator
104
    {
105
        using mutex_type = hpx::spinlock;
106

107
    public:
108
        free_list_allocator()
×
109
          : chain(nullptr)
1✔
110
          , pages(nullptr)
1✔
111
        {
112
        }
×
113

114
        ~free_list_allocator() = default;
115

116
        free_list_allocator(free_list_allocator const&) = delete;
117
        free_list_allocator(free_list_allocator&&) = delete;
118

119
        free_list_allocator& operator=(free_list_allocator const&) = delete;
120
        free_list_allocator& operator=(free_list_allocator&&) = delete;
121

122
        HPX_FORCEINLINE static free_list_allocator& get_allocator();
123

124
        constexpr HPX_FORCEINLINE alloc_page const* first_page() const
125
        {
126
            return pages;
1✔
127
        }
128

129
        void* alloc();
130
        void free(void* addr);
131

132
    private:
133
        friend struct alloc_page;
134

135
        alloc_block<T>* chain;
136
        alloc_page* pages;
137
        mutex_type mtx;
138
    };
139

140
    ///////////////////////////////////////////////////////////////////////////
141
    struct alloc_page
142
    {
143
        HPX_FORCEINLINE explicit alloc_page(std::size_t size) noexcept
144
          : next(nullptr)
2✔
145
          , allocated_blocks(0)
2✔
146
          , block_size(size)
2✔
147
        {
148
        }
149

150
        // FIXME: loop over blocks and call destructor
151
        ~alloc_page() = default;
152

153
        alloc_page(alloc_page const&) = delete;
154
        alloc_page& operator=(alloc_page const&) = delete;
155

156
        alloc_page(alloc_page&&) = delete;
157
        alloc_page& operator=(alloc_page&&) = delete;
158

159
        template <typename T>
160
        HPX_FORCEINLINE alloc_block<T>* get_block() noexcept
161
        {
162
            static_assert(page_size >= alloc_block<T>::allocation_size,
163
                "size of objects is larger than configured page size");
164

165
            // block_size must match allocation size of requested type
166
            HPX_ASSERT(block_size == alloc_block<T>::allocation_size);
167

168
            return reinterpret_cast<alloc_block<T>*>(&data);
169
        }
170
        template <typename T>
171
        HPX_FORCEINLINE alloc_block<T> const* get_block() const noexcept
172
        {
173
            static_assert(page_size >= alloc_block<T>::allocation_size,
174
                "size of objects is larger than configured page size");
175

176
            // block_size must match allocation size of requested type
177
            HPX_ASSERT(block_size == alloc_block<T>::allocation_size);
178

179
            return reinterpret_cast<alloc_block<T> const*>(&data);
1,000✔
180
        }
181

182
        template <typename T>
183
        HPX_FORCEINLINE T& get(std::size_t i) noexcept
184
        {
185
            static_assert(page_size >= alloc_block<T>::allocation_size,
186
                "size of objects is larger than configured page size");
187

188
            // block_size must match allocation size of requested type
189
            HPX_ASSERT(block_size == alloc_block<T>::allocation_size);
190

191
            // NOLINTNEXTLINE(bugprone-casting-through-void)
192
            return *reinterpret_cast<T*>(static_cast<void*>(
193
                static_cast<alloc_block_header*>((*get_block<T>())[i]) + 1));
194
        }
195
        template <typename T>
196
        HPX_FORCEINLINE T const& get(std::size_t i) const noexcept
197
        {
198
            static_assert(page_size >= alloc_block<T>::allocation_size,
199
                "size of objects is larger than configured page size");
200

201
            // block_size must match allocation size of requested type
202
            HPX_ASSERT(block_size == alloc_block<T>::allocation_size);
203

204
            // NOLINTNEXTLINE(bugprone-casting-through-void)
205
            return *reinterpret_cast<T const*>(static_cast<void const*>(
1,000✔
206
                static_cast<alloc_block_header const*>((*get_block<T>())[i]) +
207
                1));
208
        }
209

210
        // for the available page size we account for the members of this
211
        // class below
212
        static constexpr std::size_t page_size =
213
            PAGE_SIZE_ - sizeof(void*) - 2 * sizeof(std::size_t);
214

215
        hpx::aligned_storage_t<page_size> data;
216

217
        alloc_page* next;
218
        std::size_t allocated_blocks;
219
        std::size_t const block_size;
220
    };
221

222
    template <typename T>
223
    constexpr HPX_FORCEINLINE T& get(alloc_page* page, std::size_t i) noexcept
224
    {
225
        return page->template get<T>(i);
226
    }
227
    template <typename T>
228
    constexpr HPX_FORCEINLINE T const& get(
229
        alloc_page const* page, std::size_t i) noexcept
230
    {
231
        return page->template get<T>(i);
232
    }
233

234
    ///////////////////////////////////////////////////////////////////////////
235
    template <typename T>
236
    HPX_FORCEINLINE free_list_allocator<T>&
237
    free_list_allocator<T>::get_allocator()
1✔
238
    {
239
        static free_list_allocator ctx{};
240
        return ctx;
241
    }
242

243
    ///////////////////////////////////////////////////////////////////////////
1,000✔
244
    template <typename T>
245
    void* free_list_allocator<T>::alloc()
1,000✔
246
    {
247
        std::unique_lock<mutex_type> lk(mtx);
1,000✔
248

1,000✔
249
        alloc_block<T>* blk = chain;
250
        if (HPX_LIKELY(blk != nullptr))
998✔
251
        {
998✔
252
            chain = blk->next_free;
998✔
253
            ++blk->page->allocated_blocks;
254
            return reinterpret_cast<alloc_block_header*>(blk) + 1;
255
        }
256

257
        alloc_page* pg = nullptr;
258
        alloc_block<T>* new_chain = nullptr;
259

260
        {
261
            hpx::unlock_guard<std::unique_lock<mutex_type>> ul(lk);
262

2✔
263
            // allocate new page
2✔
264
            pg = reinterpret_cast<alloc_page*>(std::malloc(sizeof(alloc_page)));
265
            if (nullptr == pg)
266
            {
267
                return nullptr;
268
            }
269

270
            hpx::construct_at(pg, alloc_block<T>::allocation_size);
271

272
            // FIXME: move block construction into alloc_page constructor
273
            blk = new_chain = (*pg->template get_block<T>())[1];
274
            hpx::construct_at(blk, pg);
275

276
            std::size_t blocks =
277
                (alloc_page::page_size / alloc_block<T>::allocation_size) - 2;
278

279
            do
280
            {
281
                alloc_block<T>* next = (*blk)[1];
282
                hpx::construct_at(next, pg);
1,018✔
283

284
                blk->next_free = next;
1,018✔
285
                blk = next;
286
            } while (--blocks != 0);
287

288
            blk->next_free = nullptr;
289

290
            blk = pg->template get_block<T>();
291
            hpx::construct_at(blk, pg);
292

293
            blk->next_free = nullptr;
294
        }
2✔
295

2✔
296
        pg->next = pages;
2✔
297
        pages = pg;
298
        chain = new_chain;
2✔
299

300
        ++blk->page->allocated_blocks;
2✔
301

302
        return reinterpret_cast<alloc_block_header*>(blk) + 1;
303
    }
304

1,000✔
305
    template <typename T>
306
    void free_list_allocator<T>::free(void* addr)
1,000✔
307
    {
308
        if (addr == nullptr)
×
309
        {
310
            return;    // ignore nullptr arguments
311
        }
1,000✔
312

313
        std::unique_lock<mutex_type> lk(mtx);
1,000✔
314

315
        auto* blk = static_cast<alloc_block<T>*>(
316
            reinterpret_cast<alloc_block_header*>(addr) - 1);
1,000✔
317

318
        --blk->page->allocated_blocks;
1,000✔
319

1,000✔
320
        blk->next_free = chain;
321
        chain = blk;
322
    }
323
}    // namespace allocator
324

325
///////////////////////////////////////////////////////////////////////////////
326
// define component type
327
struct hello_world_server : hpx::components::component_base<hello_world_server>
328
{
1,000✔
329
    hello_world_server(std::size_t cnt = 0)
330
      : count_(cnt)
331
    {
332
    }
2,000✔
333

334
    void print(std::size_t pagenum, std::size_t item) const
2,000✔
335
    {
336
        if (pagenum != std::size_t(-1))
1,000✔
337
        {
1,000✔
338
            hpx::cout << "hello world from page: " << pagenum
339
                      << ", item: " << item << ", number: " << count_ << "\n";
340
        }
341
        else
1,000✔
342
        {
1,000✔
343
            hpx::cout << "hello world from item: " << item
344
                      << ", number: " << count_ << "\n";
2,000✔
345
        }
346
    }
347

348
    HPX_DEFINE_COMPONENT_ACTION(hello_world_server, print, print_action)
349

350
    std::size_t count_;
351
};
352

353
// special heap to use for placing components in free_list_heap
354
template <typename Component>
355
struct free_list_component_heap
356
{
1,000✔
357
    // alloc and free have to be exposed from a component heap
358
    static void* alloc(std::size_t)
359
    {
1,000✔
360
        return allocator::free_list_allocator<Component>::get_allocator()
361
            .alloc();
362
    }
1,000✔
363

364
    static void free(void* p, std::size_t)
1,000✔
365
    {
1,000✔
366
        return allocator::free_list_allocator<Component>::get_allocator().free(
367
            p);
368
    }
369

1✔
370
    // this is an additional function needed just for this example
371
    static allocator::alloc_page const* get_first_page()
372
    {
1✔
373
        return allocator::free_list_allocator<Component>::get_allocator()
374
            .first_page();
375
    }
376
};
377

378
///////////////////////////////////////////////////////////////////////////////
379
// associate heap with component above
380
namespace hpx { namespace traits {
381
    template <>
382
    struct component_heap_type<hello_world_server>
383
    {
384
        using type = free_list_component_heap<hello_world_server>;
385
    };
386
}}    // namespace hpx::traits
387

388
// the component macros must come after the component_heap_type specialization
3,002✔
389
using server_type = hpx::components::component<hello_world_server>;
390
HPX_REGISTER_COMPONENT(server_type, hello_world_server)
391

392
using print_action = hello_world_server::print_action;
×
393
HPX_REGISTER_ACTION_DECLARATION(print_action)
394
HPX_REGISTER_ACTION(print_action)
395

1✔
396
///////////////////////////////////////////////////////////////////////////////
397
int main()
398
{
1✔
399
    // create 100 new component instances
1✔
400
    std::vector<hpx::id_type> ids;
401
    ids.reserve(1000);
1,001✔
402

403
    for (std::size_t i = 0; i != 1000; ++i)
1,000✔
404
    {
405
        ids.push_back(hpx::local_new<hello_world_server>(hpx::launch::sync, i));
406
    }
407

408
    // Just for demonstration purposes we now access the components directly
409
    // without having to go through AGAS to resolve their addresses. This
410
    // obviously relies on internal knowledge of the used heap.
411

412
    // Extract base pointer to the array (pages) managed by the heap above.
413
    free_list_component_heap<hello_world_server>& heap =
414
        hpx::components::component_heap<server_type>();
415

3✔
416
    std::size_t pagenum = 0;
2✔
417
    for (auto* page = heap.get_first_page(); page != nullptr;
418
        page = page->next, ++pagenum)
2✔
419
    {
1,002✔
420
        auto blocks = page->allocated_blocks;
421
        for (std::size_t i = 0; i != blocks; ++i)
1,000✔
422
        {
423
            allocator::get<hello_world_server>(page, i).print(pagenum, i);
424
        }
425
    }
426

427
    // now do the same but using AGAS
1,001✔
428
    std::size_t i = 0;
429
    for (auto const& id : ids)
2,000✔
430
    {
431
        print_action()(id, std::size_t(-1), i++);
432
    }
433

1✔
434
    return 0;
435
}
436

437
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc