10 पॉइंट द्वारा GN⁺ 2025-11-19 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • safe_c.h एक 600-लाइन की कस्टम हेडर फ़ाइल है जो C भाषा में C++ और Rust की सुरक्षा व सुविधा सुविधाएँ जोड़ती है, और इसका उपयोग मेमोरी लीक-रहित thread-safe grep(cgrep) इम्प्लीमेंटेशन में किया गया है
  • RAII, smart pointers, automatic cleanup cleanup attribute के जरिए मैन्युअल free() कॉल के बिना resource management को ऑटोमेट किया जाता है
  • vector, view, Result type, contract macros आदि की मदद से buffer overflow, error handling, और precondition validation को सुरक्षित तरीके से किया जाता है
  • automatic mutex unlock, thread spawn macros, branch prediction optimization आदि के जरिए concurrency और performance बनाए रखते हुए safety सुनिश्चित की जाती है
  • नतीजतन, समान performance (-O2 स्तर) के साथ लीक और segfault के बिना C code लिखना संभव है यह दिखाया गया है

safe_c.h अवलोकन

  • safe_c.h एक हेडर फ़ाइल है जो C++ और Rust की सुविधाओं को C code में लाती है
    • C23 के [[cleanup]] attribute को सपोर्ट न करने वाले compilers (GCC 11, Clang 18 आदि) में भी वही RAII (automatic cleanup) व्यवहार देता है
    • CLEANUP(func) macro से function समाप्त होने पर resources अपने-आप रिलीज़ हो जाते हैं
    • LIKELY() और UNLIKELY() macros के जरिए hot path branch prediction optimization

मेमोरी प्रबंधन: UniquePtr और SharedPtr

  • UniquePtr एक single-ownership smart pointer है जो scope खत्म होने पर अपने-आप free() कॉल करता है
    • AUTO_UNIQUE_PTR() macro के साथ declare करने पर error या early return की स्थिति में भी memory अपने-आप रिलीज़ हो जाती है
  • SharedPtr एक automatic reference counting structure है जिसमें आख़िरी reference हटने पर resource अपने-आप destroy हो जाता है
    • shared_ptr_init() और shared_ptr_copy() से reference increment/decrement अपने-आप संभाला जाता है
    • threads के बीच safely shared structs को manage करने में उपयोगी

buffer overflow रोकथाम: Vector और View

  • DEFINE_VECTOR_TYPE() macro से type-safe auto-growing vector बनाया जा सकता है
    • reallocation, capacity management, और cleanup अपने-आप संभाले जाते हैं
    • AUTO_TYPED_VECTOR() के साथ declare करने पर scope खत्म होने पर auto free हो जाता है
  • StringView और Span non-owning reference structures हैं, जो अलग malloc के बिना string और array slices को संभालते हैं
    • DEFINE_SPAN_TYPE() से type-specific Span define किया जा सकता है
    • boundary checks शामिल होने से safe array access सुनिश्चित होता है

error handling: Result type और RAII

  • Result structure Rust के Result<T, E> जैसा success/failure-discriminated return type है
    • DEFINE_RESULT_TYPE() से type-specific result structures बनाए जा सकते हैं
    • RESULT_IS_OK() और RESULT_UNWRAP_ERROR() से error handling स्पष्ट रहती है
  • CLEANUP attribute के साथ मिलाकर function समाप्ति पर resources auto-release किए जा सकते हैं
    • AUTO_MEMORY() macro से malloc की गई memory अपने-आप साफ़ होती है

contracts और safe strings

  • requires() / ensures() macros से function की preconditions और postconditions स्पष्ट की जा सकती हैं
    • failure होने पर साफ़ error message प्रिंट होता है
  • safe_strcpy() एक buffer size check के साथ copy function है, जो overflow रोकता है
    • failure पर false return करके safe error handling देता है

concurrency: automatic unlock और thread macros

  • CLEANUP आधारित automatic mutex unlock function deadlock रोकने में मदद करता है
    • scope समाप्त होने पर pthread_mutex_unlock() अपने-आप कॉल होता है
  • SPAWN_THREAD() और JOIN_THREAD() macros से thread creation और join सरल हो जाते हैं
    • cgrep के file-processing thread pool इम्प्लीमेंटेशन में उपयोग किया गया

performance optimization

  • LIKELY() / UNLIKELY() macros के जरिए hot path branch prediction मिलती है
    • -O2 build में भी PGO-स्तर जैसा optimization प्रभाव मिलता है
  • safety features जुड़ने के बावजूद performance loss नहीं होता

निष्कर्ष

  • safe_c.h का उपयोग करने वाला cgrep 2,300 lines of C code का है, और इसमें 50 से अधिक manual free() calls हटाई गई हैं
  • वही assembly और execution speed बनाए रखते हुए memory leak और segfault के बिना सुरक्षित C code इम्प्लीमेंट किया गया है
  • C की सादगी और स्वतंत्रता बनाए रखते हुए modern safety जोड़ने का यह एक उदाहरण है
  • लेखक अगली पोस्ट में बताएगा कि cgrep, ripgrep से 2x से अधिक तेज़ और memory usage में 20x कम क्यों है
  • safe_c.h को नए projects के लिए उपयुक्त बताया गया है, लेकिन macro-आधारित होने के कारण debugging की कठिनाई बढ़ सकती है
  • विभिन्न static analyzers (GCC analyzer, ASAN, UBSAN, Clang-tidy आदि) के जरिए correctness और safety verification किया गया है

1 टिप्पणियां

 
GN⁺ 2025-11-19
Hacker News टिप्पणियाँ
  • यह लेख C में safe abstraction लागू करते समय आने वाली लागत की समस्या दिखाता है
    shared pointer का implementation POSIX mutex का उपयोग करता है, इसलिए (1) यह platform-independent नहीं है और (2) single-threaded स्थिति में भी mutex overhead देना पड़ता है
    यानी यह ‘zero-cost abstraction’ नहीं है
    C++ का shared_ptr भी इसी समस्या से जूझता है, लेकिन Rust इसे Rc और Arc दो प्रकारों में बाँटकर हल करता है

    • C++ का shared_ptr mutex नहीं बल्कि atomic operations का उपयोग करता है
      यह Rust के Arc जैसा है, और ब्लॉग का implementation बस अक्षम है
      हालांकि C++ में Rc के बराबर कोई type नहीं है, इसलिए अगर केवल simple reference-counting pointer चाहिए तो तब भी लागत आती है
    • glibc और libstdc++ वातावरण में अगर pthreads link नहीं किया गया हो तो shared_ptr thread-safe नहीं होता
      runtime पर pthread symbols ढूँढ़कर atomic या non-atomic path चुना जाता है
      मुझे तो लगता है कि हमेशा atomic का उपयोग करना बेहतर है
    • मुझे लगता है कि code को crash न होने देना कहीं ज़्यादा महत्वपूर्ण है
      cross-platform support ज़्यादातर मामलों में बस ‘अच्छा हो तो ठीक’ जैसी चीज़ है
      mutex overhead परेशान करता है, लेकिन modern CPU पर यह संभालने लायक है
      Rust शानदार है, यह मैं मानता हूँ, लेकिन C ecosystem इतना विशाल है कि उसे पूरी तरह replace करना मुश्किल है
    • mutex की जगह C11 atomic operations से reference count implement किया जा सकता है
      इस स्थिति में mutex के फायदे क्या हैं, यह मुझे स्पष्ट नहीं है
    • POSIX mutex पहले से कई platforms पर implement है, इसलिए मुझे यह ज़्यादा सामान्य API लगता है
  • Fil (aka pizlonator) द्वारा बनाया गया FUGC नाम का एक garbage collector project है जो C को memory-safe बनाने की कोशिश करता है
    इसे मौजूदा codebase में लगभग बिना बदलाव के लागू किया जा सकता है, और यह C/C++ को memory-safe language में बदल देता है
    संबंधित HN पोस्ट और official site देखें

    • इसी वजह से मुझे इस project के बारे में पहली बार पता चला। यह सच में एक शानदार प्रयास लगता है
    • लेकिन मैं garbage collector की performance penalty स्वीकार नहीं करना चाहता
  • मुझे लगता है कि यह लेख memory safety के मूल मुद्दे को कुछ हद तक गलत ढंग से पेश करता है
    local variables का automatic cleanup या bounds checking भर काफी नहीं है
    पूरे program की memory lifetime management ही असली समस्या है
    उदाहरण के लिए, UniquePtr return करते समय या SharedPtr copy करते समय reference count बढ़ाना भूल तो नहीं रहे, intrusive list के elements की lifetime कौन manage करेगा, आदि
    अंततः मुझे यह तरीका पुराने #define xfree(p) pattern से बहुत अलग नहीं लगता

    • UniquePtr संभव है क्योंकि struct को value के रूप में return किया जा सकता है
      लेकिन SharedPtr copy reference count बढ़ाने का काम अपने-आप नहीं करती
    • मैं जानना चाहता हूँ कि #define xfree(p) pattern खराब क्यों माना जाता है
  • कहा गया है कि C23 ने [[cleanup]] attribute पेश किया है, लेकिन वास्तव में यह GCC extension है और इसे [[gnu::cleanup()]] के रूप में लिखना पड़ता है
    example code देखें

    • इस बारे में जानकारी ढूँढ़ना मुश्किल था, और अंत में लगता है कि सिर्फ syntax बदला है, feature अभी भी extension ही है
  • एक मज़ाक था: “C++: देखो, दूसरी भाषाओं को मेरी शक्ति का थोड़ा-सा हिस्सा नकल करने के लिए भी कितनी मेहनत करनी पड़ती है”
    macro से C++ की नकल क्यों की जा रही है, यह समझ नहीं आता, लेकिन फिर भी यह एक दिलचस्प प्रयास है

    • C++ की सारी सुविधाएँ डाले बिना ज़्यादा सुरक्षित C बनाने की प्रक्रिया दिलचस्प लगी
      लेकिन आखिर में जब C++17 की सुविधाओं तक की नकल की जा रही है, तो लगता है कि सीधे C++ का उपयोग करना बेहतर नहीं होगा?
    • मुझे एक parse की जा सकने वाली language चाहिए
      C अब भी संभालना आसान है, लेकिन C++ इतना जटिल है कि frontend के बिना उसके पास जाना मुश्किल है
    • C सरल है, इसलिए hack करने के लिए अच्छी language है
      C++ पर जाने के बाद build chain, name mangling, libstdc++ dependency जैसी चीज़ों से जटिलता बढ़ जाती है
    • यह project C++ की कुछ ही features की अनुमति देकर सीमित syntax लागू कर सकता है
      जबकि C++ को C style में लिखने पर ऐसी पाबंदियाँ नहीं लगतीं
    • embedded CPU vendors द्वारा C++ compiler न देना भी एक व्यावहारिक बाधा है
  • यह setjmp/longjmp आधारित exception handling के साथ compatible नहीं है
    इसके बजाय POSIX के pthread_cleanup_push से प्रेरित cleanup macro pair के साथ इसे integrate किया जा सकता है
    cleanup_push(fn, type, ptr, init) और cleanup_pop(ptr) का उपयोग करके stack-based cleanup routine implement की जाती है
    इस तरीके का फायदा यह है कि यह compile time पर balancing errors पकड़ सकता है

  • इसे safeclib के असली safec.h के साथ भ्रमित नहीं करना चाहिए
    safeclib header देखें

    • मुझे समझ नहीं आता कि कोई Annex K implementation को maintain क्यों करना चाहेगा
      global constraint handler की वजह से इसे design failure माना जाता है, और अधिकतर toolchains इसका समर्थन भी नहीं करते
      संबंधित दस्तावेज़ देखें
  • अगर आप Nim language का उपयोग करें, तो safe_c.h जो सुविधाएँ देता है वे सब मिल सकती हैं
    Nim, C में compile होती है और safety और performance दोनों देती है
    ARC-आधारित automatic reference counting, defer, Option[T], bounds-checking, likely/unlikely जैसी कई सुविधाएँ यह default रूप से देती है
    official site, ARC परिचय, view types, Option docs, likely template देखें

  • अगर इस approach का लक्ष्य portability है, तो व्यवहारिक रूप से C99 पर टिके रहना ही सुरक्षित है
    MSVC का C compiler मुश्किल है, लेकिन cross-platform support के लिए लगभग अनिवार्य है
    मैंने भी ऐसा ही एक header बनाया था, लेकिन portability issues की वजह से cleanup utilities शामिल नहीं कीं

    • अगर macros से C++ code (destructor-based) generate कराया जाए, तो cleanup attribute के बिना भी यह संभव है
      अगर C code, C++ में भी compile हो सके तो यह अच्छी तरह काम करेगा
    • Windows पर भी MSYS2 + GCC के साथ आराम से development किया जा सकता है
      package manager भी साथ में मिलता है
    • जानकारी के लिए, MSVC अब C17 support करता है
  • लेख में कई बार आए cgrep के code का link नहीं दिया गया है
    GitHub पर इसी नाम के कई projects हैं, लेकिन ज़्यादातर दूसरी languages में लिखे गए हैं

    • मुझे भी नहीं पता कि किस cgrep की बात हो रही है, और मैं इसे खुद आज़माना चाहूँगा