Skip to content

Commit de81ae8

Browse files
committed
Merge branch 'main' into autobuild
2 parents c7151ee + 8bafccb commit de81ae8

File tree

131 files changed

+20629
-1358
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+20629
-1358
lines changed

CMakeLists.txt

Lines changed: 298 additions & 47 deletions
Large diffs are not rendered by default.

Examples/CInteropExample.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include <stdio.h>
2+
#include <inttypes.h>
3+
#include <Core/entropy_c_api.h>
4+
#include <Logging/CLogger.h>
5+
6+
// Dummy owner and vtable callbacks for demonstration
7+
static int dummy_validate(const void* owner, uint32_t index, uint32_t generation) {
8+
// No backing store; nothing is valid in this minimal example
9+
(void)owner; (void)index; (void)generation;
10+
return 0;
11+
}
12+
static EntropyObjectRef* dummy_resolve(const void* owner, uint32_t index, uint32_t generation) {
13+
// No backing store; cannot resolve
14+
(void)owner; (void)index; (void)generation;
15+
return NULL;
16+
}
17+
18+
int main(void) {
19+
uint32_t maj=0, min=0, pat=0, abi=0;
20+
entropy_get_version(&maj, &min, &pat, &abi);
21+
ENTROPY_LOG_INFO_F("EntropyCore C API version: %u.%u.%u (ABI %u)", maj, min, pat, abi);
22+
23+
// Register a dummy owner vtable (for demonstration of handle flow)
24+
const void* dummy_owner = (const void*)0xDEADBEEF;
25+
entropy_register_owner_vtable(dummy_owner, dummy_resolve, dummy_validate);
26+
27+
// Construct a handle value (no real object behind it in this example)
28+
EntropyHandle h = { dummy_owner, 42u, 7u, 0u };
29+
30+
// Basic handle operations
31+
ENTROPY_LOG_INFO_F("Handle valid? %d", (int)entropy_handle_is_valid(h));
32+
EntropyHandle h2 = h;
33+
ENTROPY_LOG_INFO_F("Handles equal? %d", (int)entropy_handle_equals(h, h2));
34+
ENTROPY_LOG_INFO_F("Type matches (expected 0)? %d", (int)entropy_handle_type_matches(h, 0));
35+
36+
// Attempt to resolve (will be NULL in this minimal example)
37+
EntropyObjectRef* obj = entropy_resolve_handle(h);
38+
ENTROPY_LOG_INFO_F("Resolved object: %p (expected NULL)", (void*)obj);
39+
if (obj) {
40+
// If your application registered a real owner, you would get a retained object here
41+
entropy_object_release(obj);
42+
}
43+
44+
// Allocation helpers
45+
char* buf = (char*)entropy_alloc(32);
46+
if (buf) {
47+
for (int i=0;i<31;i++) buf[i] = (i%10)+'0';
48+
buf[31] = '\0';
49+
ENTROPY_LOG_INFO_F("Allocated buffer: %s", buf);
50+
entropy_free(buf);
51+
}
52+
53+
return 0;
54+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// This file intentionally left with minimal contents to ensure the example links
2+
// against the C++ runtime on MSVC when the main source is C.
3+
extern "C" void entropy_c_example_link_shim() {}

Examples/ConcurrencyAPIExample.c

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright (c) 2025 Jonathan "Geenz" Goodman
7+
* This file is part of the Entropy Core project.
8+
*/
9+
10+
/**
11+
* @file ConcurrencyAPIExample.c
12+
* @brief Example demonstrating the C API for EntropyCore concurrency primitives
13+
*
14+
* This example shows how to use the C API to create work groups, submit tasks,
15+
* and execute them using a work service (thread pool), along with the C logging API.
16+
*/
17+
18+
#include <entropy/entropy_concurrency_types.h>
19+
#include <entropy/entropy_work_contract_group.h>
20+
#include <entropy/entropy_work_contract_handle.h>
21+
#include <entropy/entropy_work_service.h>
22+
#include <Logging/CLogger.h>
23+
#include <stdio.h>
24+
#include <stdlib.h>
25+
#include <unistd.h>
26+
27+
// Example work context
28+
typedef struct {
29+
int task_id;
30+
const char* message;
31+
} TaskContext;
32+
33+
// Re-entrant work context (task spawns children)
34+
typedef struct {
35+
entropy_WorkContractGroup group; // Need group reference to schedule from within
36+
int depth; // Recursion depth
37+
int max_depth; // Maximum depth
38+
int task_id; // Task identifier
39+
} ReentrantContext;
40+
41+
// Example work callback
42+
void example_work(void* user_data) {
43+
TaskContext* ctx = (TaskContext*)user_data;
44+
ENTROPY_LOG_INFO_CAT_F("WorkerThread",
45+
"Task %d: %s (executing on worker thread)",
46+
ctx->task_id, ctx->message);
47+
48+
// Simulate some work
49+
usleep(10000); // 10ms
50+
51+
ENTROPY_LOG_DEBUG_CAT_F("WorkerThread",
52+
"Task %d completed", ctx->task_id);
53+
}
54+
55+
// Main thread work callback
56+
void main_thread_work(void* user_data) {
57+
TaskContext* ctx = (TaskContext*)user_data;
58+
ENTROPY_LOG_INFO_CAT_F("MainThread",
59+
"Task %d: %s (executing on MAIN THREAD)",
60+
ctx->task_id, ctx->message);
61+
}
62+
63+
// Re-entrant work callback (spawns child tasks)
64+
void reentrant_work(void* user_data) {
65+
ReentrantContext* ctx = (ReentrantContext*)user_data;
66+
67+
ENTROPY_LOG_INFO_CAT_F("Reentrant",
68+
"Task %d at depth %d (max: %d)",
69+
ctx->task_id, ctx->depth, ctx->max_depth);
70+
71+
// If we haven't reached max depth, spawn two child tasks
72+
if (ctx->depth < ctx->max_depth) {
73+
EntropyStatus status = ENTROPY_OK;
74+
75+
// Allocate contexts for child tasks (will be freed by children or at end)
76+
ReentrantContext* left_child = malloc(sizeof(ReentrantContext));
77+
ReentrantContext* right_child = malloc(sizeof(ReentrantContext));
78+
79+
if (left_child && right_child) {
80+
// Setup left child
81+
left_child->group = ctx->group;
82+
left_child->depth = ctx->depth + 1;
83+
left_child->max_depth = ctx->max_depth;
84+
left_child->task_id = ctx->task_id * 2;
85+
86+
// Setup right child
87+
right_child->group = ctx->group;
88+
right_child->depth = ctx->depth + 1;
89+
right_child->max_depth = ctx->max_depth;
90+
right_child->task_id = ctx->task_id * 2 + 1;
91+
92+
ENTROPY_LOG_DEBUG_CAT_F("Reentrant",
93+
"Task %d spawning children %d and %d",
94+
ctx->task_id, left_child->task_id, right_child->task_id);
95+
96+
// RE-ENTRANT SCHEDULING: Create and schedule child tasks from within this task
97+
entropy_WorkContractHandle left = entropy_work_contract_group_create_contract(
98+
ctx->group, reentrant_work, left_child, ENTROPY_EXEC_ANY_THREAD, &status
99+
);
100+
101+
if (status == ENTROPY_OK) {
102+
entropy_work_contract_schedule(left, &status);
103+
}
104+
105+
entropy_WorkContractHandle right = entropy_work_contract_group_create_contract(
106+
ctx->group, reentrant_work, right_child, ENTROPY_EXEC_ANY_THREAD, &status
107+
);
108+
109+
if (status == ENTROPY_OK) {
110+
entropy_work_contract_schedule(right, &status);
111+
}
112+
}
113+
} else {
114+
ENTROPY_LOG_DEBUG_CAT_F("Reentrant",
115+
"Task %d reached max depth, completing as leaf",
116+
ctx->task_id);
117+
}
118+
119+
// Free our own context (we're done with it)
120+
free(ctx);
121+
}
122+
123+
int main(void) {
124+
EntropyStatus status = ENTROPY_OK;
125+
126+
ENTROPY_LOG_INFO_CAT_F("Example", "=== EntropyCore C API Example ===");
127+
ENTROPY_LOG_INFO_CAT_F("Example", "Demonstrating C API for concurrency + logging");
128+
129+
// 1. Create a work contract group
130+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 1: Creating work contract group (capacity: 1024)...");
131+
entropy_WorkContractGroup group = entropy_work_contract_group_create(
132+
1024, "ExampleGroup", &status
133+
);
134+
if (status != ENTROPY_OK) {
135+
ENTROPY_LOG_ERROR_CAT_F("Example",
136+
"Failed to create group: %s", entropy_status_to_string(status));
137+
return 1;
138+
}
139+
ENTROPY_LOG_INFO_CAT_F("Example", "Group created successfully");
140+
141+
// 2. Create a work service (thread pool)
142+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 2: Creating work service (4 threads)...");
143+
EntropyWorkServiceConfig config;
144+
entropy_work_service_config_init(&config);
145+
config.thread_count = 4;
146+
147+
entropy_WorkService service = entropy_work_service_create(&config, &status);
148+
if (status != ENTROPY_OK) {
149+
ENTROPY_LOG_ERROR_CAT_F("Example",
150+
"Failed to create service: %s", entropy_status_to_string(status));
151+
entropy_work_contract_group_destroy(group);
152+
return 1;
153+
}
154+
ENTROPY_LOG_INFO_CAT_F("Example", "Service created with %zu threads",
155+
entropy_work_service_get_thread_count(service));
156+
157+
// 3. Register the group with the service
158+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 3: Registering group with service...");
159+
entropy_work_service_add_group(service, group, &status);
160+
if (status != ENTROPY_OK) {
161+
ENTROPY_LOG_ERROR_CAT_F("Example",
162+
"Failed to add group: %s", entropy_status_to_string(status));
163+
entropy_work_service_destroy(service);
164+
entropy_work_contract_group_destroy(group);
165+
return 1;
166+
}
167+
ENTROPY_LOG_INFO_CAT_F("Example", "Group registered");
168+
169+
// 4. Start the service
170+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 4: Starting work service...");
171+
entropy_work_service_start(service, &status);
172+
if (status != ENTROPY_OK) {
173+
ENTROPY_LOG_ERROR_CAT_F("Example",
174+
"Failed to start service: %s", entropy_status_to_string(status));
175+
entropy_work_service_destroy(service);
176+
entropy_work_contract_group_destroy(group);
177+
return 1;
178+
}
179+
ENTROPY_LOG_INFO_CAT_F("Example", "Service started");
180+
181+
// 5. Create and schedule background work
182+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 5: Creating and scheduling 10 background tasks...");
183+
TaskContext* contexts = malloc(sizeof(TaskContext) * 10);
184+
entropy_WorkContractHandle* handles = malloc(sizeof(entropy_WorkContractHandle) * 10);
185+
186+
for (int i = 0; i < 10; i++) {
187+
contexts[i].task_id = i;
188+
contexts[i].message = "Processing background data";
189+
190+
handles[i] = entropy_work_contract_group_create_contract(
191+
group, example_work, &contexts[i], ENTROPY_EXEC_ANY_THREAD, &status
192+
);
193+
194+
if (status != ENTROPY_OK) {
195+
ENTROPY_LOG_WARNING_CAT_F("Example",
196+
"Failed to create contract %d: %s", i, entropy_status_to_string(status));
197+
continue;
198+
}
199+
200+
// Schedule the contract
201+
EntropyScheduleResult result = entropy_work_contract_schedule(handles[i], &status);
202+
if (result == ENTROPY_SCHEDULE_SCHEDULED) {
203+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Task %d scheduled", i);
204+
}
205+
}
206+
207+
// 6. Create main thread work
208+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 6: Creating main thread tasks...");
209+
TaskContext main_contexts[3];
210+
entropy_WorkContractHandle main_handles[3];
211+
212+
for (int i = 0; i < 3; i++) {
213+
main_contexts[i].task_id = 100 + i;
214+
main_contexts[i].message = "Updating UI";
215+
216+
main_handles[i] = entropy_work_contract_group_create_contract(
217+
group, main_thread_work, &main_contexts[i],
218+
ENTROPY_EXEC_MAIN_THREAD, &status
219+
);
220+
221+
if (status == ENTROPY_OK) {
222+
entropy_work_contract_schedule(main_handles[i], &status);
223+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Main thread task %d scheduled", 100 + i);
224+
}
225+
}
226+
227+
// 7. Execute main thread work
228+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 7: Executing main thread work...");
229+
if (entropy_work_service_has_main_thread_work(service)) {
230+
EntropyMainThreadWorkResult result;
231+
entropy_work_service_execute_main_thread_work(service, 0, &result, &status);
232+
ENTROPY_LOG_INFO_CAT_F("Example",
233+
"Executed %zu contracts from %zu groups",
234+
result.contracts_executed, result.groups_with_work);
235+
}
236+
237+
// Wait for initial work to complete before starting re-entrant example
238+
entropy_work_contract_group_wait(group, &status);
239+
ENTROPY_LOG_INFO_CAT_F("Example", "Initial work completed");
240+
241+
// 8. Re-entrant work contracts (tasks spawning tasks)
242+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 8: Re-entrant work contracts (recursive task spawning)...");
243+
ENTROPY_LOG_INFO_CAT_F("Example", "Creating binary tree of tasks (depth 3 = 15 total tasks)");
244+
245+
// Create root task that will spawn children recursively
246+
ReentrantContext* root = malloc(sizeof(ReentrantContext));
247+
if (root) {
248+
root->group = group;
249+
root->depth = 0;
250+
root->max_depth = 3; // Creates 2^3 - 1 = 7 tasks at depth 3, 15 total
251+
root->task_id = 1; // Root is task 1
252+
253+
entropy_WorkContractHandle root_handle = entropy_work_contract_group_create_contract(
254+
group, reentrant_work, root, ENTROPY_EXEC_ANY_THREAD, &status
255+
);
256+
257+
if (status == ENTROPY_OK) {
258+
entropy_work_contract_schedule(root_handle, &status);
259+
ENTROPY_LOG_INFO_CAT_F("Example", "Root task scheduled - it will spawn children recursively");
260+
}
261+
}
262+
263+
// Wait for the recursive tree to complete
264+
ENTROPY_LOG_INFO_CAT_F("Example", "Waiting for re-entrant task tree to complete...");
265+
entropy_work_contract_group_wait(group, &status);
266+
ENTROPY_LOG_INFO_CAT_F("Example", "Re-entrant task tree completed");
267+
268+
// 9. Print statistics
269+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 9: Final statistics:");
270+
ENTROPY_LOG_INFO_CAT_F("Example", " Capacity: %zu",
271+
entropy_work_contract_group_capacity(group));
272+
ENTROPY_LOG_INFO_CAT_F("Example", " Active contracts: %zu",
273+
entropy_work_contract_group_active_count(group));
274+
ENTROPY_LOG_INFO_CAT_F("Example", " Scheduled contracts: %zu",
275+
entropy_work_contract_group_scheduled_count(group));
276+
ENTROPY_LOG_INFO_CAT_F("Example", " Executing contracts: %zu",
277+
entropy_work_contract_group_executing_count(group));
278+
279+
// 10. Cleanup
280+
ENTROPY_LOG_INFO_CAT_F("Example", "Step 10: Cleaning up...");
281+
entropy_work_service_stop(service, &status);
282+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Service stopped");
283+
284+
entropy_work_service_destroy(service);
285+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Service destroyed");
286+
287+
entropy_work_contract_group_destroy(group);
288+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Group destroyed");
289+
290+
free(contexts);
291+
free(handles);
292+
ENTROPY_LOG_DEBUG_CAT_F("Example", "Memory freed");
293+
294+
ENTROPY_LOG_INFO_CAT_F("Example", "=== Example completed successfully ===");
295+
return 0;
296+
}

0 commit comments

Comments
 (0)