110 lines
3.5 KiB
C
110 lines
3.5 KiB
C
/* Pi-hole: A black hole for Internet advertisements
|
|
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
|
* Network-wide ad blocking via your own hardware.
|
|
*
|
|
* FTL Engine
|
|
* Event queue processing routines
|
|
*
|
|
* This file is copyright under the latest version of the EUPL.
|
|
* Please see LICENSE file for your rights under this license. */
|
|
|
|
|
|
#include "FTL.h"
|
|
// public prototypes
|
|
#include "events.h"
|
|
// atomic_flag_test_and_set()
|
|
#include <stdatomic.h>
|
|
// struct config
|
|
#include "config.h"
|
|
// logg()
|
|
#include "log.h"
|
|
|
|
// Private prototypes
|
|
static const char *eventtext(const enum events event);
|
|
|
|
// Queue containing all possible events
|
|
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
|
|
// event has already been requested, this has no consequences as event cannot be
|
|
// added multiple times
|
|
void _set_event(const enum events event, int line, const char *function, const char *file)
|
|
{
|
|
bool is_set = false;
|
|
// Set eventqueue bit
|
|
if(atomic_flag_test_and_set(&eventqueue[event]))
|
|
is_set = true;
|
|
|
|
// Possible debug logging
|
|
if(config.debug & DEBUG_EVENTS)
|
|
{
|
|
logg("Event %s -> %s called from %s() (%s:%i)",
|
|
eventtext(event),
|
|
is_set ? "was ALREADY SET" : "now SET",
|
|
function, file, line);
|
|
}
|
|
}
|
|
|
|
// Get and clear event
|
|
// 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)
|
|
{
|
|
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 & DEBUG_EVENTS)
|
|
{
|
|
logg("Event %s -> was SET, now CLEARED called from %s() (%s:%i)",
|
|
eventtext(event), function, file, line);
|
|
}
|
|
|
|
// Clear eventqueue bit (we set it above) ...
|
|
atomic_flag_clear(&eventqueue[event]);
|
|
|
|
// ... and return status
|
|
return is_set;
|
|
}
|
|
|
|
// Output human-readable version event text representation
|
|
static const char *eventtext(const enum events event)
|
|
{
|
|
switch(event)
|
|
{
|
|
case RELOAD_GRAVITY:
|
|
return "RELOAD_GRAVITY";
|
|
case RELOAD_PRIVACY_LEVEL:
|
|
return "RELOAD_PRIVACY_LEVEL";
|
|
case RERESOLVE_HOSTNAMES:
|
|
return "RERESOLVE_HOSTNAMES";
|
|
case RERESOLVE_HOSTNAMES_FORCE:
|
|
return "RERESOLVE_HOSTNAMES_FORCE";
|
|
case REIMPORT_ALIASCLIENTS:
|
|
return "REIMPORT_ALIASCLIENTS";
|
|
case PARSE_NEIGHBOR_CACHE:
|
|
return "PARSE_NEIGHBOR_CACHE";
|
|
case RESOLVE_NEW_HOSTNAMES:
|
|
return "RESOLVE_NEW_HOSTNAMES";
|
|
case RELOAD_BLOCKINGSTATUS:
|
|
return "RELOAD_BLOCKINGSTATUS";
|
|
case EVENTS_MAX: // fall through
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|