Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems

1. Singleton Pattern

The Singleton Pattern ensures that a class has only one instance and provides a global access point.

Core structure diagram of the Singleton Pattern:

Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems

The structure typically includes:

  • A private static instance (pointer to itself)
  • A private constructor (to prevent external instantiation)
  • A public static method (to get the unique instance)

In embedded systems, this pattern is particularly suitable for:

  • Global State: System configuration, error logs
  • Hardware Peripheral Management: SPI/I2C controllers
  • Resource Pool: Memory pool, connection pool
  • Core Manager: Power management, security controllers

Key points in embedded design:

  • Thread Safety: Critical sections must be protected by disabling interrupts or using mutexes (especially in RTOS).
  • No Dynamic Memory: Prefer static allocation of instances to avoid heap memory fragmentation.

2. Embedded Application Case

Maintaining system state in a remote monitoring system.

  • Non-Singleton Pattern: Each module (sensor module, control module, communication module) maintains its own system state, leading to:
    • The temperature sensor believes the system is normal
    • But the control module detects a fault
    • Inconsistent states lead to dangerous operations
  • Singleton Pattern: Establish a central console (the unique instance), all modules access the system state through it, ensuring global decision consistency.
Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems

1. Code Implementation:

In C language:

Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems
#include <stdio.h>
#include <pthread.h>

// Global state structure
typedef struct {
    int system_mode;
    int error_code;
    unsigned long operation_count;
} SystemState;

// Singleton state manager
typedef struct {
    SystemState state;
    pthread_mutex_t lock;
} StateManager;

// Global singleton instance
static StateManager* instance = NULL;

// Get singleton instance (thread-safe)
StateManager* get_state_manager() {
    if (instance == NULL) {
        // Create mutex
        static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
        pthread_mutex_lock(&init_lock);
        
        if (instance == NULL) {
            static StateManager manager;
            manager.state.system_mode = 0;
            manager.state.error_code = 0;
            manager.state.operation_count = 0;
            pthread_mutex_init(&manager.lock, NULL);
            instance = &manager;
        }
        
        pthread_mutex_unlock(&init_lock);
    }
    return instance;
}

// Set system mode
void set_system_mode(int mode) {
    StateManager* manager = get_state_manager();
    pthread_mutex_lock(&manager->lock);
    manager->state.system_mode = mode;
    pthread_mutex_unlock(&manager->lock);
}

// Get system mode
int get_system_mode() {
    StateManager* manager = get_state_manager();
    pthread_mutex_lock(&manager->lock);
    int mode = manager->state.system_mode;
    pthread_mutex_unlock(&manager->lock);
    return mode;
}

// Report error
void report_error(int error_code) {
    StateManager* manager = get_state_manager();
    pthread_mutex_lock(&manager->lock);
    manager->state.error_code = error_code;
    pthread_mutex_unlock(&manager->lock);
}

// Increment operation count
void increment_operation_count() {
    StateManager* manager = get_state_manager();
    pthread_mutex_lock(&manager->lock);
    manager->state.operation_count++;
    pthread_mutex_unlock(&manager->lock);
}

// Print system state
void print_system_state() {
    StateManager* manager = get_state_manager();
    pthread_mutex_lock(&manager->lock);
    
    printf("\n=== System State ===\n");
    printf("Current Mode: %s\n", 
           manager->state.system_mode == 0 ? "Normal Mode" :
           manager->state.system_mode == 1 ? "Maintenance Mode" : "Emergency Mode");
    printf("Error Code: 0x%04X\n", manager->state.error_code);
    printf("Operation Count: %lu\n", manager->state.operation_count);
    
    pthread_mutex_unlock(&manager->lock);
}

// Simulate sensor module
void* sensor_module(void* arg) {
    printf("Sensor starting...\n");
    for (int i = 0; i < 3; i++) {
        increment_operation_count();
        printf("Sensor detecting...\n");
        sleep(1);
    }
    return NULL;
}

// Simulate control module
void* control_module(void* arg) {
    printf("Controller starting...\n");
    sleep(1);
    
    // Simulate error detection
    report_error(0xE1A2);
    set_system_mode(2);  // Enter emergency mode
    
    printf("Controller detected a serious error!\n");
    return NULL;
}

int main() {
    printf("=== Control System Starting ===\n");
    
    // Initialize state manager
    get_state_manager();
    print_system_state();
    
    // Create simulation threads
    pthread_t sensor_thread, control_thread;
    pthread_create(&sensor_thread, NULL, sensor_module, NULL);
    pthread_create(&control_thread, NULL, control_module, NULL);
    
    // Wait for threads to finish
    pthread_join(sensor_thread, NULL);
    pthread_join(control_thread, NULL);
    
    // Print final state
    print_system_state();
    
    printf("\n=== System Shutdown ===\n");
    return 0;
}
Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems

In C language, there is no concept of classes, so we simulate the Singleton Pattern using static global variables and functions.

  • Private Static Instance: A static global pointer <span>static StateManager* instance = NULL;</span> points to the unique instance. The instance itself is created using a static local variable <span>static StateManager manager;</span> within the getter function. This way, the instance is initialized only once during the program’s execution and returned via a pointer.

  • Private Constructor: In C, this is achieved through static initialization or initialization within the getter function. In the <span>get_state_manager()</span> function, the instance is created using a static local variable and initialized only once.

  • Public Static Method: This is the <span>get_state_manager()</span> function, which returns a pointer to the singleton instance. We also provide other functions to operate on the state, such as <span>set_system_mode()</span>, <span>get_system_mode()</span>, etc., which internally call <span>get_state_manager()</span> to get the singleton instance.

In C++:

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

class StateManager {
private:
    struct State {
        int system_mode = 0;
        int error_code = 0;
        unsigned long operation_count = 0;
    } state;
    
    std::mutex mtx;  // Non-static member lock, protects instance state
    
    StateManager() = default;  // Private constructor

public:
    // Delete copy constructor and assignment operator
    StateManager(const StateManager&) = delete;
    void operator=(const StateManager&) = delete;

    // Use local static variable to implement thread-safe singleton
    static StateManager& getInstance() {
        static StateManager instance;
        return instance;
    }
    
    void setSystemMode(int mode) {
        std::lock_guard<std::mutex> lock(mtx);
        state.system_mode = mode;
        state.operation_count++;
    }
    
    int getSystemMode() {
        std::lock_guard<std::mutex> lock(mtx);
        return state.system_mode;
    }
    
    void printState() {
        std::lock_guard<std::mutex> lock(mtx);
        std::cout << "Mode: " << state.system_mode 
                  << " | Errors: 0x" << std::hex << state.error_code
                  << " | Operations: " << std::dec << state.operation_count << "\n";
    }
};

// Test function
void threadTask(int mode) {
    StateManager& manager = StateManager::getInstance();
    manager.setSystemMode(mode);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    manager.printState();
}

int main() {
    std::vector<std::thread> threads;
    
    // Create multiple threads to test thread safety
    for (int i = 1; i <= 5; ++i) {
        threads.emplace_back(threadTask, i);
    }
    
    // Wait for all threads to finish
    for (auto& t : threads) {
        t.join();
    }

    // Main thread access
    StateManager::getInstance().printState();
    return 0;
}

2. Advantages and Disadvantages

Advantages:

(1) Resource Efficiency:

  • Memory Savings: Only one instance, reducing RAM occupied by duplicate objects (critical for resource-constrained MCUs).
  • Performance Improvement: Avoid frequent creation/destruction of objects (e.g., communication protocol stacks), reducing CPU overhead.

(2) Behavioral Consistency:

  • Prevents hardware state conflicts (e.g., multiple tasks configuring UART baud rates simultaneously).
  • Ensures global data (e.g., calibration parameters) is uniquely reliable.

Disadvantages:

(1) Poor Scalability: Lack of abstraction layer makes it difficult to extend functionality through inheritance (violating the Open/Closed Principle).

(2) Lifecycle Issues: Static instances occupy memory for a long time, wasting resources if not utilized.

3. Summary of Applicability in Embedded Scenarios

Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems
Scenario Reason
Hardware Peripheral Management (SPI/UART) Prevent multi-task conflicts
Global Configuration Manager Ensure parameter consistency
High-frequency Object Creation/Destruction Reduce CPU overhead
Shared Utility Classes Across Modules Simplify access but increase coupling

Singleton Pattern: The Guardian of Global State Consistency in Embedded Systems

END

Author: Mixed Content

Source: Embedded MiscellaneousCopyright belongs to the original author. If there is any infringement, please contact for deletion..Recommended ReadingSharing several BootLoaders suitable for MCUWhy some domestic chips do not disclose data sheets?Incredible! These 17 C pointer tricks have been collected by countless programmers overnight→ Follow for more updates ←

Leave a Comment