RAII in C

Joe Linhoff

In my last post, I talked about resource management, and how RAII, Resource Acquisition is Initialization, is important in C++ because of the guarantees it provides. In this post, I’ll show how we can make the same guarantees in C.

In C++, RAII provides a solution to resource management and the issues arising with exceptions. Although C doesn’t support exceptions, many of the same challenges, problems, and motivation for proper resource management are present in C. Leaking or conflicted resource management will at best hurt performance, and may crash your application.

In C, we write our resource-management code in such a way as to guarantee complete execution of an initialization and finalization step. For the purposes of this idiom, functions start with initialization code, run through body code, and then end with finalization code.

Compared with C++, we can simplify our work by handling all resources as dynamic or transient resources — what we called case 3, later allocation, in the previous post.

The C implementation is further simplified because we don’t have exceptions. We can guarantee execution will run through all the initialization and finalization code we write.

There are three distinct parts to this pattern: initialization, body, and finalization.

Initialization

Near the top of a C function that implements the RAII pattern, we start with initialization code. Initialization code must follow a simple rule that all initialization code is guaranteed to execute. There are no early returns, nor are there gotos or constructs that skip initialization code.

The best way to handle this is to write initialization code that cannot fail, and simply clears resource references. Code that could fail, like allocations or file-opening, are best moved into the body. If the initialization code does run into a failure, a failure result should be returned, but that failure must not prevent other initialization code from running.

For example, if we have three resources, A, B, and C, the preferred initialization code would only clear the references to each, and postpone allocation of the resource until later. Further, if an error condition was detected during the initialization of A, we must continue with the initialization of B and C.

When working with multiple resources, this rule guarantees that all resource references are initialized.

Body

The body code follows initialization. Whereas the initialization and finalization steps are guaranteed to run on entry and exit, the body isn’t so linear. Execution may run through the body in multiple paths. The only requirement is that the finalization code is always called after the body  is run—so the body cannot return out of the function.

Your resources dictate how your body handles them. Where some resources may be dynamic or transient, and require code to check for validity before use, other resources remain valid after their allocation or binding.

For example, a network connection is considered transient, and may be lost and re-connected at any time. Your body code must work with the initialization and finalization code to manage the resource.

Another example, a memory allocation is dynamic and may fail at allocation, but will remain valid until freed.

The body code will often start with startup or setup code. This code runs through attempted memory and resource allocations. If any of the allocations fail, the body code may jump directly to the finalization.

Finalization

Like initialization, finalization is guaranteed to run. As the C++ RAII destructor balanced the constructor, design your C finalization code to balance the initialization.

Write your finalization code to check to see if the resource reference is valid, and release the resource if it is valid.

This code will handle any path the execution took through the body, including skipping the body completely.

Architecture

Granularity

In C++, RAII-objects are setup to handle one resource per object; necessary with exceptions. In C, we have more freedom, since we are guaranteed complete initialization and finalization. Write your C systems at a similar granularity – don’t overload a single system with too many resources.

A C programmer will benefit from thinking a bit like a C++ programmer when designing resource access: encapsulate the resource initialization, finalization, and access functions in an object-like way.

For example, the interface to a configuration resource may include:

typedef struct { /* config fields */ } Config;

void ConfigInit(Config *cfg); // initialization

void ConfigFinal(Config *cfg); // finalization

int ConfigProcess(Config *cfg,const char *path); // read into cfg

Order Of Initialization

Avoid order-dependent initialization code. Code with fewer dependencies is easier to reuse. Where there are dependencies, for example, initialization of the memory system, comment that in your code.

Even when your code is order-independent, it’s a good idea to set up the initialization and finalization in stack-based order: That is, you should release resources in the reverse order they were acquired during the initialization step. Releasing in reverse order is good practice as it unwinds the top layer dependencies first, child systems before parent systems, and the base systems last.

Structure 1 shows an example of the pattern that sets up the initialization phase with calls to system initializations, a call to the body function, and then the finalization phase, with calls to system finalizations.

 

=== STRUCTURE 1 ===

 

// INIT

MemoryInit(); // must be first

FileSystemInit();

SerialPortInit();

 

// BODY

Body();

 

// FINAL

SerialPortFinal();

FileSystemFinal();

MemoryFinal();

Setup / Start

For many systems, more work has to be done after initialization and before use. This work includes actions like allocating buffers, opening files, scanning for, and locating system resources.

Because all of these actions may fail, they may not be written into the initialization code. Failure of some of these actions may make running the program infeasible. Other failures may be recoverable.

This setup / start code needs to be completed before the program may drop into its main loop, and is written as the first part of the body code, after the initialization.

Structure 2 shows use of a goto that jumps to the finalization phase. This limited use of goto guarantees we run the finalization code when we run into failures in the body. For these examples, a negative return signals an error.

 

=== STRUCTURE 2 ==

 

// INIT

MemoryInit(); // must be first

FileSystemInit();

SerialPortInit();

 

// BODY-STARTUP

if(ReadAllConfigFiles()<0)

 goto FINAL;

if(OpenDebugUART()<0)

 goto FINAL;

if(AllocateBuffers()<0)

 goto FINAL;

 

// BODY-MAIN

ApplicationBody();

 

FINAL: // FINAL

 SerialPortFinal();

 FileSystemFinal();

 MemoryFinal();

Summary

RAII is an important C++ idiom for resource management. Notably, RAII provides a structural idiom for proper resource management with exceptions.

The power of the idiom is in the guarantees it provides. Properly used, the destructor for your RAII-object is guaranteed to be called to allow you to free resources.

In a future post, I’ll compare the code and compiler output from the C++ and C approaches to RAII.

Categories: RAII

Cardinal Peak
Learn more about our Audio & Video capabilities.

Dive deeper into our IoT portfolio

Take a look at the clients we have helped.

We’re always looking for top talent, check out our current openings. 

Contact Us

Please fill out the contact form below and our engineering services team will be in touch soon.

fake yeezys
We rely on Cardinal Peak for their ability to bolster our patent licensing efforts with in-depth technical guidance. They have deep expertise and they’re easy to work with.
Diego deGarrido Sr. Manager, LSI
Cardinal Peak has a strong technology portfolio that has complemented our own expertise well. They are communicative, drive toward results quickly, and understand the appropriate level of documentation it takes to effectively convey their work. In…
Jason Damori Director of Engineering, Biamp Systems
We asked Cardinal Peak to take ownership for an important subsystem, and they completed a very high quality deliverable on time.
Matt Cowan Chief Scientific Officer, RealD
Cardinal Peak’s personnel worked side-by-side with our own engineers and engineers from other companies on several of our key projects. The Cardinal Peak staff has consistently provided a level of professionalism and technical expertise that we…
Sherisse Hawkins VP Software Development, Time Warner Cable
Cardinal Peak was a natural choice for us. They were able to develop a high-quality product, based in part on open source, and in part on intellectual property they had already developed, all for a very effective price.
Bruce Webber VP Engineering, VBrick
We completely trust Cardinal Peak to advise us on technology strategy, as well as to implement it. They are a dependable partner that ultimately makes us more competitive in the marketplace.
Brian Brown President and CEO, Decatur Electronics
The Cardinal Peak team started quickly and delivered high-quality results, and they worked really well with our own engineering team.
Charles Corbalis VP Engineering, RGB Networks
We found Cardinal Peak’s team to be very knowledgeable about embedded video delivery systems. Their ability to deliver working solutions on time—combined with excellent project management skills—helped bring success not only to the product…
Ralph Schmitt VP, Product Marketing and Engineering, Kustom Signals
Cardinal Peak has provided deep technical insights, and they’ve allowed us to complete some really hard projects quickly. We are big fans of their team.
Scott Garlington VP Engineering, xG Technology
We’ve used Cardinal Peak on several projects. They have a very capable engineering team. They’re a great resource.
Greg Read Senior Program Manager, Symmetricom
Cardinal Peak has proven to be a trusted and flexible partner who has helped Harmonic to deliver reliably on our commitments to our own customers. The team at Cardinal Peak was responsive to our needs and delivered high quality results.
Alex Derecho VP Professional Services, Harmonic
Yonder Music was an excellent collaboration with Cardinal Peak. Combining our experience with the music industry and target music market, with Cardinal Peak’s technical expertise, the product has made the mobile experience of Yonder as powerful as…
Adam Kidron founder and CEO, Yonder Music
The Cardinal Peak team played an invaluable role in helping us get our first Internet of Things product to market quickly. They were up to speed in no time and provided all of the technical expertise we lacked. They interfaced seamlessly with our i…
Kevin Leadford Vice President of Innovation, Acuity Brands Lighting
We asked Cardinal Peak to help us address a number of open items related to programming our systems in production. Their engineers have a wealth of experience in IoT and embedded fields, and they helped us quickly and diligently. I’d definitely…
Ryan Margoles Founder and CTO, notion