Time of thread memory allocation

Is thread memory allocated at the time of Thread object creation, or when the thread is started? I have been having some memory crashes and am trying to reduce memory usage where I can. The most obvious way for me to try to do this is by reducing the stack size of threads that are only started under particular operation modes, which are relatively rare compared to the standard operation mode of my product. Basically, I want to know if instantiated but not started threads are contributing to my memory overhead.

Hello Eric,

Thread’s stack memory seems to be allocated dynamically on the heap using the new operator when a thread is started. See in mbed-os/rtos/source/Thread.cpp :

osStatus Thread::start(mbed::Callback<void()> task)
{
    ...
    if (_attr.stack_mem == nullptr) {
        _attr.stack_mem = new uint32_t[_attr.stack_size / sizeof(uint32_t)];
        MBED_ASSERT(_attr.stack_mem != nullptr);
    }
    ...

However, in addition to that data members Thread Control Block and Thread Attributes are allocated in the constructor during the creation of a Thread object:

/// Thread Control Block
typedef struct osRtxThread_s {
  uint8_t                          id;  ///< Object Identifier
  uint8_t                       state;  ///< Object State
  uint8_t                       flags;  ///< Object Flags
  uint8_t                        attr;  ///< Object Attributes
  const char                    *name;  ///< Object Name
  struct osRtxThread_s   *thread_next;  ///< Link pointer to next Thread in Object list
  struct osRtxThread_s   *thread_prev;  ///< Link pointer to previous Thread in Object list
  struct osRtxThread_s    *delay_next;  ///< Link pointer to next Thread in Delay list
  struct osRtxThread_s    *delay_prev;  ///< Link pointer to previous Thread in Delay list
  struct osRtxThread_s   *thread_join;  ///< Thread waiting to Join
  uint32_t                      delay;  ///< Delay Time
  int8_t                     priority;  ///< Thread Priority
  int8_t                priority_base;  ///< Base Priority
  uint8_t                 stack_frame;  ///< Stack Frame (EXC_RETURN[7..0])
  uint8_t               flags_options;  ///< Thread/Event Flags Options
  uint32_t                 wait_flags;  ///< Waiting Thread/Event Flags
  uint32_t               thread_flags;  ///< Thread Flags
  struct osRtxMutex_s     *mutex_list;  ///< Link pointer to list of owned Mutexes
  void                     *stack_mem;  ///< Stack Memory
  uint32_t                 stack_size;  ///< Stack Size
  uint32_t                         sp;  ///< Current Stack Pointer
  uint32_t                thread_addr;  ///< Thread entry address
  uint32_t                  tz_memory;  ///< TrustZone Memory Identifier
#ifdef RTX_TF_M_EXTENSION
  uint32_t                  tz_module;  ///< TrustZone Module Identifier
#endif
} osRtxThread_t;

...
/// Attributes structure for thread.
typedef struct {
  const char                   *name;   ///< name of the thread
  uint32_t                 attr_bits;   ///< attribute bits
  void                      *cb_mem;    ///< memory for control block
  uint32_t                   cb_size;   ///< size of provided memory for control block
  void                   *stack_mem;    ///< memory for stack
  uint32_t                stack_size;   ///< size of stack
  osPriority_t              priority;   ///< initial thread priority (default: osPriorityNormal)
  TZ_ModuleId_t            tz_module;   ///< TrustZone module identifier
  uint32_t                  reserved;   ///< reserved (must be 0)
} osThreadAttr_t;

Thank you for your reply @hudakz . This is consistent with what I have seen in experimentation.