Closed LandryNorris closed 1 year ago
I can reproduce this on both of the two picos I currently have access to. Please let me know if any more information is required.
In any of the modified cases, the servo moves as expected. In the unmodified main.cpp, the servo never spins, since it is infinite looping in attach.
I'm traveling so won't be able to look at this for a while, but if you could hook up a Picoprobe and get a dump of the stack traces that would be helpful. At some point you start running into OOM errors just because of fragmentation and other things coming in, especially since there is use of the STL in the core (i.e. std::map
makes a tree of alloc'd pointers). ~160KB (the double array) out of ~190KB free (normal range) does seem like there should be plenty of space, but given what you're seeing I'd say you're hitting a fragmentation OOM since it works w/less allocated space...
I just noticed the dynamic allocation test is using the stack-allocated servo. Switched it to the dynamically allocated servo, and it reproduces the issue. I'll look at using the second pico as a picoprobe to get a report.
After debugging, I realized that I'm using malloc
, which does not initialize the memory, and won't call C++ constructors. Because of this, _attached
is not always initialized to 0, causing it to skip setting up the PIO state machines. The infinite loop appears to happen at the call to pio_sm_clear_fifos
on Servo.cpp:154. I guess I'm too used to doing things in a C way, where in an API like this, attach
would initialize the fields, or there would be an initialize
function for servos that would act like the constructor.
As for why this works if the memory usage is low, it appears that there's a zeroed out region (64 bytes long in my testing) that malloc was allocating from. In this case, _attached
is zero as expected, and the _minUs
and _maxUs
fields get set to avoid extremes.
Given that the issue is from me misusing memory and not an issue in the library, I'm happy to close this issue, but it would be nice if there were some extra protections, or if possible, if there could be a C-like initialization method, or if attach
handled this case.
Thanks for digging into this. I believe the proper way to make everything work is to use new
(which should work on classes IIRC and call embedded object constructors)
holder = new ServoHolder();
If that barfs, then making the struct
a class
would definitely call the proper constructors when new
is called.
A dynamically allocated Servo will infinite loop when
attach
ed if the program uses a large amount of static memory.I am using platform.io for the build system with the following platformio.ini file for all snippets below. Each snippet is in
main.cpp
There are multiple factors at play here, so this issue will unfortunately have to be a wall of text.
Minimum reproducible example
Actual
With the code above, I get the following output:
After this, there is no more output (adding code turning on the built-in LED shows that no code following this runs at all).
Expected
When I remove the
foo
array and comment the line printing items, I get the following output:This shows that the allocated memory is large enough to hold a servo object, and that the object is allocated properly.
Global Servo
I also modified the program to use a global servo object as shown below.
The output is as expected:
Static memory allocation
For completeness's sake, I also wrote a version of this using dynamically allocated memory for
foo
.The
attach
call freezes as before: