Are C++ exceptions sufficient to implement thread-local storage?

瘦欲@ 提交于 2019-11-28 07:18:49

In the playful spirit of the question, I offer this horrifying nightmare creation:

class tls
{
    void push(void *ptr)
    {
        // allocate a string to store the hex ptr 
        // and the hex of its own address
        char *str = new char[100];
        sprintf(str, " |%x|%x", ptr, str);
        strtok(str, "|");
    }

    template <class Ptr>
    Ptr *next()
    {
        // retrieve the next pointer token
        return reinterpret_cast<Ptr *>(strtoul(strtok(0, "|"), 0, 16));
    }

    void *pop()
    {
        // retrieve (and forget) a previously stored pointer
        void *ptr = next<void>();
        delete[] next<char>();
        return ptr;
    }

    // private constructor/destructor
    tls() { push(0); }
    ~tls() { pop(); }

public:
    static tls &singleton()
    {
        static tls i;
        return i;
    }

    void *set(void *ptr)
    {
        void *old = pop();
        push(ptr);
        return old;
    }

    void *get()
    {
        // forget and restore on each access
        void *ptr = pop();
        push(ptr);
        return ptr;
    }
};

Taking advantage of the fact that according to the C++ standard, strtok stashes its first argument so that subsequent calls can pass 0 to retrieve further tokens from the same string, so therefore in a thread-aware implementation it must be using TLS.

example *e = new example;

tls::singleton().set(e);

example *e2 = reinterpret_cast<example *>(tls::singleton().get());

So as long as strtok is not used in the intended way anywhere else in the program, we have another spare TLS slot.

I think you're onto something here. This might even be a portable way to get data into callbacks that don't accept a user "state" variable, as you've mentioned, even apart from any explicit use of threads.

So it sounds like you've answered the question in your subject: YES.

void *kid( void *local_v ) try {
    thlocal &local = * static_cast< thlocal * >( local_v );
    throw local;
} catch( thlocal & ) {
    print_thread();

    return NULL;
}

==

void *kid (void *local_v ) { print_thread(local_v); }

I might be missing something here, but it's not a thread local storage, just unnecessarily complicated argument passing. Argument is different for each thread only because it is passed to pthread_create, not because of any exception juggling.


It turned out that I indeed was missing that GCC is producing actual thread local storage calls in this example. It actually makes the issue interesting. I'm still not quite sure whether it is a case for other compilers, and how is it different from calling thread storage directly.

I still stand by my general argument that the same data can be accessed in a more simple and straight-forward way, be it arguments, stack walking or thread local storage.

Accessing data on the current function call stack is always thread safe. That's why your code is thread safe, not because of the clever use of exceptions. Thread local storage allows us to store per-thread data and reference it outside of the immediate call stack.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!