0

I have three threads in an application I'm building, all of which remain open for the lifetime of the application. Several variables and functions should only be accessed from specific threads. In my debug compile, I'd like a check to be run and an error to be thrown if one of these functions or variables is accessed from an illegal thread, but I don't want this as overhead in my final compilation. I really just want this so I the programmer don't make stupid mistakes, not to protect my executing program from making mistakes.

Originally, I had a 'thread protected' class template that would wrap around return types for functions, and run a check on construction before implicitly converting to the intended return type, but this didn't seem to work for void return types without disabling important warnings, and it didn't resolve my issue for protected variables.

Is there a method of doing this, or is it outside the scope of the language? 'If you need this solution, you're doing it wrong' comments not appreciated, I managed to near halve my program's execution time with this methodology, but it's just too likely I'm going to make a mistake that results in a silent race condition and ultimately undefined behavior.

TechNeko
  • 35
  • 5
  • static_assert seems the solution to me. You can give your threads IDs and when accessing the variables you check via static_assert. It shouldnt be overhead after compile time – Narase Jun 19 '19 at 06:04
  • @Narase can you give some example code? Short of setters/getters I'm not where sure to actually PUT the static_assert, though I do agree it's going to play at least some role in this. – TechNeko Jun 19 '19 at 06:08
  • 2
    Can you provide an example of what you're doing? That makes answering much easier. –  Jun 19 '19 at 06:14
  • 2
    "_I'd like a check to be run and an error to be thrown if one of these functions or variables is accessed from an illegal thread,_" - Why not just make the variables inaccessible from other threads? No need to check, no overhead. Wrap the thread object in a class and add private members to the class. Example: https://stackoverflow.com/questions/56588075/how-to-extract-taskidtid-of-a-pthread-from-the-parent-thread/56588190#56588190 – Ted Lyngmo Jun 19 '19 at 06:20
  • @TechNeko Put a wrapper around them like "template ProtectedVariable" with the value inside via "T& get(int id)". It checks the id via static_assert and when no expection is thrown it returns the reference. Since the static_assert is only evaluated at compile time, Im pretty sure the optimizer will throw the whole thing away after compiling – Narase Jun 19 '19 at 06:23
  • @Narase `static_assert` won't work here because the condition to be check is a runtime condition (e.g. Which thread is running this). – Humphrey Winnebago Jun 22 '19 at 19:11

1 Answers1

0

What you described is exactly what assert macro is for.

assert(condition)

In a debug build condition is checked. If it is false, the program will throw an exception at that line. In a release build, the assert and whatever is inside the parentheses aren't compiled.

Without being harsh, it would have been more helpful if you had explained the variables you are trying to protect. What type are they? Where do they come from? What's their lifetime? Are they global? Why do you need to protect a returned type if it's void? How did you end up in a situation where one thread might accidentally access something. I kinda have to guess but I'll throw out some ideas here:

#include <thread>
#include <cassert>


void protectedFunction()
{
    assert(std::this_thread::get_id() == g_thread1.get_id());
}

// protect a global singleton (full program lifetime)
std::string& protectedGlobalString()
{
    static std::string inst;
    assert(std::this_thread::get_id() == g_thread1.get_id());
    return inst;
}

// protect a class member
int SomeClass::protectedInt()
{
    assert(std::this_thread::get_id() == g_thread1.get_id());
    return this->m_theVar;
}

// thread protected wrapper
template <typename T>
class ThreadProtected
{
    std::thread::id m_expected;
    T               m_val;
public:
    ThreadProtected(T init, std::thread::id expected)
        : m_val(init), m_expected(expected)
    { }

    T Get()
    {
        assert(std::this_thread::get_id() == m_expected);
        return m_val;
    }
};

// specialization for void
template <>
class ThreadProtected<void>
{
public:
    ThreadProtected(std::thread::id expected)
    {
        assert(std::this_thread::get_id() == expected);
    }
};

assert is oldschool. We were actually told to stop using it at work because it was causing resource leaks (the exception was being caught high up in the stack). It has the potential to cause debugging headaches because the debug behavior is different from the release behavior. A lot of the time if the asserted condition is false, there isn't really a good choice of what to do; you usually don't want to continue running the function but you also don't know what value to return. assert is still very useful when developing code. I personally use assert all the time.

static_assert will not help here because the condition you are checking for (e.g. "Which thread is running this code?") is a runtime condition.

Another note:

Don't put things that you want to be compiled inside an assert. It seems obvious now, but it's easy to do something dumb like

int* p;
assert(p = new(nothrow) int);  // check that `new` returns a value -- BAD!!

It's good to check the allocation of new, but the allocation won't happen in a release build, and you won't even notice until you start release testing!

int* p;
p = new(nothrow) int;
assert(p);  // check that `new` returns a value -- BETTER...

Lastly, if you write the protected accessor functions in a class body or in a .h, you can goad the compiler into inlining them.


Update to address the question:

The real question though is where do I PUT an assert macro? Is a requirement that I write setters and getters for all my thread protected variables then declare them as inline and hope they get optimised out in the final release?

You said there are variables that should be checked (in the debug build only) when accessed to make sure the correct thread is accessing them. So, theoretically, you would want an assert macro before every such access. This is easy if there are only a few places (if this is the the case, you can ignore everything I'm about to say). However, if there are so many places that it starts to violate the DRY Principal, I suggest writing getters/setters and putting the assert inside (This is what I've casually given examples of above). But while the assert won't add overhead in release mode (since it's conditionally compiled), using extra functions (probably) adds function call overhead. However, if you write them in the .h, there's a good chance they'll be inlined.

Your requirement for me was to come up with a way to do this without release overhead. Now that I've mentioned inlining I'm obligated to say that the compiler knows best. There usually are compiler-specific ways to force inlining (since the compiler is allowed to ignore the inline keyword). You should be profiling the code before trying to inline things. See the answer to this question. Is it good practice to make getters and setters inline?. You can easily see if the compiler is inlining the function by looking at the assembly. Don't worry, you don't have to be good at assembly. Just find the calling function and look for a call to the getter/setter. If the function was inlined, you won't see a call and you'll see probably a mov instead.

Humphrey Winnebago
  • 1,512
  • 8
  • 15
  • I appreciate the information, certainly helpful. The real question though is where do I PUT an assert macro? Is a requirement that I write setters and getters for all my thread protected variables then declare them as inline and hope they get optimised out in the final release? – TechNeko Jun 25 '19 at 03:30
  • I have updated the answer. Hopefully it answer these questions – Humphrey Winnebago Jun 26 '19 at 06:50