Since presym is now mandatory, mruby.h includes presym.h so that
MRB_SYM() macros are available everywhere without explicit include.
Remove redundant #include <mruby/presym.h> from all source files.
Co-authored-by: Claude <noreply@anthropic.com>
Replace check_method_noarg() with check_argument_count() that validates
min <= argc <= max using the full aspec stored in mrb_method_t.flags.
This catches ArgumentError earlier at dispatch time, before entering
the C function.
The old check only handled the special case of aspec==0 (NOARG).
The new check extracts REQ, OPT, REST, POST, KEY, and KDICT from
the aspec and validates accordingly. Keyword hash is counted as
a positional arg only when the method doesn't accept keywords.
Remove MRB_METHOD_NOARG_P macro from proc.h (subsumed by aspec check).
Fix 15 incorrect aspec declarations across the codebase that were
exposed by the stricter enforcement.
Co-authored-by: Claude <noreply@anthropic.com>
The current implementation of `task_init_context` inheriting a receiver from the parent task is unstable and causes critical faults, especially on microcontrollers.
- It leads to a HardFault on devices like Raspberry Pi Pico 2 by accessing a potentially NULL `mrb->c->ci`.
- Even when `mrb->c->ci` is not NULL, this incomplete context copy causes other memory errors (SEGV).
This patch reverts to the safer, previous behavior, that I implemented in picoruby/picoruby, of always initializing a new task's receiver to `top_self`, ensuring predictable and
robust operation.
The issue was likely masked on POSIX systems due to the unpredictable nature of undefined behavior.
Old code:
```c
t = q_ready_;
/* No task ready - check if all tasks are done */
if (!t) {
/* If there are tasks waiting or suspended, idle */
if (q_waiting_ || q_suspended_) {
mrb_hal_task_idle_cpu(mrb);
continue;
```
IRQ possibly happens between `t = q_ready_;` and `if (q_waiting_ || q_suspended_) {` and, for example, a waiting task may move to the ready queue.
As a result, the infinite loop in mrb_task_run unexpectedly breaks in spite of not all the task is dormant.
This patch fixes the issue above by setting the `exitting` condition with a critical section.
- Add mrb_gc_protect() after arena_restore to prevent result from being collected before returning to caller
- Add comment to suspend_task_internal explaining why WAITING and DORMANT tasks can also be suspended
- Move argc/argv cast at the beginning of function with comment
With this PR, I can remove the original task.c in picoruby/picoruby and future development will be much easier.
## Add
### General
- C API functions exported with MRB_API for external integration:
- mrb_execute_proc_synchronously() for synchronous proc execution
- Task control APIs (mrb_create_task, mrb_suspend_task, mrb_resume_task, mrb_terminate_task, mrb_stop_task, mrb_task_value, mrb_task_status)
- Task context management APIs for picoruby-sandbox (mrb_task_init_context, mrb_task_reset_context, mrb_task_proc_set)
- Task.tick class method to get current tick count
- Comprehensive C API documentation with WASM integration examples
### For PicoRuby.wasm
- WASM/Emscripten support: Disable SIGALRM timer when __EMSCRIPTEN__ is defined, as JavaScript handles tick calls via setInterval
- Scheduler lock mechanism to prevent asynchronous task operations during synchronous execution (scheduler_lock counter in mrb_task_state)
- mrb_task_run_once() for single-step execution (event loop integration)
## Fix
### task.c
- Replace MRB_FIBER_TERMINATED with MRB_TASK_STOPPED just for clarity
- Allow suspending DORMANT and WAITING tasks in mrb_task_suspend (See comment in the source)
- Task context initialization by removing dummy callinfo push/pop
*NOTE*
With the dummy callinfo code that I deleted, IRB in PicoRuby ended SEGV.
If that code is mandatory, we need to discuss how to solve my problem.
### vm.c
- Handle MRB_TASK_CREATED status in VM's NORMAL_RETURN phase to properly stop tasks
----
These changes are necessary to make PicoRuby work.
Nevertheless, even with this patch, MicroRuby for Raspberry Pi Pico 2 is still unstable.
I would like to merge this PR anyway to make development easier by involving the PicoRuby community.
remove redundant visualcpp and mingw checks since for_windows? already
detects all windows builds including visual c++ and mingw.
ref #6653
Co-authored-by: Claude <noreply@anthropic.com>
increase fiber_stack_init_size and task_stack_init_size from 16 to 64
to fix crashes on 32-bit msvc builds. git bisect identified commit
3246dd2 (which reduced sizes from 64 to 16) as causing the issue.
empirical testing shows 48 fails intermittently but 64 is stable on
32-bit msvc, likely due to different alignment or initialization
overhead on 32-bit platforms.
Co-authored-by: Claude <noreply@anthropic.com>
mingw provides posix compatibility for file i/o but not for signal
handling. hal-posix-task relies on SIGALRM, setitimer(), and
sigprocmask() which are not available on windows even through mingw.
changed hal selection for mruby-task to use hal-win-task for mingw,
while mruby-dir, mruby-io, and mruby-socket correctly use posix hals
for mingw since those features are supported.
Co-authored-by: Claude <noreply@anthropic.com>
when building with MSVC on Windows, RUBY_PLATFORM (from the Ruby
installation running rake) may indicate "mingw" if Ruby was installed
via RubyInstaller, causing incorrect selection of POSIX HALs instead
of Windows HALs.
fixed by checking spec.build.primary_toolchain first:
- if toolchain is "visualcpp", select Windows HALs
- otherwise fall through to existing platform checks
this ensures MSVC builds use hal-win-* gems even when Ruby itself
was installed with MinGW.
affected gems:
- mruby-dir
- mruby-io
- mruby-socket
- mruby-task
Co-authored-by: Claude <noreply@anthropic.com>
change sleep_us_impl and sleep_ms_impl parameters from mrb_int to uint32_t.
this makes the type requirement explicit and resolves msvc warning c4244.
all type conversions happen at ruby boundary functions after validation.
Co-authored-by: Claude <noreply@anthropic.com>
reduced TASK_STACK_INIT_SIZE from 64 to 16 and TASK_CI_INIT_SIZE from 8 to 4,
matching mruby-fiber's conservative allocations. this saves 56 bytes per task
(320 bytes down to 160 bytes for initial allocations). stacks grow dynamically
via mrb_stack_extend when needed.
Co-authored-by: Claude <noreply@anthropic.com>
use t->c.ci->proc directly with explicit null check instead of falling
back to t->proc (which was removed). with c function boundary checks
preventing suspension in c functions, proc should always be valid on resume.
Co-authored-by: Claude <noreply@anthropic.com>
removes duplicate proc field and adds state-based union for result/timeslice,
achieving 16 bytes total savings per task (12.5% reduction):
optimizations:
- removed proc field (stored in c.ci->proc, already marked by gc): 8 bytes
- unified result/timeslice into state union (mutually exclusive): ~4 bytes
- combined with previous commit savings (priority_preemption, started, etc)
total reduction: 128 -> 112 bytes per task
impact:
- 10 tasks: 160 bytes saved
- 50 tasks: 800 bytes saved
- 100 tasks: 1.6 KB saved
all 1770 tests pass with zero functionality changes.
Co-authored-by: Claude <noreply@anthropic.com>
reduces per-task memory usage by 8 bytes (6.2%) through:
- removing priority_preemption field (always equals priority)
- removing started flag (inferred from context status)
- unifying wakeup_tick/join/mutex into single union
old size: 128 bytes
new size: 120 bytes
all tests pass with no functionality changes.
Co-authored-by: Claude <noreply@anthropic.com>
when task.pass is called from within a C function (such as Module.new's
block evaluation), attempting to yield would cause a segfault because C
functions lack valid bytecode program counters (see #6642).
this commit adds C function boundary detection to task.pass, raising a
runtime error when cci > 0 (indicating execution is inside a C function).
this matches fiber's behavior and provides a clear error message instead of
a cryptic segfault.
unlike the previous commit which allowed sleep to fall back to blocking
sleep, task.pass raises an exception because its sole purpose is cooperative
yielding - there is no sensible blocking fallback behavior.
Co-authored-by: Claude <noreply@anthropic.com>
when sleep() was called from within a C function (such as module.new's block
evaluation), the task scheduler would segfault while attempting to resume the
task. this occurred because C functions don't execute bytecode and thus their
callinfo has no valid program counter (pc). when the task tried to resume
execution, mrb_vm_exec() received a null pc, causing a segmentation fault.
the fix adds two safeguards in task.c:
1. C function boundary detection: before suspending a task for sleep, check
if we're inside a C function by examining the cci (c call info) field.
if cci > 0, fall back to blocking sleep via HAL instead of attempting
cooperative context switch. this preserves sleep functionality without
raising exceptions, though it blocks other tasks during the sleep period.
2. proc fallback in execute_task(): use the task's stored proc if the
current callinfo's proc is null, ensuring mrb_vm_exec() always receives
a valid proc pointer.
this approach prioritizes functionality over strict cooperative multitasking
semantics - tasks can still sleep inside C functions, but the sleep becomes
blocking. the alternative would be raising an exception like fiber does, but
that would break existing code unexpectedly.
Co-authored-by: Claude <noreply@anthropic.com>
rename all HAL functions from mrb_<feature>_hal_<name>() to
mrb_hal_<feature>_<name>() for better grouping and clarity. this makes all
HAL functions immediately identifiable with the mrb_hal_* prefix.
affected gems:
- mruby-task: mrb_task_hal_* -> mrb_hal_task_*
- mruby-io: mrb_io_hal_* -> mrb_hal_io_*
- mruby-socket: mrb_socket_hal_* -> mrb_hal_socket_*
- mruby-dir: mrb_dir_hal_* -> mrb_hal_dir_*
Co-authored-by: Claude <noreply@anthropic.com>
changed from angle brackets to quotes for gem-local HAL headers
(task.h, io_hal.h, socket_hal.h), and removed relative path prefix
from task.h include. this follows the mrbgem build system convention
where gem/include/ is automatically added to the include path.
Co-authored-by: Claude <noreply@anthropic.com>
task.c used clock_gettime() directly, breaking portability. added
mrb_task_hal_sleep_us() to hal interface.
Co-authored-by: Claude <noreply@anthropic.com>
follows mrb_{gem_name}_{operation} naming convention consistently
with other hal functions like mrb_task_hal_init. the plural form was
semantically correct but inconsistent with gem naming patterns.
Co-authored-by: Claude <noreply@anthropic.com>
removes mrb_tasks_run and mrb_task_mark_all from task_hal.h as these
are core scheduler functions, not HAL interface functions. only
mrb_tick remains as it must be called by HAL timer callbacks.
Co-authored-by: Claude <noreply@anthropic.com>
separates platform-specific timer and interrupt code into hal-posix-task
and hal-win-task gems. mruby-task now uses HAL interface defined in
task_hal.h, making it easier to port to new platforms.
hal-posix-task: uses sigalrm/setitimer for timer, sigprocmask for irq
protection, and SA_RESTART flag to prevent EINTR on system calls.
hal-win-task: uses multimedia timer API and critical_section for irq
protection.
both HALs support multiple mrb_state instances with single shared timer.
auto-detection loads appropriate HAL based on platform.
Co-authored-by: Claude <noreply@anthropic.com>
mruby-task uses mrb_context and mrb_fiber_state enum, but these are
part of core mruby, not the mruby-fiber gem. the dependency was not
needed.
Co-authored-by: Claude <noreply@anthropic.com>
windows multimedia timer api requires linking with winmm.lib. added
conditional linker library using spec.for_windows? to match mruby
build system conventions.
Co-authored-by: Claude <noreply@anthropic.com>
extended posix platform detection to include macos via __APPLE__ and
__MACH__ defines. implemented full windows hal using multimedia timer
(timeSetEvent) and CRITICAL_SECTION for thread synchronization. added
task_count_update stub for unsupported platforms with clear warnings.
Co-authored-by: Claude <noreply@anthropic.com>
remove MRB_USE_TASK_SCHEDULER ifdef guards from task.h and task.c
since the macro is always defined when compiling this gem
Co-authored-by: Claude <noreply@anthropic.com>
- remove redundant MRB_TASK_CREATED/STOPPED macros from task.h since
they are now properly defined in mrb_fiber_state enum in mruby.h
- declare kw_names array separately to avoid taking address of
temporary array in c++ compilation
Co-authored-by: Claude <noreply@anthropic.com>
clarify that task.new name parameter must be string, document
task#name returns "(noname)" for unnamed tasks, and provide full
structure of task.stat return value.
Co-authored-by: Claude <noreply@anthropic.com>
add tests for sleep/usleep validation, task creation, status/inspect
methods, control methods, task.stat, priority handling, and name
handling.
Co-authored-by: Claude <noreply@anthropic.com>
fix uninitialized kwargs array causing crashes, add type validation for
name (must be String) and priority (must be Integer) parameters, return
"(noname)" for unnamed tasks.
Co-authored-by: Claude <noreply@anthropic.com>
replaced stub with full implementation that returns a hash containing
scheduler statistics:
- tick: current tick counter
- wakeup_tick: next scheduled wakeup time
- dormant/ready/waiting/suspended: per-queue statistics
each queue stat includes:
- count: number of tasks in queue
- tasks: array of task objects in that queue
implements helper function mrb_stat_sub() to walk queues and collect
task information. uses irq disable/enable to ensure consistent snapshot.
returns hash directly as requested, not wrapped in stat object.
Co-authored-by: Claude <noreply@anthropic.com>
added inspect method that returns formatted string showing:
- task pointer address
- task name (string/symbol), or "(unnamed)" for nil/other types
- task status (RUNNING, READY, WAITING, SUSPENDED, DORMANT, UNKNOWN)
format matches original implementation: #<Task:0x12345678 name:STATUS>
avoids mrb_funcall during inspection to prevent vm state issues.
handles string and symbol names directly, treats other types as unnamed.
Co-authored-by: Claude <noreply@anthropic.com>
replaced stub implementation with proper status reporting that returns
symbols representing task state:
- :RUNNING for executing tasks
- :READY for tasks ready to execute
- :WAITING for tasks waiting (sleeping, blocked, etc.)
- :SUSPENDED for manually suspended tasks
- :DORMANT for terminated tasks
- :UNKNOWN for invalid states
implementation matches original mruby-task design using ternary operators
and MRB_SYM() macros for efficient symbol lookup.
Co-authored-by: Claude <noreply@anthropic.com>
improved sleep_us_impl() in several ways:
1. dynamic sleep intervals: now sleeps for actual remaining time instead
of fixed 1ms polling, reducing unnecessary wakeups and improving
efficiency for longer sleeps
2. error handling: added checks for clock_gettime() failures with fallback
to usleep(), and input validation to handle negative values
3. overflow prevention: use named constant USEC_PER_MSEC instead of
literal 1000 for microsecond-to-nanosecond conversion, and validate
input before conversion
4. wraparound handling: fixed tick comparison at line 580 to use signed
arithmetic like other tick comparisons in the codebase
5. code clarity: added time conversion constants (NSEC_PER_MSEC,
NSEC_PER_SEC, USEC_PER_MSEC) to replace magic numbers
all tests pass.
Co-authored-by: Claude <noreply@anthropic.com>
when sleep is called from root context (not within a task), it was
instantly advancing the simulated tick counter instead of actually
delaying. this caused task_pass.rb example to run tasks 0-5 instantly
without proper delays between iterations.
fixed by using clock_gettime() to track elapsed real time and sleeping
in 1ms intervals. also clear switching_ flag when returning from root
context sleep to prevent unwanted context switches.
removed find_earliest_wakeup_tick() function and time-advancing logic
from task_run_one_iteration() as real delays are now handled by sleep
itself.
Co-authored-by: Claude <noreply@anthropic.com>
Renamed constants to use more descriptive underscores:
- MRB_TASKSTATUS_* -> MRB_TASK_STATUS_*
- MRB_TASKREASON_* -> MRB_TASK_REASON_*
This improves code readability by making the constant names clearer.
Co-authored-by: Claude <noreply@anthropic.com>