Skip to content

docs: establish singleton and static object guidelines for SDOF prevention #202

Description

@kcenon

Summary

Establish ecosystem-wide guidelines for implementing singletons and managing static objects to prevent Static Destruction Order Fiasco (SDOF) across all kcenon system libraries.

Background

The Problem

Multiple ecosystem projects have encountered SDOF issues during process termination:

Current Ad-hoc Solutions

Different projects have applied various solutions independently:

  1. Intentional Leak pattern - Don't destroy singletons
  2. atexit handlers - Set shutdown flags before destruction
  3. Defensive checks - Skip operations if shutting down

Without unified guidelines, each project reinvents solutions, leading to:

  • Inconsistent implementations
  • Missed edge cases
  • Difficulty maintaining cross-project compatibility

Proposed Guidelines

1. Singleton Pattern Selection

Use Case Recommended Pattern Example
Logger, metrics (accessed from destructors) Intentional Leak thread_logger
Thread pools (long-lived, shared) Intentional Leak io_context_thread_manager
Configuration (read-only after init) Meyer's Singleton -
Factory (not accessed from destructors) Meyer's Singleton -

2. Intentional Leak Pattern Template

class MySingleton {
public:
    static MySingleton& instance() {
        // Intentionally leak to avoid SDOF.
        // Memory is reclaimed by OS on process termination.
        // This singleton may be accessed during other singletons' destruction.
        static MySingleton* instance = new MySingleton();
        return *instance;
    }
    
    // Optional: Explicit shutdown for graceful cleanup before exit
    static void prepare_shutdown() {
        // Set flags, flush buffers, etc.
        // Called via atexit or explicitly by application
    }

private:
    MySingleton() {
        // Register shutdown handler early
        std::atexit(prepare_shutdown);
    }
};

3. shared_ptr with No-op Deleter

For objects managed by shared_ptr that may outlive static destruction:

auto* obj = new MyClass();
auto ptr = std::shared_ptr<MyClass>(
    obj,
    [](MyClass*) { /* no-op deleter - intentional leak */ }
);

4. Documentation Requirements

Each singleton should document:

  1. Why the pattern was chosen
  2. Memory impact (typically ~few KB)
  3. Shutdown behavior (what happens at process exit)
  4. Thread safety guarantees

5. Testing Requirements

  • Test with Address Sanitizer (ASan)
  • Test with Thread Sanitizer (TSan)
  • Verify no SDOF on Ubuntu (strictest glibc checks)

Proposed Location

Add guidelines document to common_system:

  • docs/SINGLETON_GUIDELINES.md - Full documentation
  • include/kcenon/common/patterns/singleton.h - Optional template implementations

Acceptance Criteria

  • Create SINGLETON_GUIDELINES.md in common_system
  • Document Intentional Leak pattern with code examples
  • Document when to use Meyer's Singleton vs Intentional Leak
  • Add shared_ptr no-op deleter pattern documentation
  • Reference from thread_system and network_system READMEs
  • Optional: Provide template implementations

Benefits

  1. Consistency: All ecosystem projects follow same patterns
  2. Maintainability: Single source of truth for SDOF prevention
  3. Onboarding: Clear guidelines for new contributors
  4. Debugging: Easier to identify SDOF issues when patterns are documented

Related Issues

References

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions