Skip to content

ESP8266WebServer uses >2kb of stack when reinitialized in setup() #2557

Closed
@chschu

Description

@chschu

Basic Infos

Hardware

Hardware: ESP-12F on NodeMCU clone (Geekcreit Doit)
Core Version: 2.3.0

Description

When reassigning a ESP8266WebServer in the setup() function, e.g. to make it listen only on the AP IP address, a temporary instance is created on the stack. This requires more than 2 kilobytes of stack space, and is pretty likely to overrun the stack boundary at 4 kilobytes in complex setups. This may or may not be detected by cont_check, and lead to strange errors and WDT resets.

The reason for this huge stack allocation is the 2K buffer in HTTPUpload contained in a ESP8266WebServer instance.

Strictly speaking, this is NOT a bug, and the stack is properly released after setup() returns. Still, it is somewhat inconvenient considering the small total stack size. The code below illustrates some workarounds (CASE 2 and 3) using more complex C++ syntax, but it would be nicer to not have to write things like that.

Settings in IDE

Module: NodeMCU 1.0 (ESP-12E Module)
Flash Size: 4MB (3M SPIFFS)
CPU Frequency: 80Mhz
Upload Using: SERIAL

Sketch

#include "ESP8266WiFi.h"
#include "ESP8266WebServer.h"

extern "C" {
#include <cont.h>
  extern cont_t g_cont;
}

// CASE 0: no reinitialization
// CASE 1: reinitialize from an implicit temporary instance created on the stack
// CASE 2: reinitialize from an explicit temporary instance created on the heap
// CASE 3: reinitialize with "placement new" (no temporary instance)
#define CASE 1

#define WIFI_SSID "..."
#define WIFI_PASS "..."

ESP8266WebServer server;

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  Serial.println();

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.print("Connecting to WiFi.");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("OK");

#if CASE == 0
  /* no code */
#elif CASE == 1
  server = ESP8266WebServer(WiFi.softAPIP());
#elif CASE == 2
  ESP8266WebServer *tmp = new ESP8266WebServer(WiFi.softAPIP());
  server = *tmp;
  delete tmp;
#elif CASE == 3
  server.~ESP8266WebServer();
  new (&server) ESP8266WebServer(WiFi.softAPIP());
#endif

  server.begin();

  Serial.printf("unmodified stack   = %4d\n", cont_get_free_stack(&g_cont));
  register uint32_t *sp asm("a1");
  Serial.printf("current free stack = %4d\n", 4 * (sp - g_cont.stack));
}

void loop() {
  // do nothing
  delay(1000);
}

Debug Messages

CASE 0: no reinitialization

unmodified stack   = 3024
current free stack = 4064

CASE 1: reinitialize from an implicit temporary instance created on the stack

unmodified stack   =  736
current free stack = 1776

CASE 2: reinitialize from an explicit temporary instance created on the heap

unmodified stack   = 2992
current free stack = 4032

CASE 3: reinitialize with "placement new" (no temporary instance)

unmodified stack   = 3008
current free stack = 4048

The value of "unmodified stack" varies a bit even for the same CASE, which may be caused by some uninitialized variables. The value of "current free stack" is always the same for one CASE, as one would expect.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions