4

Imagine a project with the development stretched over 10+ years timespan. Some parts are in C, some are in C++ and all of the code uses global functions and global variables. The architecture was designed inherently single threaded and kept growing that way. But now we consider utilizing many-core architectures.

Now one idea being evaluated is to refactor a part of the code into a library, to make it possible to create more than one instance, so that they can run in separate threads and don’t interfere with each other.

The proposal that gains the most traction at this point is to wrap all the library files into namespaces with macro defines, like:

namespace VARIANT {
    // all the code
}

Then define the VARIANT in a header or on project level. This will make it possible to have different contexts within different namespaces. And the selling point is that this approach will require minimal code change and has low risk of introducing any regression.

But if at some point we need to make the behavior of Variant1 different from Variant2, things will get tricky, since there’s no way to compare the value of a macro define with a string in a preprocessor macro.

Is there a more elegant way to achieve this?

nVxx
  • 661
  • 7
  • 20
  • 3
    The elegant way is to refactor the whole thing not to use globals, but you know this already ;) – Lightness Races in Orbit Mar 05 '18 at 10:16
  • I'm not sure if the namespaces are "elegant" at all - you would have to convert your C files into C++ files (C does not support namespaces!) and you would have to re-compile the entire library for every new thread using it! – Aconcagua Mar 05 '18 at 10:27

3 Answers3

4

Another variant might be spotting all global variables and making them thread_local. Requires either C++11 or at least compiler extensions providing the same (__thread using older GCC).

If I read this question right, you even don't need to convert your C files into C++ files (which your approach requires as C does not support namespaces...), but you need C11 for.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • This seems to be a good alternative. Certainly a comprehensive strategy for refactoring will be broader than this, but `thread_local` provides similar per-thread separation as a `namspace`, only in a cleaner way. – nVxx Mar 06 '18 at 12:55
1

Refactoring an old project and make it multithreading is not so simple. First of all you have mixture of C and C++ codes and you cannot blindly follow C++ approach here. Instead of namespace you need to thing on the below areas:-

  1. Find out all the code blocks, container like list, large array of objects etc which need synchronization.
  2. Find out interdependency of threads and how will you control them. For example one thread will generate a report and insert into a table and second one need that information to generate its final report, now you need to find out these kind of dependency among the threads in your code base and need to find out their control mechanism.
  3. Old style of multithreading in C++ was very tricky hence you need to migrate your code to C++11 where implementation of multithreading is much easier.

  4. As you said that in your current project there are lots of global variable, you need to think properly how you are going to share these variables amongst different threads and how will you synchronize access of these variables.

These are some hints you need to consider lots of areas in advance before starting refactoring else all your efforts end in smoke.

GOOD LUCK for your plan.

Abhijit Pritam Dutta
  • 5,521
  • 2
  • 11
  • 17
1

Just do it in steps, testing each time:

1) typedef a struct with all the globals in it. malloc one, and edit the existing code to reference it. Test - should work exactly the same as with the globals.

2) Create one thread to run one instance of the code. Test - should work exactly the same as with the globals.

3) Try multiple threads.

One step at a time...

Please try very hard to not attempt any bodges!

Martin James
  • 24,453
  • 3
  • 36
  • 60