Error compiling FTL 6.7.1

Please follow the below template, it will help us to help you!

If you are Experiencing issues with a Pi-hole install that has non-standard elements (e.g you are using nginx, apache2 or another reverse proxy, or there is some other aspect of your install that is customised) - please use the Community Help category.

Expected Behaviour:

Trying to compile FTL version 6.7.1. Previously, version 6.7 compiled fine.

  • Operating System ARMv5TE
  • Hardware: Pogoplug V4

Actual Behaviour:

[  4%] Building C object src/CMakeFiles/core.dir/events.c.o
In file included from /root/FTL/src/FTL.h:67,
                 from /root/FTL/src/events.c:12:
/root/FTL/src/events.c:30:1: error: static assertion failed: "Event queue must be lock-free, it is used from a signal handler"
   30 | static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "Event queue must be lock-free, it is used from a signal handler");
      | ^~~~~~~~~~~~~

gmake[2]: *** [src/CMakeFiles/core.dir/build.make:160: src/CMakeFiles/core.dir/events.c.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:433: src/CMakeFiles/core.dir/all] Error 2
gmake: *** [Makefile:136: all] Error 2

Debug Token:

n/a

I see this in 6.7.1 event.c, but not in the 6.7 version:

static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "Event queue must be lock-free, it is used from a signal handler");

I assume this is what I'm hitting, but I don't know what "Event queue must be lock free" means, or how to fix.

Sorry - should have put the compiler error in a block quote

[ 4%] Building C object src/CMakeFiles/core.dir/events.c.o

In file included from /root/FTL/src/FTL.h:67,

             from /root/FTL/src/events.c:12:

/root/FTL/src/events.c:30:1: error: static assertion failed: "Event queue must be lock-free, it is used from a signal handler"

30 | static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "Event queue must be lock-free, it is used from a signal handler");

  | ^\~\~\~\~\~\~\~\~\~\~\~\~

gmake[2]: *** [src/CMakeFiles/core.dir/build.make:160: src/CMakeFiles/core.dir/events.c.o] Error 1

gmake[1]: *** [CMakeFiles/Makefile2:433: src/CMakeFiles/core.dir/all] Error 2

gmake: *** [Makefile:136: all] Error 2

I recommend you use code blocks instead of block quotes to paste terminal outputs.

In the editor you can select the (+) and then “Preforamatted text” when on a newline.

It should open and close with three backticks on empty lines.
Like this:
```
[your pasted log]
```

It should look like this:

[your pasted log]

Please also provide the names and versions of your distro, compiler and standard C library.
The following commands should help:

cat /etc/os-release
uname -a
cc --version
ldd --version

Here is OS and compiler info:

root@pihole:~# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@pihole:~# uname -a
Linux pihole 6.18.10-kirkwood-tld-1 #4 Sun Feb 15 10:35:48 PST 2026 armv5tel GNU/Linux
root@pihole:~# cc --version
cc (Debian 12.2.0-14+deb12u1) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

root@pihole:~# ldd --version
ldd (Debian GLIBC 2.36-9+deb12u14) 2.36
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
root@pihole:~#

And here is the failing part of the compile output:

[  4%] Building C object src/CMakeFiles/core.dir/events.c.o
In file included from /root/FTL/src/FTL.h:67,
                 from /root/FTL/src/events.c:12:
/root/FTL/src/events.c:30:1: error: static assertion failed: "Event queue must be lock-free, it is used from a signal handler"
   30 | static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "Event queue must be lock-free, it is used from a signal handler");
      | ^~~~~~~~~~~~~
gmake[2]: *** [src/CMakeFiles/core.dir/build.make:160: src/CMakeFiles/core.dir/events.c.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:433: src/CMakeFiles/core.dir/all] Error 2
gmake: *** [Makefile:136: all] Error 2

that assert is probably the whole issue. armv5 does not provide a lock-free C11 atomic bool, and 6.7.1 now requires it because the event queue is touched from a signal handler. so this is a platform limitation on the pogoplug, not a missing debian package.

I would not remove the assert unless you are also changing the signal-handler path. The safe options are to stay on 6.7 for that box, or build and run FTL on a newer ARM target. The compiler and libc versions look normal for Debian 12. The failing part is the ARMv5 atomic capability.

How would I change the signal handler path if I wanted to give that a try?

The assert got introduced by read shared state while it is still protected by 010011110 · Pull Request #3065 · pi-hole/FTL · GitHub and backported to 6.7.1 via commit Read and clear an event in one operation · pi-hole/FTL@eb0aa5e · GitHub
Raise events from the signal handler without logging · pi-hole/FTL@6694d7b · GitHub from the same PR is also relevant here.

You may try by simply reverting the first commit (be wary of conflicts with the latter commit). Compared to just removing the assert you don’t risk a deadlock but you lose the original fix, that an event may be dropped under certain circumstances (that bug was present on 6.7).

I'm a newbie when it comes to git.

So, I obviously can't revert it on the master repository. How do I revert it on my local copy? Or do I just manually replace events.c and events.h with the ones from version 6.7?

I assume you cloned the FTL git repo in a similar way as described on the docs.

I recommend you create a new branch first:

git switch master
git branch --copy  v6.7.1-patched
git switch v6.7.1-patched

Due to the history being based on eb0aa5e, a simple git revert eb0aa5e does not suffice.
I created a patchfile that properly un-does the commit and removes the static_assert():
0001-Don-t-require-ATOMIC_BOOL_LOCK_FREE.txt (4.2 KB)

From 09921676e944a9afc3abb3ce9f372cdcba2467f5 Mon Sep 17 00:00:00 2001
From: darkexplosiveqwx <101737077+darkexplosiveqwx@users.noreply.github.com>
Date: Mon, 21 Sep 2026 07:16:40 +0200: [PATCH] Don't require ATOMIC_BOOL_LOCK_FREE

This reverts commit eb0aa5eaa3abedeba61328b0ab7f3600548bc81f.
---
 src/events.c | 43 +++++++++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 14 deletions(-)

diff --git a/src/events.c b/src/events.c
index 72a7e495..9d8fcf0e 100644
--- a/src/events.c
+++ b/src/events.c
@@ -12,7 +12,7 @@
 #include "FTL.h"
 // public prototypes
 #include "events.h"
-// atomic_exchange()
+// atomic_flag_test_and_set()
 #include <stdatomic.h>
 // struct config
 #include "config/config.h"
@@ -23,11 +23,7 @@
 static const char *eventtext(const enum events event);
 
 // Queue containing all possible events
-// An atomic_bool rather than an atomic_flag: a flag cannot be read without
-// setting it. RELOAD_GRAVITY is raised from the SIGRTMIN handler, so the type
-// has to be lock-free for the exchange to be async-signal-safe
-static _Atomic bool eventqueue[EVENTS_MAX];
-static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "Event queue must be lock-free, it is used from a signal handler");
+static volatile atomic_flag eventqueue[EVENTS_MAX] = { ATOMIC_FLAG_INIT };
 
 // Set/Request event
 // We set the events atomically to ensure no race collisions can happen. If an
@@ -35,8 +31,10 @@ static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "Event queue must be lock-free, it is
 // added multiple times
 void _set_event(const enum events event, int line, const char *function, const char *file)
 {
-	// Set eventqueue bit, learning what it was in the same operation
-	const bool is_set = atomic_exchange(&eventqueue[event], true);
+	bool is_set = false;
+	// Set eventqueue bit
+	if(atomic_flag_test_and_set(&eventqueue[event]))
+		is_set = true;
 
 	// Possible debug logging
 	if(config.debug.events.v.b)
@@ -56,17 +54,30 @@ void _set_event(const enum events event, int line, const char *function, const c
 // exchange on its own: the event is logged where it is processed anyway
 void set_event_from_signal(const enum events event)
 {
-	atomic_exchange(&eventqueue[event], true);
+	// Set eventqueue bit
+	atomic_flag_test_and_set(&eventqueue[event]);
 }
 
 // Get and clear event
-// Read and clear in one exchange, so there is no window in which a producer
-// sees a value the consumer has already taken. Compiles to XCHG on x86_64 and
-// i686, and to the LDAXRB/STLXRB pair on ARM64
+// Unfortunately, we cannot read the value of an atomic_flag without setting it
+// either to true or false. This is by design. Hence, we implement testing by
+// first trying to set the the flag to true. If this "fails", we know the flag
+// has already been set.
+// On x86_64 and i686 CPUs, these atomic instrictions are implemented using the
+// XCHG asm instruction, which simply exchanges the content of two registers or,
+// in this case, a register and a memory location (the respective eventqueue
+// pointer). This is guaranteed to happen atomically by automatically
+// implementing the processor's locking protocol during the operation.
+// On other architecture, similar instructions are used to reassemble the same
+// effect (but typically with a few more instructions). ARM64, for instance,
+// uses LDAXRB (Load-aquire exclusive register byte) and STAXRB (Store-release
+// exclusive register byte) to implement the same thing with a few more
+// instructions.
 bool _get_and_clear_event(const enum events event, int line, const char *function, const char *file)
 {
-	// Read the bit and clear it in the same operation
-	const bool is_set = atomic_exchange(&eventqueue[event], false);
+	bool is_set = false;
+	if(atomic_flag_test_and_set(&eventqueue[event]))
+		is_set = true;
 
 	// Possible debug logging only for SET status, to avoid log file flooding with NOT SET messages
 	if(is_set && config.debug.events.v.b)
@@ -75,6 +86,10 @@ bool _get_and_clear_event(const enum events event, int line, const char *functio
 		          eventtext(event), function, file, line);
 	}
 
+	// Clear eventqueue bit (we set it above) ...
+	atomic_flag_clear(&eventqueue[event]);
+
+	// ... and return status
 	return is_set;
 }
 
-- 
2.53.0

You may try copy and pasting the patch, but you need to be careful with tabs (they may get copied as spaces and then the diff will fail) into 0001-Don-t-require-ATOMIC_BOOL_LOCK_FREE.patch.
Alternatively you can download the file liked above and copy it as 0001-Don-t-require-ATOMIC_BOOL_LOCK_FREE.patch to your Pogoplug.

Then apply the patch:

git am 0001-Don-t-require-ATOMIC_BOOL_LOCK_FREE.patch

For FTL v7 there is another feature planned to require lock-free C11 atomics, but that will be an integral part of the new logger, so reverting it will be difficult.
I’d be interested in the output of the following test program to see what values your system has for the other lock-free test macros:

cat > /tmp/lock-free-c11-test.c <<'EOF'
#include <stdio.h>
#include <stdatomic.h>

int main(void)
{
    printf("ATOMIC_BOOL_LOCK_FREE     = %d\n", ATOMIC_BOOL_LOCK_FREE);
    printf("ATOMIC_CHAR_LOCK_FREE     = %d\n", ATOMIC_CHAR_LOCK_FREE);
    printf("ATOMIC_CHAR16_T_LOCK_FREE = %d\n", ATOMIC_CHAR16_T_LOCK_FREE);
    printf("ATOMIC_CHAR32_T_LOCK_FREE = %d\n", ATOMIC_CHAR32_T_LOCK_FREE);
    printf("ATOMIC_WCHAR_T_LOCK_FREE  = %d\n", ATOMIC_WCHAR_T_LOCK_FREE);
    printf("ATOMIC_SHORT_LOCK_FREE    = %d\n", ATOMIC_SHORT_LOCK_FREE);
    printf("ATOMIC_INT_LOCK_FREE      = %d\n", ATOMIC_INT_LOCK_FREE);
    printf("ATOMIC_LONG_LOCK_FREE     = %d\n", ATOMIC_LONG_LOCK_FREE);
    printf("ATOMIC_LLONG_LOCK_FREE    = %d\n", ATOMIC_LLONG_LOCK_FREE);
    printf("ATOMIC_POINTER_LOCK_FREE  = %d\n", ATOMIC_POINTER_LOCK_FREE);

    return 0;
}
EOF
cc -std=c11 -O2 /tmp/lock-free-c11-test.c -o /tmp/lock-free-c11-test
cc -std=c11 -O2 -march=native -mcpu=native /tmp/lock-free-c11-test.c -o /tmp/lock-free-c11-test-native
/tmp/lock-free-c11-test
/tmp/lock-free-c11-test-native
rm -rf /tmp/lock-free-c11-test*

Right, I'm doing this to clone it locally:

git clone https://github.com/pi-hole/FTL.git 2>&1

I ran your lock-free test and this is what I get (note compile warning on the 2nd compile)

root@pihole:/tmp# cc -std=c11 -O2 /tmp/lock-free-c11-test.c -o /tmp/lock-free-c11-test
root@pihole:/tmp# cc -std=c11 -O2 -march=native -mcpu=native /tmp/lock-free-c11-test.c -o /tmp/lock-free-c11-test-native
cc1: warning: switch '-mcpu=arm10tdmi' conflicts with switch '-march=armv5te'
root@pihole:/tmp# /tmp/lock-free-c11-test
ATOMIC_BOOL_LOCK_FREE = 1
ATOMIC_CHAR_LOCK_FREE = 1
ATOMIC_CHAR16_T_LOCK_FREE = 1
ATOMIC_CHAR32_T_LOCK_FREE = 1
ATOMIC_WCHAR_T_LOCK_FREE = 1
ATOMIC_SHORT_LOCK_FREE = 1
ATOMIC_INT_LOCK_FREE = 1
ATOMIC_LONG_LOCK_FREE = 1
ATOMIC_LLONG_LOCK_FREE = 1
ATOMIC_POINTER_LOCK_FREE = 1
root@pihole:/tmp# /tmp/lock-free-c11-test-native
ATOMIC_BOOL_LOCK_FREE = 1
ATOMIC_CHAR_LOCK_FREE = 1
ATOMIC_CHAR16_T_LOCK_FREE = 1
ATOMIC_CHAR32_T_LOCK_FREE = 1
ATOMIC_WCHAR_T_LOCK_FREE = 1
ATOMIC_SHORT_LOCK_FREE = 1
ATOMIC_INT_LOCK_FREE = 1
ATOMIC_LONG_LOCK_FREE = 1
ATOMIC_LLONG_LOCK_FREE = 1
ATOMIC_POINTER_LOCK_FREE = 1
root@pihole:/tmp#

Does this mean my processor DOES support all these?

Doing the compile without the -march=native flag eliminated the warning. Output is the same

root@pihole:/tmp# cc -std=c11 -O2 -mcpu=native /tmp/lock-free-c11-test.c -o /tmp/lock-free-c11-test-native
root@pihole:/tmp# ./lock-free-c11-test-native
ATOMIC_BOOL_LOCK_FREE = 1
ATOMIC_CHAR_LOCK_FREE = 1
ATOMIC_CHAR16_T_LOCK_FREE = 1
ATOMIC_CHAR32_T_LOCK_FREE = 1
ATOMIC_WCHAR_T_LOCK_FREE = 1
ATOMIC_SHORT_LOCK_FREE = 1
ATOMIC_INT_LOCK_FREE = 1
ATOMIC_LONG_LOCK_FREE = 1
ATOMIC_LLONG_LOCK_FREE = 1
ATOMIC_POINTER_LOCK_FREE = 1
root@pihole:/tmp#

Thanks. I downloaded your patch file, did git clone, then git switch master, git branch --copy, git switch v6.7.1-armv5te (what I called it), applied the patch and am now compiling. Will let you know how it works when it completes.

OK, that compiled clean and seems to be working. So, as long as events.c doesn't change in subsequent releases, I assume I do the same thing going forward. Until we get to V7, at which point it sounds like things get much more difficult.

Any estimated timeframe on when you will be releasing V7?

Thanks for all your help!

No time frame at the moment.

This is on the OXNAS Pogoplug Pro:

ATOMIC_BOOL_LOCK_FREE     = 1
ATOMIC_CHAR_LOCK_FREE     = 1
ATOMIC_CHAR16_T_LOCK_FREE = 1
ATOMIC_CHAR32_T_LOCK_FREE = 1
ATOMIC_WCHAR_T_LOCK_FREE  = 1
ATOMIC_SHORT_LOCK_FREE    = 1
ATOMIC_INT_LOCK_FREE      = 1
ATOMIC_LONG_LOCK_FREE     = 1
ATOMIC_LLONG_LOCK_FREE    = 1
ATOMIC_POINTER_LOCK_FREE  = 1
ATOMIC_BOOL_LOCK_FREE     = 2
ATOMIC_CHAR_LOCK_FREE     = 2
ATOMIC_CHAR16_T_LOCK_FREE = 2
ATOMIC_CHAR32_T_LOCK_FREE = 2
ATOMIC_WCHAR_T_LOCK_FREE  = 2
ATOMIC_SHORT_LOCK_FREE    = 2
ATOMIC_INT_LOCK_FREE      = 2
ATOMIC_LONG_LOCK_FREE     = 2
ATOMIC_LLONG_LOCK_FREE    = 2
ATOMIC_POINTER_LOCK_FREE  = 2

Hope this helps.

Is my interpretation correct that this should actually work on an OXNAS architecture (as long I pass the correct compiler arguments), since the second run of the code shows:
ATOMIC_BOOL_LOCK_FREE = 2

?

So OXNAS seems to be supported, you need to specify the necessary flags before building:

# Set compiler flags to detect the host CPU's native instructions
export CFLAGS="-O2 -march=native -mcpu=native"
export CXXFLAGS="-O2 -march=native -mcpu=native"

This produces a working build, without the need of commenting out the ATOMIC_BOOL_LOCK_FREE line.

Very happy about that and very happy I can continue to put this old device to great use :+1: