8000 Under-provisioned fd_limit_per_thread leads to repeated reallocs and “leaks” under ESP-IDF 5.4.1 · Issue #3390 · warmcat/libwebsockets · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Under-provisioned fd_limit_per_thread leads to repeated reallocs and “leaks” under ESP-IDF 5.4.1 #3390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
filzek opened this issue May 15, 2025 · 5 comments

Comments

@filzek
Copy link
filzek commented May 15, 2025

When info.fd_limit_per_thread is set below the actual number of file descriptors (sockets + timer/SUL pipes), each call to _lws_change_pollfd() (triggered by lws_cancel_service() or internal timers) does a memory leakeage by 16kb in every cicle.

Workaround: bump fd_limit_per_thread to at least your real high-water FD count—once it never needs to grow, the “leaks” disappear.

  1. Build and run on ESP-IDF with standalone heap tracing (HEAP_TRACE_LEAKS) enabled.
  2. In your lws_context_creation_info, set: info.fd_limit_per_thread = 10; (or below the real LWS needs.
  3. Periodically call lws_cancel_service(context); (e.g. via WSI wake-pipe or SUL).
  4. Observe repeated 16-byte allocations in the heap-trace dump
  5. Change to: info.fd_limit_per_thread = 16; or greater until the problem disapear.
  6. Re-run and see that the allocations stop.

Expected Behavior
Libwebsockets should either:

Pre-allocate enough headroom by default to avoid repeated realloc(), or

Expose a better default for fd_limit_per_thread on ESP-IDF.

@lws-team
Copy link
Member

Thanks... I take it when you mention in the first sentence

When info.fd_limit_per_thread is set below the actual number of file descriptors (sockets + timer/SUL pipes), each call to _lws_change_pollfd() (triggered by lws_cancel_service() or internal timers) does a memory leakeage by 16kb in every cicle.

16kb means 16 bytes mentioned later?

Expose a better default for fd_limit_per_thread on ESP-IDF.

Because lws can be built in ways and for uses where the max fds is very low (eg, client-only) I don't think that changing this can really solve this problem, conversely whatever the limit you might sometimes reach it when several unrealated things are happening at once in the user code above lws. Unexpected retries might prolong the use of fds too and force you over the limit.

Looking at _lws_change_pollfd()... the suspicious guy loitering there is allocating struct lws_foreign_thread_pollfd which will be 16 bytes on a 32-bit machine.

That code is not actually aimed at ESP32, it was originally written as a workaround for BSD poll() which has a quirk it will overwrite any fd event wait changes (like POLLIN etc) on any fds that are undergoing a wait at the time, when the wait ends. So this code tries to collate any changes like that into an allocated linked-list, and replay them after the wait end, when they will stick. Unfortunately the code to drain the linked-list of changes every event loop only exists in the unix plat service code. Since that time, using lws apis from another thread (except lws_cancel_service()) was made illegal anyway, so this is kind of defunct.

Rather than pull it out, since unless there is an illegal thread going using lws apis it should never allocate, but it's possible there are users relying on that, I pushed a patch that only includes it if's not a non-unix plat. That should fix this for you.

@filzek
Copy link
Author
filzek commented May 15, 2025

@lws-team thanks.

I am one of the firsts that are doing the lsw under the esp32 right now based on the espressif component and found this bogus, we than start to follow all over to find if there is anything messing with the memory everywhere and only has found that, yes 16bytes sorry for the typo.

I have applied all the patches to the lastest as of today and will check if this is fixes so far and will reply later.

thanks

@filzek
Copy link
Author
filzek commented May 15, 2025

@lws-team so far so good, things running perfect for now!

thanks I will close the issue!

@filzek filzek closed this as completed May 15, 2025
@lws-team
Copy link
Member

Although espressif got interested recently, lws has been working on esp32/8266 since 2016 with 7acf76c . Changes to esp-idf in the years after that trashed most of that work, although in the last couple of years I added back platform and build support with less emphasis on esp and more on freertos. AFAIK what they are making available esp-idf is more or less just upstream lws. It's a good thing if that means they will maintain the esp-specific pieces against esp-idf mutations, since I got third degree burns by trying to do that myself for free back in the day.

@filzek
Copy link
Author
filzek commented May 16, 2025

sure, I am testing the full approach on it, i have moved to the head of master and its working well, i have recoded all the handles now and it seens that its okay. still facing some weirdo disconnections issues but as I am in china now everything is possible, so, i will not change anything on it, but there is something thats fault in the wdt_reset, as it depends on the time it was set in the sdkconfig from the project no matter if pure aws freertos or esp-idf the wdt will strike as the lws_services will take more time to wdt as it is in a stale state, so, I will think how would be the best approach to suggest any changes on this to make more efficient and produces a well result.

One of the think should be ack from the service creation handle the wdt time from the system, so this should correct the path and actions as the wdt in services should be called prior to the time rings.

no memory issues so far!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
0