home.social

#undef — Public Fediverse posts

Live and recent posts from across the Fediverse tagged #undef, aggregated by home.social.

  1. sys/arch/m68k/include: asm.h

    christos: Make the _C_LABEL definition match exactly <sys/cdefs_elf.h> because
    atomic_cas.S now includes <sys/ras.h> and <sys/ras.h> includes <sys/cdefs.h>
    because it wants things like __CONCAT(). I think it is better to do it this
    way rather than #undef here or in atomic_cas.S because then if the definition
    changes it will break again.

    cvsweb.netbsd.org/bsdweb.cgi/s

  2. sys/arch/m68k/include: asm.h

    christos: Make the _C_LABEL definition match exactly <sys/cdefs_elf.h> because
    atomic_cas.S now includes <sys/ras.h> and <sys/ras.h> includes <sys/cdefs.h>
    because it wants things like __CONCAT(). I think it is better to do it this
    way rather than here or in atomic_cas.S because then if the definition
    changes it will break again.

    cvsweb.netbsd.org/bsdweb.cgi/s

  3. Oh for heck's sake.

    #undef Status
    #undef Success

    This at the top of the file fixes the problem. I'm in #define hell and I don't know why. :(

  4. Oh for heck's sake.

    #undef Status
    #undef Success

    This at the top of the file fixes the problem. I'm in #define hell and I don't know why. :(

  5. Oh for heck's sake.

    #undef Status
    #undef Success

    This at the top of the file fixes the problem. I'm in #define hell and I don't know why. :(

  6. Oh for heck's sake.

    #undef Status
    #undef Success

    This at the top of the file fixes the problem. I'm in #define hell and I don't know why. :(

  7. Oh for heck's sake.

    #undef Status
    #undef Success

    This at the top of the file fixes the problem. I'm in #define hell and I don't know why. :(

  8. As it happens, we still use CVS in our operating system project (there are reasons for doing this, but migration to git would indeed make sense).

    While working on our project, we occasionally have to do a full checkout of the whole codebase, which is several gigabytes. Over time, this operation has gotten very, very, very slow - I mean "2+ hours to perform a checkout" slow.

    This was getting quite ridiculous. Even though it's CVS, it shouldn't crawl like this. A quick build of CVS with debug symbols and sampling the "cvs server" process with Linux perf showed something peculiar: The code was spending the majority of the time inside one function.

    So what is this get_memnode() function? Turns out this is a support function from Gnulib that enables page-aligned memory allocations. (NOTE: I have no clue why CVS thinks doing page-aligned allocations is beneficial here - but here we are.)

    The code in question has support for three different backend allocators:
    1. mmap
    2. posix_memalign
    3. malloc

    Sounds nice, except that both 1 and 3 use a linked list to track the allocations. The get_memnode() function is called when deallocating memory to find out the original pointer to pass to the backend deallocation function: The node search code appears as:

    for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
    if (c->aligned_ptr == aligned_ptr)
    break;

    The get_memnode() function is called from pagealign_free():

    #if HAVE_MMAP
    if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
    error (EXIT_FAILURE, errno, "Failed to unmap memory");
    #elif HAVE_POSIX_MEMALIGN
    free (aligned_ptr);
    #else
    free (get_memnode (aligned_ptr));
    #endif

    This is an O(n) operation. CVS must be allocating a huge number of small allocations, which will result in it spending most of the CPU time in get_memnode() trying to find the node to remove from the list.

    Why should we care? This is "just CVS" after all. Well, Gnulib is used in a lot of projects, not just CVS. While pagealign_alloc() is likely not the most used functionality, it can still end up hurting performance in many places.

    The obvious easy fix is to prefer the posix_memalign method over the other options (I quickly made this happen for my personal CVS build by adding tactical #undef HAVE_MMAP). Even better, the list code should be replaced with something more sensible. In fact, there is no need to store the original pointer in a list; a better solution is to allocate enough memory and store the pointer before the calculated aligned pointer. This way, the original pointer can be fetched from the negative offset of the pointer passed to pagealign_free(). This way, it will be O(1).

    I tried to report this to the Gnulib project, but I have trouble reaching gnu.org services currently. I'll be sure to do that once things recover.

    #opensource #development #bugstories

  9. As it happens, we still use CVS in our operating system project (there are reasons for doing this, but migration to git would indeed make sense).

    While working on our project, we occasionally have to do a full checkout of the whole codebase, which is several gigabytes. Over time, this operation has gotten very, very, very slow - I mean "2+ hours to perform a checkout" slow.

    This was getting quite ridiculous. Even though it's CVS, it shouldn't crawl like this. A quick build of CVS with debug symbols and sampling the "cvs server" process with Linux perf showed something peculiar: The code was spending the majority of the time inside one function.

    So what is this get_memnode() function? Turns out this is a support function from Gnulib that enables page-aligned memory allocations. (NOTE: I have no clue why CVS thinks doing page-aligned allocations is beneficial here - but here we are.)

    The code in question has support for three different backend allocators:
    1. mmap
    2. posix_memalign
    3. malloc

    Sounds nice, except that both 1 and 3 use a linked list to track the allocations. The get_memnode() function is called when deallocating memory to find out the original pointer to pass to the backend deallocation function: The node search code appears as:

    for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
    if (c->aligned_ptr == aligned_ptr)
    break;

    The get_memnode() function is called from pagealign_free():

    #if HAVE_MMAP
    if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
    error (EXIT_FAILURE, errno, "Failed to unmap memory");
    #elif HAVE_POSIX_MEMALIGN
    free (aligned_ptr);
    #else
    free (get_memnode (aligned_ptr));
    #endif

    This is an O(n) operation. CVS must be allocating a huge number of small allocations, which will result in it spending most of the CPU time in get_memnode() trying to find the node to remove from the list.

    Why should we care? This is "just CVS" after all. Well, Gnulib is used in a lot of projects, not just CVS. While pagealign_alloc() is likely not the most used functionality, it can still end up hurting performance in many places.

    The obvious easy fix is to prefer the posix_memalign method over the other options (I quickly made this happen for my personal CVS build by adding tactical #undef HAVE_MMAP). Even better, the list code should be replaced with something more sensible. In fact, there is no need to store the original pointer in a list; a better solution is to allocate enough memory and store the pointer before the calculated aligned pointer. This way, the original pointer can be fetched from the negative offset of the pointer passed to pagealign_free(). This way, it will be O(1).

    I tried to report this to the Gnulib project, but I have trouble reaching gnu.org services currently. I'll be sure to do that once things recover.

    #opensource #development #bugstories

  10. As it happens, we still use CVS in our operating system project (there are reasons for doing this, but migration to git would indeed make sense).

    While working on our project, we occasionally have to do a full checkout of the whole codebase, which is several gigabytes. Over time, this operation has gotten very, very, very slow - I mean "2+ hours to perform a checkout" slow.

    This was getting quite ridiculous. Even though it's CVS, it shouldn't crawl like this. A quick build of CVS with debug symbols and sampling the "cvs server" process with Linux perf showed something peculiar: The code was spending the majority of the time inside one function.

    So what is this get_memnode() function? Turns out this is a support function from Gnulib that enables page-aligned memory allocations. (NOTE: I have no clue why CVS thinks doing page-aligned allocations is beneficial here - but here we are.)

    The code in question has support for three different backend allocators:
    1. mmap
    2. posix_memalign
    3. malloc

    Sounds nice, except that both 1 and 3 use a linked list to track the allocations. The get_memnode() function is called when deallocating memory to find out the original pointer to pass to the backend deallocation function: The node search code appears as:

    for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
    if (c->aligned_ptr == aligned_ptr)
    break;

    The get_memnode() function is called from pagealign_free():

    #if HAVE_MMAP
    if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
    error (EXIT_FAILURE, errno, "Failed to unmap memory");
    #elif HAVE_POSIX_MEMALIGN
    free (aligned_ptr);
    #else
    free (get_memnode (aligned_ptr));
    #endif

    This is an O(n) operation. CVS must be allocating a huge number of small allocations, which will result in it spending most of the CPU time in get_memnode() trying to find the node to remove from the list.

    Why should we care? This is "just CVS" after all. Well, Gnulib is used in a lot of projects, not just CVS. While pagealign_alloc() is likely not the most used functionality, it can still end up hurting performance in many places.

    The obvious easy fix is to prefer the posix_memalign method over the other options (I quickly made this happen for my personal CVS build by adding tactical #undef HAVE_MMAP). Even better, the list code should be replaced with something more sensible. In fact, there is no need to store the original pointer in a list; a better solution is to allocate enough memory and store the pointer before the calculated aligned pointer. This way, the original pointer can be fetched from the negative offset of the pointer passed to pagealign_free(). This way, it will be O(1).

    I tried to report this to the Gnulib project, but I have trouble reaching gnu.org services currently. I'll be sure to do that once things recover.

    #opensource #development #bugstories

  11. As it happens, we still use CVS in our operating system project (there are reasons for doing this, but migration to git would indeed make sense).

    While working on our project, we occasionally have to do a full checkout of the whole codebase, which is several gigabytes. Over time, this operation has gotten very, very, very slow - I mean "2+ hours to perform a checkout" slow.

    This was getting quite ridiculous. Even though it's CVS, it shouldn't crawl like this. A quick build of CVS with debug symbols and sampling the "cvs server" process with Linux perf showed something peculiar: The code was spending the majority of the time inside one function.

    So what is this get_memnode() function? Turns out this is a support function from Gnulib that enables page-aligned memory allocations. (NOTE: I have no clue why CVS thinks doing page-aligned allocations is beneficial here - but here we are.)

    The code in question has support for three different backend allocators:
    1. mmap
    2. posix_memalign
    3. malloc

    Sounds nice, except that both 1 and 3 use a linked list to track the allocations. The get_memnode() function is called when deallocating memory to find out the original pointer to pass to the backend deallocation function: The node search code appears as:

    for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
    if (c->aligned_ptr == aligned_ptr)
    break;

    The get_memnode() function is called from pagealign_free():

    #if HAVE_MMAP
    if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
    error (EXIT_FAILURE, errno, "Failed to unmap memory");
    #elif HAVE_POSIX_MEMALIGN
    free (aligned_ptr);
    #else
    free (get_memnode (aligned_ptr));
    #endif

    This is an O(n) operation. CVS must be allocating a huge number of small allocations, which will result in it spending most of the CPU time in get_memnode() trying to find the node to remove from the list.

    Why should we care? This is "just CVS" after all. Well, Gnulib is used in a lot of projects, not just CVS. While pagealign_alloc() is likely not the most used functionality, it can still end up hurting performance in many places.

    The obvious easy fix is to prefer the posix_memalign method over the other options (I quickly made this happen for my personal CVS build by adding tactical #undef HAVE_MMAP). Even better, the list code should be replaced with something more sensible. In fact, there is no need to store the original pointer in a list; a better solution is to allocate enough memory and store the pointer before the calculated aligned pointer. This way, the original pointer can be fetched from the negative offset of the pointer passed to pagealign_free(). This way, it will be O(1).

    I tried to report this to the Gnulib project, but I have trouble reaching gnu.org services currently. I'll be sure to do that once things recover.

    #opensource #development #bugstories

  12. As it happens, we still use CVS in our operating system project (there are reasons for doing this, but migration to git would indeed make sense).

    While working on our project, we occasionally have to do a full checkout of the whole codebase, which is several gigabytes. Over time, this operation has gotten very, very, very slow - I mean "2+ hours to perform a checkout" slow.

    This was getting quite ridiculous. Even though it's CVS, it shouldn't crawl like this. A quick build of CVS with debug symbols and sampling the "cvs server" process with Linux perf showed something peculiar: The code was spending the majority of the time inside one function.

    So what is this get_memnode() function? Turns out this is a support function from Gnulib that enables page-aligned memory allocations. (NOTE: I have no clue why CVS thinks doing page-aligned allocations is beneficial here - but here we are.)

    The code in question has support for three different backend allocators:
    1. mmap
    2. posix_memalign
    3. malloc

    Sounds nice, except that both 1 and 3 use a linked list to track the allocations. The get_memnode() function is called when deallocating memory to find out the original pointer to pass to the backend deallocation function: The node search code appears as:

    for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
    if (c->aligned_ptr == aligned_ptr)
    break;

    The get_memnode() function is called from pagealign_free():

    #if HAVE_MMAP
    if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
    error (EXIT_FAILURE, errno, "Failed to unmap memory");
    #elif HAVE_POSIX_MEMALIGN
    free (aligned_ptr);
    #else
    free (get_memnode (aligned_ptr));
    #endif

    This is an O(n) operation. CVS must be allocating a huge number of small allocations, which will result in it spending most of the CPU time in get_memnode() trying to find the node to remove from the list.

    Why should we care? This is "just CVS" after all. Well, Gnulib is used in a lot of projects, not just CVS. While pagealign_alloc() is likely not the most used functionality, it can still end up hurting performance in many places.

    The obvious easy fix is to prefer the posix_memalign method over the other options (I quickly made this happen for my personal CVS build by adding tactical #undef HAVE_MMAP). Even better, the list code should be replaced with something more sensible. In fact, there is no need to store the original pointer in a list; a better solution is to allocate enough memory and store the pointer before the calculated aligned pointer. This way, the original pointer can be fetched from the negative offset of the pointer passed to pagealign_free(). This way, it will be O(1).

    I tried to report this to the Gnulib project, but I have trouble reaching gnu.org services currently. I'll be sure to do that once things recover.

    #opensource #development #bugstories

  13. @eniko You mean as a shorthand for #undef and #define in a row ? Or as a "I've undefed this, now restore the previous version"?

  14. @eniko You mean as a shorthand for #undef and #define in a row ? Or as a "I've undefed this, now restore the previous version"?

  15. @eniko You mean as a shorthand for #undef and #define in a row ? Or as a "I've undefed this, now restore the previous version"?

  16. @eniko You mean as a shorthand for #undef and #define in a row ? Or as a "I've undefed this, now restore the previous version"?

  17. @eniko You mean as a shorthand for #undef and #define in a row ? Or as a "I've undefed this, now restore the previous version"?

  18. So now I have a bunch of this in my code

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER myhandler

    Then a block of code followed by

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER default_exception_handler

    Is it jank? Yes, extremely. Does it do what I need it to do? Also yes. Do I feel embarrassed about this in any way? Not at all

  19. So now I have a bunch of this in my code

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER myhandler

    Then a block of code followed by

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER default_exception_handler

    Is it jank? Yes, extremely. Does it do what I need it to do? Also yes. Do I feel embarrassed about this in any way? Not at all

  20. So now I have a bunch of this in my code

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER myhandler

    Then a block of code followed by

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER default_exception_handler

    Is it jank? Yes, extremely. Does it do what I need it to do? Also yes. Do I feel embarrassed about this in any way? Not at all

  21. So now I have a bunch of this in my code

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER myhandler

    Then a block of code followed by

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER default_exception_handler

    Is it jank? Yes, extremely. Does it do what I need it to do? Also yes. Do I feel embarrassed about this in any way? Not at all

  22. So now I have a bunch of this in my code

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER myhandler

    Then a block of code followed by

    #undef EXCEPTION_HANDLER
    #define EXCEPTION_HANDLER default_exception_handler

    Is it jank? Yes, extremely. Does it do what I need it to do? Also yes. Do I feel embarrassed about this in any way? Not at all

  23. Ughhh, autotools....

    Not naming the project here.. Why would you assume that malloc and realloc are both broken just because you're cross compiling? Are you just being lazy? Even worse, don't '#undef malloc' and replace it with your own *broken* version.

    Better yet, just don't even use autotools at all.

  24. Ughhh, autotools....

    Not naming the project here.. Why would you assume that malloc and realloc are both broken just because you're cross compiling? Are you just being lazy? Even worse, don't '#undef malloc' and replace it with your own *broken* version.

    Better yet, just don't even use autotools at all.

  25. Ughhh, autotools....

    Not naming the project here.. Why would you assume that malloc and realloc are both broken just because you're cross compiling? Are you just being lazy? Even worse, don't '#undef malloc' and replace it with your own *broken* version.

    Better yet, just don't even use autotools at all.

  26. Ughhh, autotools....

    Not naming the project here.. Why would you assume that malloc and realloc are both broken just because you're cross compiling? Are you just being lazy? Even worse, don't '#undef malloc' and replace it with your own *broken* version.

    Better yet, just don't even use autotools at all.

  27. Ughhh, autotools....

    Not naming the project here.. Why would you assume that malloc and realloc are both broken just because you're cross compiling? Are you just being lazy? Even worse, don't '#undef malloc' and replace it with your own *broken* version.

    Better yet, just don't even use autotools at all.

  28. silly little thing i made to debug that heisenbug

    collecting all the things that i want to log and printing them after the fact, this avoids the overhead of printing immediately that seeming skewed the timing enough to make the bug disappear

    there is one logger for every relevant thread, because synchronizing the logging would have also skewed the result obviously

    struct microlog_entry
    {
        LONGLONG perf;
        const char *text;
    };
    
    #define MICROLOGGER_CAP 100
    
    struct micrologger
    {
        size_t pos;
        size_t read_pos;
        struct microlog_entry entries[MICROLOGGER_CAP];
    };
    
    #define microlog(l, text) if (l.pos < MICROLOGGER_CAP) { \
            LARGE_INTEGER perf; \
            QueryPerformanceCounter(&perf); \
            l.entries[l.pos++] = (struct microlog_entry) { perf.QuadPart, text }; \
        }
    
    static struct micrologger l_grabber_events;
    static struct micrologger l_grabber_samples;
    static struct micrologger l_main;
    
    void microlog_flush()
    {
    #define NUM_MICROLOGGERS 3
        struct micrologger *loggers[NUM_MICROLOGGERS] = { &l_grabber_events, &l_grabber_samples, &l_main };
    
        for (;;) {
            struct micrologger *least = NULL;
    
            for (size_t i = 0; i < NUM_MICROLOGGERS; i++) {
                if (loggers[i]->read_pos == loggers[i]->pos)
                    continue;
                if (!least || loggers[i]->entries[loggers[i]->read_pos].perf < least->entries[least->read_pos].perf)
                    least = loggers[i];
            }
    
            if (!least)
                break;
    
            printf("%s\n", least->entries[least->read_pos].text);
            least->read_pos++;
        }
    #undef NUM_MICROLOGGERS
    }
    
  29. silly little thing i made to debug that heisenbug

    collecting all the things that i want to log and printing them after the fact, this avoids the overhead of printing immediately that seeming skewed the timing enough to make the bug disappear

    there is one logger for every relevant thread, because synchronizing the logging would have also skewed the result obviously

    struct microlog_entry
    {
        LONGLONG perf;
        const char *text;
    };
    
    #define MICROLOGGER_CAP 100
    
    struct micrologger
    {
        size_t pos;
        size_t read_pos;
        struct microlog_entry entries[MICROLOGGER_CAP];
    };
    
    #define microlog(l, text) if (l.pos < MICROLOGGER_CAP) { \
            LARGE_INTEGER perf; \
            QueryPerformanceCounter(&perf); \
            l.entries[l.pos++] = (struct microlog_entry) { perf.QuadPart, text }; \
        }
    
    static struct micrologger l_grabber_events;
    static struct micrologger l_grabber_samples;
    static struct micrologger l_main;
    
    void microlog_flush()
    {
    #define NUM_MICROLOGGERS 3
        struct micrologger *loggers[NUM_MICROLOGGERS] = { &l_grabber_events, &l_grabber_samples, &l_main };
    
        for (;;) {
            struct micrologger *least = NULL;
    
            for (size_t i = 0; i < NUM_MICROLOGGERS; i++) {
                if (loggers[i]->read_pos == loggers[i]->pos)
                    continue;
                if (!least || loggers[i]->entries[loggers[i]->read_pos].perf < least->entries[least->read_pos].perf)
                    least = loggers[i];
            }
    
            if (!least)
                break;
    
            printf("%s\n", least->entries[least->read_pos].text);
            least->read_pos++;
        }
    #undef NUM_MICROLOGGERS
    }
    
  30. silly little thing i made to debug that heisenbug

    collecting all the things that i want to log and printing them after the fact, this avoids the overhead of printing immediately that seeming skewed the timing enough to make the bug disappear

    there is one logger for every relevant thread, because synchronizing the logging would have also skewed the result obviously

    struct microlog_entry
    {
        LONGLONG perf;
        const char *text;
    };
    
    #define MICROLOGGER_CAP 100
    
    struct micrologger
    {
        size_t pos;
        size_t read_pos;
        struct microlog_entry entries[MICROLOGGER_CAP];
    };
    
    #define microlog(l, text) if (l.pos < MICROLOGGER_CAP) { \
            LARGE_INTEGER perf; \
            QueryPerformanceCounter(&perf); \
            l.entries[l.pos++] = (struct microlog_entry) { perf.QuadPart, text }; \
        }
    
    static struct micrologger l_grabber_events;
    static struct micrologger l_grabber_samples;
    static struct micrologger l_main;
    
    void microlog_flush()
    {
    #define NUM_MICROLOGGERS 3
        struct micrologger *loggers[NUM_MICROLOGGERS] = { &l_grabber_events, &l_grabber_samples, &l_main };
    
        for (;;) {
            struct micrologger *least = NULL;
    
            for (size_t i = 0; i < NUM_MICROLOGGERS; i++) {
                if (loggers[i]->read_pos == loggers[i]->pos)
                    continue;
                if (!least || loggers[i]->entries[loggers[i]->read_pos].perf < least->entries[least->read_pos].perf)
                    least = loggers[i];
            }
    
            if (!least)
                break;
    
            printf("%s\n", least->entries[least->read_pos].text);
            least->read_pos++;
        }
    #undef NUM_MICROLOGGERS
    }
    
  31. silly little thing i made to debug that heisenbug

    collecting all the things that i want to log and printing them after the fact, this avoids the overhead of printing immediately that seeming skewed the timing enough to make the bug disappear

    there is one logger for every relevant thread, because synchronizing the logging would have also skewed the result obviously

    struct microlog_entry
    {
        LONGLONG perf;
        const char *text;
    };
    
    #define MICROLOGGER_CAP 100
    
    struct micrologger
    {
        size_t pos;
        size_t read_pos;
        struct microlog_entry entries[MICROLOGGER_CAP];
    };
    
    #define microlog(l, text) if (l.pos < MICROLOGGER_CAP) { \
            LARGE_INTEGER perf; \
            QueryPerformanceCounter(&perf); \
            l.entries[l.pos++] = (struct microlog_entry) { perf.QuadPart, text }; \
        }
    
    static struct micrologger l_grabber_events;
    static struct micrologger l_grabber_samples;
    static struct micrologger l_main;
    
    void microlog_flush()
    {
    #define NUM_MICROLOGGERS 3
        struct micrologger *loggers[NUM_MICROLOGGERS] = { &l_grabber_events, &l_grabber_samples, &l_main };
    
        for (;;) {
            struct micrologger *least = NULL;
    
            for (size_t i = 0; i < NUM_MICROLOGGERS; i++) {
                if (loggers[i]->read_pos == loggers[i]->pos)
                    continue;
                if (!least || loggers[i]->entries[loggers[i]->read_pos].perf < least->entries[least->read_pos].perf)
                    least = loggers[i];
            }
    
            if (!least)
                break;
    
            printf("%s\n", least->entries[least->read_pos].text);
            least->read_pos++;
        }
    #undef NUM_MICROLOGGERS
    }
    
  32. silly little thing i made to debug that heisenbug

    collecting all the things that i want to log and printing them after the fact, this avoids the overhead of printing immediately that seeming skewed the timing enough to make the bug disappear

    there is one logger for every relevant thread, because synchronizing the logging would have also skewed the result obviously

    struct microlog_entry
    {
        LONGLONG perf;
        const char *text;
    };
    
    #define MICROLOGGER_CAP 100
    
    struct micrologger
    {
        size_t pos;
        size_t read_pos;
        struct microlog_entry entries[MICROLOGGER_CAP];
    };
    
    #define microlog(l, text) if (l.pos < MICROLOGGER_CAP) { \
            LARGE_INTEGER perf; \
            QueryPerformanceCounter(&perf); \
            l.entries[l.pos++] = (struct microlog_entry) { perf.QuadPart, text }; \
        }
    
    static struct micrologger l_grabber_events;
    static struct micrologger l_grabber_samples;
    static struct micrologger l_main;
    
    void microlog_flush()
    {
    #define NUM_MICROLOGGERS 3
        struct micrologger *loggers[NUM_MICROLOGGERS] = { &l_grabber_events, &l_grabber_samples, &l_main };
    
        for (;;) {
            struct micrologger *least = NULL;
    
            for (size_t i = 0; i < NUM_MICROLOGGERS; i++) {
                if (loggers[i]->read_pos == loggers[i]->pos)
                    continue;
                if (!least || loggers[i]->entries[loggers[i]->read_pos].perf < least->entries[least->read_pos].perf)
                    least = loggers[i];
            }
    
            if (!least)
                break;
    
            printf("%s\n", least->entries[least->read_pos].text);
            least->read_pos++;
        }
    #undef NUM_MICROLOGGERS
    }
    
  33. @SuperDicq @steph This file is part of the GNU C Library.

    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>. */

    static void
    sincosx_mpn (mp1 si, mp1 co, mp1 xx, mp1 ix)
    {
    int i;
    mp2 s[4], c[4];
    mp1 tmp, x;

    if (ix == NULL)
    {
    memset (si, 0, sizeof (mp1));
    memset (co, 0, sizeof (mp1));
    co[SZ-1] = 1;
    memcpy (x, xx, sizeof (mp1));
    }
    else
    mpn_sub_n (x, xx, ix, SZ);

    for (i = 0; i < 1 << N; i++)
    {
    #define add_shift_mulh(d,x,s1,s2,sh,n) \
    do { \
    if (s2 != NULL) { \
    if (sh > 0) { \
    assert (sh < mpbpl); \
    mpn_lshift (tmp, s1, SZ, sh); \
    if (n) \
    mpn_sub_n (tmp,tmp,s2+FRAC/mpbpl,SZ); \
    else \
    mpn_add_n (tmp,tmp,s2+FRAC/mpbpl,SZ); \
    } else { \
    if (n) \
    mpn_sub_n (tmp,s1,s2+FRAC/mpbpl,SZ); \
    else \
    mpn_add_n (tmp,s1,s2+FRAC/mpbpl,SZ); \
    } \
    mpn_mul_n(d,tmp,x,SZ); \
    } else \
    mpn_mul_n(d,s1,x,SZ); \
    assert(N+sh < mpbpl); \
    if (N+sh > 0) mpn_rshift(d,d,2*SZ,N+sh); \
    } while(0)
    #define summ(d,ss,s,n) \
    do { \
    mpn_add_n(tmp,s[1]+FRAC/mpbpl,s[2]+FRAC/mpbpl,SZ); \
    mpn_lshift(tmp,tmp,SZ,1); \
    mpn_add_n(tmp,tmp,s[0]+FRAC/mpbpl,SZ); \
    mpn_add_n(tmp,tmp,s[3]+FRAC/mpbpl,SZ); \
    mpn_divmod_1(tmp,tmp,SZ,6); \
    if (n) \
    mpn_sub_n (d,ss,tmp,SZ); \
    else \
    mpn_add_n (d,ss,tmp,SZ); \
    } while (0)

    add_shift_mulh (s[0], x, co, NULL, 0, 0); /* s0 = h * c; */
    add_shift_mulh (c[0], x, si, NULL, 0, 0); /* c0 = h * s; */
    add_shift_mulh (s[1], x, co, c[0], 1, 1); /* s1 = h * (c - c0/2); */
    add_shift_mulh (c[1], x, si, s[0], 1, 0); /* c1 = h * (s + s0/2); */
    add_shift_mulh (s[2], x, co, c[1], 1, 1); /* s2 = h * (c - c1/2); */
    add_shift_mulh (c[2], x, si, s[1], 1, 0); /* c2 = h * (s + s1/2); */
    add_shift_mulh (s[3], x, co, c[2], 0, 1); /* s3 = h * (c - c2); */
    add_shift_mulh (c[3], x, si, s[2], 0, 0); /* c3 = h * (s + s2); */
    summ (si, si, s, 0); /* s = s + (s0+2*s1+2*s2+s3)/6; */
    summ (co, co, c, 1); /* c = c - (c0+2*c1+2*c2+c3)/6; */
    }
    #undef add_shift_mulh
    #undef summ
    }
  34. @SuperDicq @steph This file is part of the GNU C Library.

    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>. */

    static void
    sincosx_mpn (mp1 si, mp1 co, mp1 xx, mp1 ix)
    {
    int i;
    mp2 s[4], c[4];
    mp1 tmp, x;

    if (ix == NULL)
    {
    memset (si, 0, sizeof (mp1));
    memset (co, 0, sizeof (mp1));
    co[SZ-1] = 1;
    memcpy (x, xx, sizeof (mp1));
    }
    else
    mpn_sub_n (x, xx, ix, SZ);

    for (i = 0; i < 1 << N; i++)
    {
    #define add_shift_mulh(d,x,s1,s2,sh,n) \
    do { \
    if (s2 != NULL) { \
    if (sh > 0) { \
    assert (sh < mpbpl); \
    mpn_lshift (tmp, s1, SZ, sh); \
    if (n) \
    mpn_sub_n (tmp,tmp,s2+FRAC/mpbpl,SZ); \
    else \
    mpn_add_n (tmp,tmp,s2+FRAC/mpbpl,SZ); \
    } else { \
    if (n) \
    mpn_sub_n (tmp,s1,s2+FRAC/mpbpl,SZ); \
    else \
    mpn_add_n (tmp,s1,s2+FRAC/mpbpl,SZ); \
    } \
    mpn_mul_n(d,tmp,x,SZ); \
    } else \
    mpn_mul_n(d,s1,x,SZ); \
    assert(N+sh < mpbpl); \
    if (N+sh > 0) mpn_rshift(d,d,2*SZ,N+sh); \
    } while(0)
    #define summ(d,ss,s,n) \
    do { \
    mpn_add_n(tmp,s[1]+FRAC/mpbpl,s[2]+FRAC/mpbpl,SZ); \
    mpn_lshift(tmp,tmp,SZ,1); \
    mpn_add_n(tmp,tmp,s[0]+FRAC/mpbpl,SZ); \
    mpn_add_n(tmp,tmp,s[3]+FRAC/mpbpl,SZ); \
    mpn_divmod_1(tmp,tmp,SZ,6); \
    if (n) \
    mpn_sub_n (d,ss,tmp,SZ); \
    else \
    mpn_add_n (d,ss,tmp,SZ); \
    } while (0)

    add_shift_mulh (s[0], x, co, NULL, 0, 0); /* s0 = h * c; */
    add_shift_mulh (c[0], x, si, NULL, 0, 0); /* c0 = h * s; */
    add_shift_mulh (s[1], x, co, c[0], 1, 1); /* s1 = h * (c - c0/2); */
    add_shift_mulh (c[1], x, si, s[0], 1, 0); /* c1 = h * (s + s0/2); */
    add_shift_mulh (s[2], x, co, c[1], 1, 1); /* s2 = h * (c - c1/2); */
    add_shift_mulh (c[2], x, si, s[1], 1, 0); /* c2 = h * (s + s1/2); */
    add_shift_mulh (s[3], x, co, c[2], 0, 1); /* s3 = h * (c - c2); */
    add_shift_mulh (c[3], x, si, s[2], 0, 0); /* c3 = h * (s + s2); */
    summ (si, si, s, 0); /* s = s + (s0+2*s1+2*s2+s3)/6; */
    summ (co, co, c, 1); /* c = c - (c0+2*c1+2*c2+c3)/6; */
    }
    #undef add_shift_mulh
    #undef summ
    }
  35. How to politely use assert in a header (gcc/clang/msvc/what else?):

    #pragma push_macro("NDEBUG")
    #undef NDEBUG
    #include <assert.h>
    // your asserts should properly assert
    #pragma pop_macro("NDEBUG")
    // nobody has 2 know

    Your vendor’s assert.h should be able to be included multiple times with NDEBUG defined or not.

  36. @kirakira did you know that you can define structured data in the c preprocessor using church encoding?

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

    #define USERS(_user) \
    _user("sjolsen", "tech.lgbt"), \
    _user("kirakira", "furry.engineer"), \
    _user("Gargron", "mastodon.social")

    #define handles_user(_name, _instance) "@" _name "@" _instance

    static const char *const handles[] = {
    USERS(handles_user)
    };

    #undef handles_user

    #define ARRAY_SIZE(_a) (sizeof(_a) / sizeof(_a[0]))

    int main() {
    for (size_t i = 0; i < ARRAY_SIZE(handles); ++i) {
    puts(handles[i]);
    }

    return EXIT_SUCCESS;
    }
  37. The Fourth Hard Problem in Computer Science:

    Whether to use

    #define FOO 0

    or

    /* #undef FOO */

    or

    #undef FOO

    or even

    /* #define FOO */

    for things that aren't present/enabled/turned on in a config.h. FOO can start with HAVE_ or not.

  38. @pythno I think I remember having to do a kludge to work around that at some point.

    I think the min/max are macros in windows.h, so #undef should do the trick.

  39. @icculus I love SDL and have been using it for more than a decade, but the way they treat `main` always bothered me (`#define main SDL_main` is already obnoxious, and the actual main being implemented in a standalone shared lib is extra spicy). I always just did `#undef main` and took control from SDL on this.

    Now they're making this thing even more integral and this makes me sad :(

    (yes you can at least disable it with a special macro, but this being the default really bothers me)

  40. Discover how conditional compilation directives can enhance your C programming skills. Learn to use #ifdef, #ifndef, #undef, #if, #else, #elif, and #endif to create more flexible and efficient code. Unlock the power of the C preprocessor!

    teguhteja.id/conditional-compi

  41. what was in the air in 1997 bro
    /* define the boolean values in a general and
    * portable way.
    */
    #undef TRUE
    #define TRUE (0 || !(0))
    #undef FALSE
    #define FALSE (!(TRUE))
    /* typedef enum { false = FALSE, true = TRUE } bool; */

    /* some other names for true and false */
    #define YES TRUE
    #define NO FALSE

    #define GOOD TRUE
    #define WRONG FALSE

    #undef OK
    #define OK TRUE
    #define BAD FALSE

    #define SOME TRUE
    #define NONE FALSE

  42. @fluor I've seen people do complex repeated-include metaprogramming with the C preprocessor for sure (it helps to know about #undef, #stringify and ##) but also leaning on the C preprocessor this way can get you in some very hard to debug spots very fast.