Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Thread locals don't fully solve the problem. They work well if you immediately call the closure, but what if you want to store the closure and call it later?

    #include <stdlib.h>
    #include <string.h>
    #include <stddef.h>

    typedef int (*comp)(const void *untyped_left, const void *untyped_right);

    thread_local int in_reverse = 0;

    __attribute__((noinline))
    int compare_impl(const void *untyped_left, const void *untyped_right, int in_reverse) {
        const int* left = untyped_left;
        const int* right = untyped_right;
        return (in_reverse) ? *right - *left : *left - *right;
    }

    comp make_sort(int direction) {
        in_reverse = direction;
        int compare(const void *untyped_left, const void *untyped_right) {
            return compare_impl(untyped_left, untyped_right, in_reverse);
        }
        return compare;
    }

    int main(int argc, char* argv[]) {

        int list[] = { 2, 11, 32, 49, 57, 20, 110, 203 };

        comp normal_sort = make_sort(0);
        comp reverse_sort = make_sort(1);

        qsort(list, (sizeof(list)/sizeof(*list)), sizeof(*list), normal_sort);
            
        return list[0];
    }
Because we create `reverse_sort` between creating `normal_sort` and calling it, we end up with a reverse sort despite clearly asking for a normal sort.




The answer is you wrap it and you don’t return until the thing stored in the thread local is not needed

So we can't return the closure?

Then it's clearly only half a solution.

The example I gave above should work fine in any language with first-class closures.


> The closure problem can be neatly described by as “how do I get extra data to use within this qsort call?”

    _Thread_local struct {
      void *data;
      int (*compare)(const void *a, const void*, void*);
    } _qsort2_closure ; 

    static int _qsort2_helper(const void *a, const void *b) {
        return _qsort2_closure.compare(a, b, _qsort2_closure.data);
    }

    void qsort2(void *base, size_t elements, size_t width, int (*compare)(const void *a, const void*, void*), void *userData) 
    {
        _qsort2_closure.data = userData;
        _qsort2_closure.compare = compare;
        qsort(base, elements, width, _qsort2_helper);
    }

you also need to restore the _qsort2_closure when done. But again you are reinventing dynamic scoping with all its advantages and disadvantages.

This is the classic lexical vs dynamic scoping. Dynamic scoping works great until it doesn't.



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: