Skip to content

Commit 2c56697

Browse files
committed
Add thread-safe IdAllocator utility and example
Introduces a generic, thread-safe IdAllocator for generating unique 32-bit IDs with type and sequence information. Adds implementation, header, and a comprehensive example demonstrating allocation, validation, decomposition, thread safety, and counter reset. Updates CMakeLists and README to include the new example and mark the project as work in progress.
1 parent 641c7e0 commit 2c56697

File tree

7 files changed

+445
-0
lines changed

7 files changed

+445
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_subdirectory(src)
88
# Add example programs subdirectory
99
add_subdirectory(examples/display_example)
1010
add_subdirectory(examples/display_c_example)
11+
add_subdirectory(examples/id_allocator_example)
1112
add_subdirectory(examples/keyboard_example)
1213
add_subdirectory(examples/menu_example)
1314
add_subdirectory(examples/menu_c_example)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
A modern cross-platform C++ library providing seamless, unified access to native system APIs across multiple platforms.
44

5+
🚧 Work in Progress: This package is currently under active development.
6+
57
## Requirements
68

79
### Build Requirements
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
3+
# Set C++ standard
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
# Create executable for ID allocator example
8+
add_executable(id_allocator_example main.cpp)
9+
10+
# Link against the nativeapi library
11+
target_link_libraries(id_allocator_example nativeapi)
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#include <algorithm>
2+
#include <chrono>
3+
#include <iostream>
4+
#include <mutex>
5+
#include <thread>
6+
#include <vector>
7+
#include "../../src/foundation/id_allocator.h"
8+
9+
using namespace nativeapi;
10+
11+
// Define some example types for demonstration
12+
struct Window {};
13+
struct Menu {};
14+
struct MenuItem {};
15+
struct TrayIcon {};
16+
struct Display {};
17+
18+
int main() {
19+
std::cout << "IdAllocator Template Example" << std::endl;
20+
std::cout << "============================" << std::endl;
21+
22+
// Example 1: Basic allocation for different object types
23+
std::cout << "\n1. Basic Allocation:" << std::endl;
24+
25+
auto window_id = IdAllocator::Allocate<Window>();
26+
auto menu_id = IdAllocator::Allocate<Menu>();
27+
auto tray_id = IdAllocator::Allocate<TrayIcon>();
28+
29+
std::cout << "Window ID: 0x" << std::hex << window_id << std::dec
30+
<< " (Type: " << IdAllocator::GetType(window_id)
31+
<< ", Sequence: " << IdAllocator::GetSequence(window_id) << ")"
32+
<< std::endl;
33+
34+
std::cout << "Menu ID: 0x" << std::hex << menu_id << std::dec
35+
<< " (Type: " << IdAllocator::GetType(menu_id)
36+
<< ", Sequence: " << IdAllocator::GetSequence(menu_id) << ")"
37+
<< std::endl;
38+
39+
std::cout << "Tray ID: 0x" << std::hex << tray_id << std::dec
40+
<< " (Type: " << IdAllocator::GetType(tray_id)
41+
<< ", Sequence: " << IdAllocator::GetSequence(tray_id) << ")"
42+
<< std::endl;
43+
44+
// Example 2: TryAllocate with error checking
45+
std::cout << "\n2. TryAllocate (safer allocation):" << std::endl;
46+
47+
auto maybe_id = IdAllocator::TryAllocate<MenuItem>();
48+
if (maybe_id != IdAllocator::kInvalidId) {
49+
std::cout << "MenuItem ID allocated successfully: 0x" << std::hex
50+
<< maybe_id << std::dec << std::endl;
51+
} else {
52+
std::cout << "MenuItem ID allocation failed" << std::endl;
53+
}
54+
55+
// Example 3: ID validation and decomposition
56+
std::cout << "\n3. ID Validation and Decomposition:" << std::endl;
57+
58+
std::cout << "Is window_id valid? "
59+
<< (IdAllocator::IsValid(window_id) ? "Yes" : "No") << std::endl;
60+
61+
auto decomposed = IdAllocator::Decompose(window_id);
62+
std::cout << "Window ID decomposed - Type: " << decomposed.first
63+
<< ", Sequence: " << decomposed.second << std::endl;
64+
65+
// Example 4: Current count query
66+
std::cout << "\n4. Current Counter Query:" << std::endl;
67+
68+
std::cout << "Current Window counter (before allocation): "
69+
<< IdAllocator::GetCurrentCount<Window>() << std::endl;
70+
71+
auto new_window_id = IdAllocator::Allocate<Window>();
72+
std::cout << "New Window ID: 0x" << std::hex << new_window_id << std::dec
73+
<< " (Sequence: " << IdAllocator::GetSequence(new_window_id) << ")"
74+
<< std::endl;
75+
76+
std::cout << "Current Window counter (after allocation): "
77+
<< IdAllocator::GetCurrentCount<Window>() << std::endl;
78+
79+
// Example 5: Multiple allocations
80+
std::cout << "\n5. Multiple Allocations:" << std::endl;
81+
82+
std::vector<IdAllocator::IdType> window_ids;
83+
for (int i = 0; i < 5; ++i) {
84+
window_ids.push_back(IdAllocator::Allocate<Window>());
85+
}
86+
87+
std::cout << "Allocated " << window_ids.size() << " Window IDs:" << std::endl;
88+
for (size_t i = 0; i < window_ids.size(); ++i) {
89+
std::cout << " ID " << (i + 1) << ": 0x" << std::hex << window_ids[i]
90+
<< std::dec
91+
<< " (Sequence: " << IdAllocator::GetSequence(window_ids[i])
92+
<< ")" << std::endl;
93+
}
94+
95+
// Example 6: Thread safety demonstration
96+
std::cout << "\n6. Thread Safety Demonstration:" << std::endl;
97+
98+
std::vector<std::thread> threads;
99+
std::vector<IdAllocator::IdType> thread_ids;
100+
std::mutex ids_mutex;
101+
102+
const int num_threads = 3;
103+
const int ids_per_thread = 10;
104+
105+
auto start_time = std::chrono::high_resolution_clock::now();
106+
107+
for (int i = 0; i < num_threads; ++i) {
108+
threads.emplace_back([&thread_ids, &ids_mutex, ids_per_thread, i]() {
109+
std::vector<IdAllocator::IdType> local_ids;
110+
111+
for (int j = 0; j < ids_per_thread; ++j) {
112+
auto id = IdAllocator::Allocate<Display>();
113+
local_ids.push_back(id);
114+
}
115+
116+
{
117+
std::lock_guard<std::mutex> lock(ids_mutex);
118+
thread_ids.insert(thread_ids.end(), local_ids.begin(), local_ids.end());
119+
}
120+
});
121+
}
122+
123+
for (auto& thread : threads) {
124+
thread.join();
125+
}
126+
127+
auto end_time = std::chrono::high_resolution_clock::now();
128+
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
129+
end_time - start_time);
130+
131+
std::cout << "Allocated " << thread_ids.size() << " Display IDs from "
132+
<< num_threads << " threads in " << duration.count()
133+
<< " microseconds" << std::endl;
134+
135+
// Verify all IDs are unique
136+
std::sort(thread_ids.begin(), thread_ids.end());
137+
auto it = std::unique(thread_ids.begin(), thread_ids.end());
138+
std::cout << "All IDs are unique: " << (it == thread_ids.end() ? "Yes" : "No")
139+
<< std::endl;
140+
141+
// Example 7: Different object types
142+
std::cout << "\n7. Different Object Types:" << std::endl;
143+
144+
auto display_id = IdAllocator::Allocate<Display>();
145+
146+
std::cout << "Display ID: 0x" << std::hex << display_id << std::dec
147+
<< " (Type: " << IdAllocator::GetType(display_id) << ")"
148+
<< std::endl;
149+
150+
// Example 8: Reset functionality (for testing)
151+
std::cout << "\n8. Reset Functionality:" << std::endl;
152+
153+
std::cout << "Menu counter before reset: "
154+
<< IdAllocator::GetCurrentCount<Menu>() << std::endl;
155+
IdAllocator::Reset<Menu>();
156+
std::cout << "Menu counter after reset: "
157+
<< IdAllocator::GetCurrentCount<Menu>() << std::endl;
158+
159+
auto new_menu_id = IdAllocator::Allocate<Menu>();
160+
std::cout << "New Menu ID after reset: 0x" << std::hex << new_menu_id
161+
<< std::dec
162+
<< " (Sequence: " << IdAllocator::GetSequence(new_menu_id) << ")"
163+
<< std::endl;
164+
165+
// Example 9: Independent types after reset
166+
std::cout << "\n9. Independent Types After Reset:" << std::endl;
167+
168+
auto window_after_reset = IdAllocator::Allocate<Window>();
169+
std::cout << "Window ID after Menu reset: 0x" << std::hex
170+
<< window_after_reset << std::dec
171+
<< " (Sequence: " << IdAllocator::GetSequence(window_after_reset)
172+
<< ")" << std::endl;
173+
std::cout << "Window counter was not affected by Menu reset" << std::endl;
174+
175+
std::cout << "\nExample completed successfully!" << std::endl;
176+
return 0;
177+
}

include/nativeapi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "../src/foundation/event.h"
99
#include "../src/foundation/event_emitter.h"
1010
#include "../src/foundation/geometry.h"
11+
#include "../src/foundation/id_allocator.h"
1112
#include "../src/keyboard_event.h"
1213
#include "../src/keyboard_monitor.h"
1314
#include "../src/menu.h"

src/foundation/id_allocator.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Implementation of IdAllocator static methods for ID querying and validation.
3+
*/
4+
5+
#include "id_allocator.h"
6+
7+
namespace nativeapi {
8+
9+
/**
10+
* Extracts the type from an ID.
11+
*/
12+
uint32_t IdAllocator::GetType(IdType id) {
13+
// Extract type value from high 8 bits (bits 31-24)
14+
// kTypeMask = 0xFF000000, kTypeShift = 24
15+
const uint32_t type_value = (id & kTypeMask) >> kTypeShift;
16+
return type_value;
17+
}
18+
19+
/**
20+
* Extracts the sequence number from an ID.
21+
*/
22+
uint32_t IdAllocator::GetSequence(IdType id) {
23+
// Extract sequence number from low 24 bits (bits 23-0)
24+
// kSequenceMask = 0x00FFFFFF
25+
return id & kSequenceMask;
26+
}
27+
28+
/**
29+
* Checks if an ID is valid.
30+
*/
31+
bool IdAllocator::IsValid(IdType id) {
32+
// Extract type value from high 8 bits
33+
const uint32_t type_value = (id & kTypeMask) >> kTypeShift;
34+
35+
// Extract sequence number from low 24 bits
36+
const uint32_t seq = id & kSequenceMask;
37+
38+
// ID is valid if:
39+
// 1. Type value is in valid range [kMinTypeValue, kMaxTypeValue] (1-10)
40+
// 2. Sequence number is non-zero (0 is reserved for kInvalidId)
41+
return IsValidType(type_value) && seq != 0u;
42+
}
43+
44+
/**
45+
* Extracts both type and sequence from an ID.
46+
*/
47+
std::pair<uint32_t, uint32_t> IdAllocator::Decompose(IdType id) {
48+
// Extract type value from high 8 bits (bits 31-24)
49+
const uint32_t type_value = (id & kTypeMask) >> kTypeShift;
50+
51+
// Extract sequence number from low 24 bits (bits 23-0)
52+
const uint32_t sequence = id & kSequenceMask;
53+
54+
// Return both components as a pair
55+
return {type_value, sequence};
56+
}
57+
58+
} // namespace nativeapi

0 commit comments

Comments
 (0)