elementdrawing 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/elementdrawing.min.js +3 -0
  3. package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
  4. package/dist/elementdrawing.min.js.map +1 -0
  5. package/dist/index.html +1 -0
  6. package/package.json +127 -0
  7. package/src/core/bridge.h +855 -0
  8. package/src/core/diff.c +900 -0
  9. package/src/core/element.c +1078 -0
  10. package/src/core/event.c +813 -0
  11. package/src/core/fiber.c +1027 -0
  12. package/src/core/hooks.c +919 -0
  13. package/src/core/renderer.c +963 -0
  14. package/src/core/scheduler.c +702 -0
  15. package/src/core/state.c +803 -0
  16. package/src/css/animations.css +779 -0
  17. package/src/css/base.css +615 -0
  18. package/src/css/components.css +1311 -0
  19. package/src/css/tailwind.css +370 -0
  20. package/src/css/themes.css +517 -0
  21. package/src/css/utilities.css +475 -0
  22. package/src/index.js +746 -0
  23. package/src/js/animation.js +655 -0
  24. package/src/js/dom.js +665 -0
  25. package/src/js/events.js +585 -0
  26. package/src/js/http.js +446 -0
  27. package/src/js/index.js +26 -0
  28. package/src/js/router.js +483 -0
  29. package/src/js/store.js +539 -0
  30. package/src/js/utils.js +593 -0
  31. package/src/js/validator.js +529 -0
  32. package/src/jsx/components/Accordion.jsx +210 -0
  33. package/src/jsx/components/Alert.jsx +169 -0
  34. package/src/jsx/components/Avatar.jsx +214 -0
  35. package/src/jsx/components/Badge.jsx +136 -0
  36. package/src/jsx/components/Breadcrumb.jsx +200 -0
  37. package/src/jsx/components/Button.jsx +188 -0
  38. package/src/jsx/components/Card.jsx +192 -0
  39. package/src/jsx/components/Carousel.jsx +278 -0
  40. package/src/jsx/components/Checkbox.jsx +215 -0
  41. package/src/jsx/components/Dialog.jsx +242 -0
  42. package/src/jsx/components/Drawer.jsx +190 -0
  43. package/src/jsx/components/Dropdown.jsx +268 -0
  44. package/src/jsx/components/Form.jsx +274 -0
  45. package/src/jsx/components/Input.jsx +285 -0
  46. package/src/jsx/components/Menu.jsx +276 -0
  47. package/src/jsx/components/Modal.jsx +274 -0
  48. package/src/jsx/components/Navbar.jsx +292 -0
  49. package/src/jsx/components/Pagination.jsx +268 -0
  50. package/src/jsx/components/Progress.jsx +252 -0
  51. package/src/jsx/components/Radio.jsx +208 -0
  52. package/src/jsx/components/Select.jsx +397 -0
  53. package/src/jsx/components/Sidebar.jsx +250 -0
  54. package/src/jsx/components/Slider.jsx +310 -0
  55. package/src/jsx/components/Spinner.jsx +198 -0
  56. package/src/jsx/components/Switch.jsx +201 -0
  57. package/src/jsx/components/Table.jsx +332 -0
  58. package/src/jsx/components/Tabs.jsx +227 -0
  59. package/src/jsx/components/Textarea.jsx +212 -0
  60. package/src/jsx/components/Toast.jsx +270 -0
  61. package/src/jsx/components/Tooltip.jsx +178 -0
  62. package/src/jsx/components/Typography.jsx +299 -0
  63. package/src/jsx/components/index.jsx +70 -0
  64. package/src/jsx/core/element.js +3 -0
  65. package/src/jsx/hooks/index.js +356 -0
  66. package/src/jsx/hooks/useCallback.js +472 -0
  67. package/src/jsx/hooks/useContext.js +586 -0
  68. package/src/jsx/hooks/useEffect.js +704 -0
  69. package/src/jsx/hooks/useLayoutEffect.js +508 -0
  70. package/src/jsx/hooks/useMemo.js +689 -0
  71. package/src/jsx/hooks/useReducer.js +729 -0
  72. package/src/jsx/hooks/useRef.js +542 -0
  73. package/src/jsx/hooks/useState.js +854 -0
  74. package/src/jsx/runtime/commit.js +903 -0
  75. package/src/jsx/runtime/createElement.js +860 -0
  76. package/src/jsx/runtime/index.js +356 -0
  77. package/src/jsx/runtime/reconcile.js +687 -0
  78. package/src/jsx/runtime/render.js +914 -0
@@ -0,0 +1,702 @@
1
+ /*
2
+ * scheduler.c - ElementDrawing Core: Cooperative Scheduler
3
+ *
4
+ * Implements a priority-based cooperative scheduler for the
5
+ * ElementDrawing engine. Supports multiple priority levels,
6
+ * frame budget tracking, yielding for main thread responsiveness,
7
+ * and task lifecycle management.
8
+ */
9
+
10
+ #include "bridge.h"
11
+
12
+ /* ================================================================
13
+ * Internal Constants
14
+ * ================================================================ */
15
+
16
+ #define ED_SCHEDULER_IMMEDIATE_INDEX 1
17
+ #define ED_SCHEDULER_USER_BLOCK_INDEX 2
18
+ #define ED_SCHEDULER_NORMAL_INDEX 3
19
+ #define ED_SCHEDULER_LOW_INDEX 4
20
+ #define ED_SCHEDULER_IDLE_INDEX 5
21
+
22
+ /* ================================================================
23
+ * Scheduler Task Pool
24
+ * ================================================================ */
25
+
26
+ typedef struct EDSchedulerTaskPool {
27
+ EDSchedulerTask* free_list;
28
+ uint32_t allocated;
29
+ uint32_t returned;
30
+ } EDSchedulerTaskPool;
31
+
32
+ static EDSchedulerTaskPool g_task_pool = {0};
33
+
34
+ static EDSchedulerTask* ed_task_alloc(void)
35
+ {
36
+ if (g_task_pool.free_list) {
37
+ EDSchedulerTask* task = g_task_pool.free_list;
38
+ g_task_pool.free_list = task->next;
39
+ g_task_pool.free_list = NULL;
40
+ memset(task, 0, sizeof(EDSchedulerTask));
41
+ task->is_running = false;
42
+ task->is_cancelled = false;
43
+ g_task_pool.allocated++;
44
+ return task;
45
+ }
46
+
47
+ EDSchedulerTask* task = (EDSchedulerTask*)calloc(1, sizeof(EDSchedulerTask));
48
+ if (task) g_task_pool.allocated++;
49
+ return task;
50
+ }
51
+
52
+ static void ed_task_free(EDSchedulerTask* task)
53
+ {
54
+ if (!task) return;
55
+ task->next = g_task_pool.free_list;
56
+ g_task_pool.free_list = task;
57
+ g_task_pool.returned++;
58
+ }
59
+
60
+ /* ================================================================
61
+ * Expiration Time Computation
62
+ * ================================================================ */
63
+
64
+ static uint64_t ed_scheduler_compute_expiration(EDSchedulerPriority priority)
65
+ {
66
+ uint64_t now = ed_get_timestamp_ms();
67
+
68
+ switch (priority) {
69
+ case ED_PRIORITY_IMMEDIATE:
70
+ return now; /* Must execute immediately */
71
+ case ED_PRIORITY_USER_BLOCKING:
72
+ return now + 100; /* 100ms budget */
73
+ case ED_PRIORITY_NORMAL:
74
+ return now + ED_FIBER_WORK_EXPIRATION; /* 5s budget */
75
+ case ED_PRIORITY_LOW:
76
+ return now + 10000; /* 10s budget */
77
+ case ED_PRIORITY_IDLE:
78
+ return UINT64_MAX; /* No deadline */
79
+ default:
80
+ return now + ED_FIBER_WORK_EXPIRATION;
81
+ }
82
+ }
83
+
84
+ /* ================================================================
85
+ * Priority Queue Index Mapping
86
+ * ================================================================ */
87
+
88
+ static uint32_t ed_priority_to_index(EDSchedulerPriority priority)
89
+ {
90
+ if (priority >= ED_SCHEDULER_MIN_PRIORITY && priority <= ED_SCHEDULER_MAX_PRIORITY) {
91
+ return (uint32_t)priority;
92
+ }
93
+ return ED_SCHEDULER_NORMAL_INDEX;
94
+ }
95
+
96
+ /* ================================================================
97
+ * Scheduler Creation and Destruction
98
+ * ================================================================ */
99
+
100
+ EDScheduler* ed_scheduler_create(void)
101
+ {
102
+ EDScheduler* scheduler = (EDScheduler*)calloc(1, sizeof(EDScheduler));
103
+ if (!scheduler) return NULL;
104
+
105
+ /* Initialize priority queues (indices 1-5) */
106
+ for (int i = 0; i < 6; i++) {
107
+ scheduler->queues[i] = NULL;
108
+ scheduler->queue_sizes[i] = 0;
109
+ }
110
+
111
+ scheduler->total_tasks = 0;
112
+ scheduler->completed_tasks = 0;
113
+ scheduler->cancelled_tasks = 0;
114
+ scheduler->frame_start = 0;
115
+ scheduler->frame_deadline = 0;
116
+ scheduler->is_working = false;
117
+ scheduler->should_yield = false;
118
+ scheduler->current_priority = ED_PRIORITY_NORMAL;
119
+ scheduler->current_task = NULL;
120
+ scheduler->next_task_id = 1;
121
+
122
+ ed_log_debug("Scheduler created");
123
+ return scheduler;
124
+ }
125
+
126
+ void ed_scheduler_destroy(EDScheduler* scheduler)
127
+ {
128
+ if (!scheduler) return;
129
+
130
+ /* Cancel and free all tasks in all priority queues */
131
+ for (int i = 0; i < 6; i++) {
132
+ EDSchedulerTask* task = scheduler->queues[i];
133
+ while (task) {
134
+ EDSchedulerTask* next = task->next;
135
+ task->is_cancelled = true;
136
+ ed_task_free(task);
137
+ task = next;
138
+ }
139
+ scheduler->queues[i] = NULL;
140
+ }
141
+
142
+ ed_log_debug("Scheduler destroyed (completed=%u, cancelled=%u)",
143
+ scheduler->completed_tasks, scheduler->cancelled_tasks);
144
+
145
+ free(scheduler);
146
+ }
147
+
148
+ /* ================================================================
149
+ * Task Scheduling
150
+ * ================================================================ */
151
+
152
+ uint32_t ed_scheduler_schedule(EDScheduler* scheduler, EDSchedulerPriority priority,
153
+ EDSchedulerCallback callback, void* data)
154
+ {
155
+ if (!scheduler || !callback) return 0;
156
+
157
+ uint32_t idx = ed_priority_to_index(priority);
158
+
159
+ /* Check queue capacity */
160
+ if (scheduler->queue_sizes[idx] >= ED_SCHEDULER_MAX_QUEUE_SIZE) {
161
+ ed_log_warn("Scheduler queue %u full (%u tasks), dropping task",
162
+ idx, scheduler->queue_sizes[idx]);
163
+ return 0;
164
+ }
165
+
166
+ /* Allocate task */
167
+ EDSchedulerTask* task = ed_task_alloc();
168
+ if (!task) return 0;
169
+
170
+ task->id = scheduler->next_task_id++;
171
+ task->priority = priority;
172
+ task->callback = callback;
173
+ task->data = data;
174
+ task->expiration_time = ed_scheduler_compute_expiration(priority);
175
+ task->start_time = 0;
176
+ task->is_running = false;
177
+ task->is_cancelled = false;
178
+ task->is_recurring = false;
179
+ task->interval_ms = 0;
180
+
181
+ /* Insert into priority queue (sorted by expiration time) */
182
+ EDSchedulerTask** prev = &scheduler->queues[idx];
183
+ EDSchedulerTask* current = scheduler->queues[idx];
184
+
185
+ while (current && current->expiration_time <= task->expiration_time) {
186
+ prev = &current->next;
187
+ current = current->next;
188
+ }
189
+
190
+ task->next = current;
191
+ *prev = task;
192
+
193
+ scheduler->queue_sizes[idx]++;
194
+ scheduler->total_tasks++;
195
+
196
+ ed_log_debug("Scheduled task %u (priority=%u, expiration=%llu)",
197
+ task->id, priority,
198
+ (unsigned long long)task->expiration_time);
199
+
200
+ return task->id;
201
+ }
202
+
203
+ /* ================================================================
204
+ * Task Cancellation
205
+ * ================================================================ */
206
+
207
+ bool ed_scheduler_cancel(EDScheduler* scheduler, uint32_t task_id)
208
+ {
209
+ if (!scheduler || task_id == 0) return false;
210
+
211
+ /* Search all priority queues for the task */
212
+ for (int i = 0; i < 6; i++) {
213
+ EDSchedulerTask** prev = &scheduler->queues[i];
214
+ EDSchedulerTask* task = scheduler->queues[i];
215
+
216
+ while (task) {
217
+ if (task->id == task_id) {
218
+ task->is_cancelled = true;
219
+
220
+ /* Remove from queue */
221
+ *prev = task->next;
222
+ scheduler->queue_sizes[i]--;
223
+ scheduler->cancelled_tasks++;
224
+
225
+ /* If currently running, just mark cancelled */
226
+ if (task->is_running) {
227
+ ed_log_debug("Task %u marked for cancellation (running)", task_id);
228
+ return true;
229
+ }
230
+
231
+ ed_task_free(task);
232
+ ed_log_debug("Task %u cancelled", task_id);
233
+ return true;
234
+ }
235
+ prev = &task->next;
236
+ task = task->next;
237
+ }
238
+ }
239
+
240
+ ed_log_warn("Task %u not found for cancellation", task_id);
241
+ return false;
242
+ }
243
+
244
+ /* ================================================================
245
+ * Work Execution
246
+ * ================================================================ */
247
+
248
+ static EDSchedulerTask* ed_scheduler_pick_next_task(EDScheduler* scheduler)
249
+ {
250
+ if (!scheduler) return NULL;
251
+
252
+ /* Pick the highest priority task with the earliest deadline */
253
+ for (int i = 1; i < 6; i++) {
254
+ if (scheduler->queues[i]) {
255
+ EDSchedulerTask* task = scheduler->queues[i];
256
+ scheduler->queues[i] = task->next;
257
+ scheduler->queue_sizes[i]--;
258
+ return task;
259
+ }
260
+ }
261
+
262
+ return NULL;
263
+ }
264
+
265
+ void ed_scheduler_work(EDScheduler* scheduler)
266
+ {
267
+ if (!scheduler) return;
268
+
269
+ scheduler->is_working = true;
270
+ scheduler->frame_start = ed_get_timestamp_ms();
271
+ scheduler->frame_deadline = scheduler->frame_start + ED_SCHEDULER_FRAME_BUDGET;
272
+ scheduler->should_yield = false;
273
+
274
+ uint32_t tasks_executed = 0;
275
+
276
+ while (!scheduler->should_yield) {
277
+ EDSchedulerTask* task = ed_scheduler_pick_next_task(scheduler);
278
+ if (!task) break; /* No more tasks */
279
+
280
+ /* Skip cancelled tasks */
281
+ if (task->is_cancelled) {
282
+ ed_task_free(task);
283
+ continue;
284
+ }
285
+
286
+ /* Execute the task */
287
+ scheduler->current_task = task;
288
+ scheduler->current_priority = task->priority;
289
+ task->is_running = true;
290
+ task->start_time = ed_get_timestamp_ms();
291
+
292
+ ed_log_debug("Executing task %u (priority=%u)",
293
+ task->id, task->priority);
294
+
295
+ /* Call the task callback */
296
+ if (task->callback) {
297
+ task->callback(task->data);
298
+ }
299
+
300
+ task->is_running = false;
301
+ scheduler->current_task = NULL;
302
+ scheduler->completed_tasks++;
303
+ tasks_executed++;
304
+
305
+ /* Handle recurring tasks */
306
+ if (task->is_recurring && !task->is_cancelled) {
307
+ task->is_running = false;
308
+ task->start_time = 0;
309
+ task->expiration_time = ed_get_timestamp_ms() + task->interval_ms;
310
+ task->next = NULL;
311
+
312
+ /* Re-insert into queue */
313
+ uint32_t idx = ed_priority_to_index(task->priority);
314
+ EDSchedulerTask** prev = &scheduler->queues[idx];
315
+ EDSchedulerTask* current = scheduler->queues[idx];
316
+ while (current && current->expiration_time <= task->expiration_time) {
317
+ prev = &current->next;
318
+ current = current->next;
319
+ }
320
+ task->next = current;
321
+ *prev = task;
322
+ scheduler->queue_sizes[idx]++;
323
+ } else {
324
+ ed_task_free(task);
325
+ }
326
+
327
+ /* Check if we should yield */
328
+ if (ed_scheduler_should_yield(scheduler)) {
329
+ scheduler->should_yield = true;
330
+ ed_log_debug("Scheduler yielding after %u tasks", tasks_executed);
331
+ }
332
+
333
+ /* Safety: limit tasks per frame */
334
+ if (tasks_executed >= ED_SCHEDULER_MAX_CALLBACKS) {
335
+ ed_log_warn("Scheduler hit max callbacks per frame (%u)",
336
+ tasks_executed);
337
+ break;
338
+ }
339
+ }
340
+
341
+ scheduler->is_working = false;
342
+ scheduler->current_priority = ED_PRIORITY_NORMAL;
343
+ }
344
+
345
+ /* ================================================================
346
+ * Yield Detection
347
+ * ================================================================ */
348
+
349
+ bool ed_scheduler_should_yield(EDScheduler* scheduler)
350
+ {
351
+ if (!scheduler) return false;
352
+
353
+ /* Check frame deadline */
354
+ uint64_t now = ed_get_timestamp_ms();
355
+ if (now >= scheduler->frame_deadline) {
356
+ return true;
357
+ }
358
+
359
+ /* Check if higher priority work arrived */
360
+ if (scheduler->current_priority > ED_PRIORITY_IMMEDIATE) {
361
+ for (int i = 1; i < (int)scheduler->current_priority; i++) {
362
+ if (scheduler->queue_sizes[i] > 0) {
363
+ return true; /* Higher priority work pending */
364
+ }
365
+ }
366
+ }
367
+
368
+ return false;
369
+ }
370
+
371
+ /* ================================================================
372
+ * Flush Operations
373
+ * ================================================================ */
374
+
375
+ void ed_scheduler_flush_all(EDScheduler* scheduler)
376
+ {
377
+ if (!scheduler) return;
378
+
379
+ uint64_t saved_deadline = scheduler->frame_deadline;
380
+ scheduler->frame_deadline = UINT64_MAX; /* Effectively infinite deadline */
381
+ scheduler->should_yield = false;
382
+
383
+ /* Execute all remaining tasks */
384
+ uint32_t total = 0;
385
+ while (true) {
386
+ EDSchedulerTask* task = ed_scheduler_pick_next_task(scheduler);
387
+ if (!task) break;
388
+
389
+ if (task->is_cancelled) {
390
+ ed_task_free(task);
391
+ continue;
392
+ }
393
+
394
+ scheduler->current_task = task;
395
+ task->is_running = true;
396
+ task->start_time = ed_get_timestamp_ms();
397
+
398
+ if (task->callback) {
399
+ task->callback(task->data);
400
+ }
401
+
402
+ task->is_running = false;
403
+ scheduler->completed_tasks++;
404
+ total++;
405
+ ed_task_free(task);
406
+ }
407
+
408
+ scheduler->frame_deadline = saved_deadline;
409
+ scheduler->should_yield = false;
410
+ scheduler->current_task = NULL;
411
+
412
+ ed_log_debug("Flushed all scheduler tasks (%u executed)", total);
413
+ }
414
+
415
+ void ed_scheduler_advance(EDScheduler* scheduler)
416
+ {
417
+ if (!scheduler) return;
418
+
419
+ /* Advance the frame: update frame timing */
420
+ scheduler->frame_start = ed_get_timestamp_ms();
421
+ scheduler->frame_deadline = scheduler->frame_start + ED_SCHEDULER_FRAME_BUDGET;
422
+ scheduler->should_yield = false;
423
+ }
424
+
425
+ /* ================================================================
426
+ * Priority and Status Queries
427
+ * ================================================================ */
428
+
429
+ EDSchedulerPriority ed_scheduler_get_current_priority(EDScheduler* scheduler)
430
+ {
431
+ return scheduler ? scheduler->current_priority : ED_PRIORITY_NORMAL;
432
+ }
433
+
434
+ void ed_scheduler_start_profiling(EDScheduler* scheduler)
435
+ {
436
+ if (!scheduler) return;
437
+ /* In a real implementation, enable detailed timing collection */
438
+ ed_log_debug("Scheduler profiling started");
439
+ }
440
+
441
+ void ed_scheduler_stop_profiling(EDScheduler* scheduler)
442
+ {
443
+ if (!scheduler) return;
444
+ ed_log_debug("Scheduler profiling stopped");
445
+ }
446
+
447
+ uint64_t ed_scheduler_get_frame_deadline(EDScheduler* scheduler)
448
+ {
449
+ return scheduler ? scheduler->frame_deadline : 0;
450
+ }
451
+
452
+ uint32_t ed_scheduler_get_pending_count(EDScheduler* scheduler)
453
+ {
454
+ if (!scheduler) return 0;
455
+
456
+ uint32_t total = 0;
457
+ for (int i = 1; i < 6; i++) {
458
+ total += scheduler->queue_sizes[i];
459
+ }
460
+ return total;
461
+ }
462
+
463
+ /* ================================================================
464
+ * Recurring Task Support
465
+ * ================================================================ */
466
+
467
+ static uint32_t ed_scheduler_schedule_recurring(EDScheduler* scheduler,
468
+ EDSchedulerPriority priority,
469
+ EDSchedulerCallback callback,
470
+ void* data,
471
+ uint32_t interval_ms)
472
+ {
473
+ if (!scheduler || !callback || interval_ms == 0) return 0;
474
+
475
+ uint32_t task_id = ed_scheduler_schedule(scheduler, priority, callback, data);
476
+ if (task_id == 0) return 0;
477
+
478
+ /* Find the task and mark it as recurring */
479
+ uint32_t idx = ed_priority_to_index(priority);
480
+ EDSchedulerTask* task = scheduler->queues[idx];
481
+ while (task) {
482
+ if (task->id == task_id) {
483
+ task->is_recurring = true;
484
+ task->interval_ms = interval_ms;
485
+ break;
486
+ }
487
+ task = task->next;
488
+ }
489
+
490
+ return task_id;
491
+ }
492
+
493
+ /* ================================================================
494
+ * Deferred Value Scheduling
495
+ *
496
+ * Implements the deferred value pattern: schedule a low-priority
497
+ * update that can be interrupted by higher priority work.
498
+ * ================================================================ */
499
+
500
+ typedef struct EDDeferredValue {
501
+ void* value;
502
+ EDSchedulerPriority priority;
503
+ uint32_t task_id;
504
+ } EDDeferredValue;
505
+
506
+ static void ed_scheduler_deferred_callback(void* data)
507
+ {
508
+ EDDeferredValue* deferred = (EDDeferredValue*)data;
509
+ if (!deferred) return;
510
+
511
+ /* In a real implementation, this would trigger a re-render
512
+ * with the deferred value */
513
+ ed_log_debug("Deferred value update executed (priority=%u)",
514
+ deferred->priority);
515
+ }
516
+
517
+ static uint32_t ed_scheduler_schedule_deferred(EDScheduler* scheduler,
518
+ void* value,
519
+ EDSchedulerPriority priority)
520
+ {
521
+ if (!scheduler) return 0;
522
+
523
+ EDDeferredValue* deferred = (EDDeferredValue*)calloc(1, sizeof(EDDeferredValue));
524
+ if (!deferred) return 0;
525
+
526
+ deferred->value = value;
527
+ deferred->priority = priority;
528
+ deferred->task_id = 0;
529
+
530
+ EDSchedulerPriority effective_priority =
531
+ (priority < ED_PRIORITY_LOW) ? ED_PRIORITY_LOW : priority;
532
+
533
+ uint32_t task_id = ed_scheduler_schedule(
534
+ scheduler, effective_priority,
535
+ ed_scheduler_deferred_callback, deferred);
536
+
537
+ deferred->task_id = task_id;
538
+ return task_id;
539
+ }
540
+
541
+ /* ================================================================
542
+ * Transition Scheduling
543
+ *
544
+ * Implements the startTransition pattern: schedule work at
545
+ * a lower priority that doesn't block user input.
546
+ * ================================================================ */
547
+
548
+ typedef struct EDTransition {
549
+ void (*callback)(void* data);
550
+ void* data;
551
+ bool is_pending;
552
+ } EDTransition;
553
+
554
+ static void ed_scheduler_transition_callback(void* data)
555
+ {
556
+ EDTransition* transition = (EDTransition*)data;
557
+ if (!transition || !transition->callback) return;
558
+
559
+ transition->is_pending = true;
560
+ transition->callback(transition->data);
561
+ transition->is_pending = false;
562
+ }
563
+
564
+ static uint32_t ed_scheduler_start_transition(EDScheduler* scheduler,
565
+ void (*callback)(void*),
566
+ void* data)
567
+ {
568
+ if (!scheduler || !callback) return 0;
569
+
570
+ EDTransition* transition = (EDTransition*)calloc(1, sizeof(EDTransition));
571
+ if (!transition) return 0;
572
+
573
+ transition->callback = callback;
574
+ transition->data = data;
575
+ transition->is_pending = false;
576
+
577
+ return ed_scheduler_schedule(
578
+ scheduler, ED_PRIORITY_LOW,
579
+ ed_scheduler_transition_callback, transition);
580
+ }
581
+
582
+ /* ================================================================
583
+ * Starvation Prevention
584
+ *
585
+ * Prevents low-priority tasks from being starved by continuously
586
+ * arriving high-priority tasks. After a configurable number of
587
+ * high-priority tasks, we promote one lower-priority task.
588
+ * ================================================================ */
589
+
590
+ #define ED_STARVATION_THRESHOLD 50
591
+ #define ED_STARVATION_BOOST_DURATION 1
592
+
593
+ typedef struct EDStarvationState {
594
+ uint32_t consecutive_high_priority;
595
+ uint32_t boost_count;
596
+ bool is_boosting;
597
+ } EDStarvationState;
598
+
599
+ static EDStarvationState g_starvation = {0};
600
+
601
+ static bool ed_scheduler_check_starvation(EDScheduler* scheduler)
602
+ {
603
+ if (!scheduler) return false;
604
+
605
+ /* If we've executed too many high-priority tasks in a row,
606
+ * it's time to boost lower priority */
607
+ if (g_starvation.consecutive_high_priority >= ED_STARVATION_THRESHOLD) {
608
+ g_starvation.is_boosting = true;
609
+ g_starvation.boost_count = ED_STARVATION_BOOST_DURATION;
610
+ g_starvation.consecutive_high_priority = 0;
611
+ return true;
612
+ }
613
+
614
+ if (g_starvation.is_boosting) {
615
+ g_starvation.boost_count--;
616
+ if (g_starvation.boost_count == 0) {
617
+ g_starvation.is_boosting = false;
618
+ }
619
+ return true;
620
+ }
621
+
622
+ return false;
623
+ }
624
+
625
+ static void ed_scheduler_record_priority_executed(EDSchedulerPriority priority)
626
+ {
627
+ if (priority <= ED_PRIORITY_USER_BLOCKING) {
628
+ g_starvation.consecutive_high_priority++;
629
+ } else {
630
+ g_starvation.consecutive_high_priority = 0;
631
+ }
632
+ }
633
+
634
+ /* ================================================================
635
+ * Batch Scheduling
636
+ *
637
+ * Schedule multiple tasks as a group, ensuring they are all
638
+ * processed in the same frame if possible.
639
+ * ================================================================ */
640
+
641
+ typedef struct EDScheduleBatch {
642
+ uint32_t task_ids[ED_SCHEDULER_MAX_CALLBACKS];
643
+ uint32_t count;
644
+ } EDScheduleBatch;
645
+
646
+ static void ed_scheduler_batch_init(EDScheduleBatch* batch)
647
+ {
648
+ if (!batch) return;
649
+ batch->count = 0;
650
+ }
651
+
652
+ static uint32_t ed_scheduler_batch_add(EDScheduler* scheduler, EDScheduleBatch* batch,
653
+ EDSchedulerPriority priority,
654
+ EDSchedulerCallback callback, void* data)
655
+ {
656
+ if (!scheduler || !batch || !callback) return 0;
657
+ if (batch->count >= ED_SCHEDULER_MAX_CALLBACKS) return 0;
658
+
659
+ uint32_t id = ed_scheduler_schedule(scheduler, priority, callback, data);
660
+ if (id > 0) {
661
+ batch->task_ids[batch->count++] = id;
662
+ }
663
+ return id;
664
+ }
665
+
666
+ static void ed_scheduler_batch_cancel(EDScheduler* scheduler, EDScheduleBatch* batch)
667
+ {
668
+ if (!scheduler || !batch) return;
669
+
670
+ for (uint32_t i = 0; i < batch->count; i++) {
671
+ ed_scheduler_cancel(scheduler, batch->task_ids[i]);
672
+ }
673
+ batch->count = 0;
674
+ }
675
+
676
+ /* ================================================================
677
+ * Scheduler Statistics
678
+ * ================================================================ */
679
+
680
+ typedef struct EDSchedulerStats {
681
+ uint32_t total_scheduled;
682
+ uint32_t total_completed;
683
+ uint32_t total_cancelled;
684
+ uint32_t current_pending;
685
+ uint64_t total_execution_time;
686
+ uint32_t starvation_boosts;
687
+ } EDSchedulerStats;
688
+
689
+ static EDSchedulerStats ed_scheduler_get_stats(EDScheduler* scheduler)
690
+ {
691
+ EDSchedulerStats stats = {0};
692
+ if (!scheduler) return stats;
693
+
694
+ stats.total_scheduled = scheduler->total_tasks;
695
+ stats.total_completed = scheduler->completed_tasks;
696
+ stats.total_cancelled = scheduler->cancelled_tasks;
697
+ stats.current_pending = ed_scheduler_get_pending_count(scheduler);
698
+ stats.total_execution_time = 0; /* Would accumulate from task timings */
699
+ stats.starvation_boosts = 0;
700
+
701
+ return stats;
702
+ }