3

I have read many blogs and stack exchange posts about global state (usually) being bad practice. I'm now trying to avoid this where possible in my code, but I'm running into a case where I don't know how to avoid global state:

Suppose I have a task which handles events. If I want to count the number of events it processes and I want to have this counter available to show via some sort of debugging channel, how can I implement this counter without using global state?

(My specific case uses vxWorks tasks.)

2 Answers 2

2

There are different aspects why global state might be problematic.

If you are concerned about coupling, testability, and data flows, you can move the global state into an argument. So instead of:

State s;

void some_function(void) { ... }

int main() {
  some_function();
}

You might have:

void some_function(State* s) { ... }


int main() {
  State s;
  some_function(&s);
}

If you are concerned about concurrency issues that arise with shared state (not just global state), then using atomics for the counter could be appropriate.

My specific case uses vxWorks tasks.

That potentially changes things.

Many software engineering best practices are aimed at large systems where you only control individual components rather than an entire application or system, and where it is often reasonable to trade some degree of performance for better design choices (e.g. using OOP techniques to decouple components).

But such best practices and techniques are frequently inapplicable in an embedded context. The previously suggested technique of moving state into a function argument is great if you want to support multiple states, for example multiple instances of some application. This is often not a concern in an embedded context. Using global variables is also entirely appropriate for embedded code if they describe the state of the device, because that is the most accurate reflection of reality.

Personally, if resources of the target platform permit, I'd still tend to the “cleaner” design of avoiding global state. I would try to design functions that are responsible for collecting and aggregating this monitoring/debugging/observability data, and pass the necessary data around as an argument. This would also allow you to toggle the diagnostics data collection at runtime. However, if this causes problems – or simply if keeping track of those extra parameters throughout your code base is more annoying than the problems posed by global state – then I'd happily use global variables instead.

2

To be clear what makes global variables bad is that you lose control of access and tracing who's changing it quickly becomes difficult.

What makes global state difficult is different. You can keep stuff in global state without giving everything access to it. But suppose today you have one task handling events and tomorrow it needs to be two tasks handling events. Global state prevents you from creating more than one task cleanly. It isn't that global state is bad. It has many good uses. It's that putting things that don't really belong there can cause difficulty later.

The object oriented way to accomplish your objective is to have a task object. One that keeps it's own counter of events processed as an instance field. The task object could be configured to talk to your debugging channel when you construct it.

So where are you going to keep these task objects? Well you could just put them in the global state and build them in main().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.