mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-05 08:11:15 -03:00
retyle event module to match project style
Reduces lint errors from 39 to 30 (-23%). Line count from 915 to 670 (-27%). Another step in resolving issue #2902.
This commit is contained in:
670
src/event.cpp
670
src/event.cpp
@@ -1,226 +1,164 @@
|
||||
/** \file event.c
|
||||
|
||||
Functions for handling event triggers
|
||||
*/
|
||||
#include <signal.h>
|
||||
// Functions for handling event triggers.
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "fallback.h" // IWYU pragma: keep
|
||||
#include "wutil.h" // IWYU pragma: keep
|
||||
#include "input_common.h"
|
||||
#include "proc.h"
|
||||
#include "parser.h"
|
||||
#include "common.h"
|
||||
#include "event.h"
|
||||
#include "signal.h"
|
||||
#include "fallback.h" // IWYU pragma: keep
|
||||
#include "input_common.h"
|
||||
#include "io.h"
|
||||
#include "parser.h"
|
||||
#include "proc.h"
|
||||
#include "signal.h"
|
||||
#include "wutil.h" // IWYU pragma: keep
|
||||
|
||||
/**
|
||||
Number of signals that can be queued before an overflow occurs
|
||||
*/
|
||||
/// Number of signals that can be queued before an overflow occurs.
|
||||
#define SIG_UNHANDLED_MAX 64
|
||||
|
||||
/**
|
||||
This struct contains a list of generated signals waiting to be
|
||||
dispatched
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
Number of delivered signals
|
||||
*/
|
||||
/// This struct contains a list of generated signals waiting to be dispatched.
|
||||
typedef struct {
|
||||
/// Number of delivered signals.
|
||||
volatile int count;
|
||||
/**
|
||||
Whether signals have been skipped
|
||||
*/
|
||||
/// Whether signals have been skipped.
|
||||
volatile int overflow;
|
||||
/**
|
||||
Array of signal events
|
||||
*/
|
||||
/// Array of signal events.
|
||||
volatile int signal[SIG_UNHANDLED_MAX];
|
||||
}
|
||||
signal_list_t;
|
||||
} signal_list_t;
|
||||
|
||||
/**
|
||||
The signal event list. Actually two separate lists. One which is
|
||||
active, which is the one that new events is written to. The inactive
|
||||
one contains the events that are currently beeing performed.
|
||||
*/
|
||||
static signal_list_t sig_list[2]= {{},{}};
|
||||
/// The signal event list. Actually two separate lists. One which is active, which is the one that
|
||||
/// new events is written to. The inactive one contains the events that are currently beeing
|
||||
/// performed.
|
||||
static signal_list_t sig_list[2] = {{}, {}};
|
||||
|
||||
/**
|
||||
The index of sig_list that is the list of signals currently written to
|
||||
*/
|
||||
static volatile int active_list=0;
|
||||
/// The index of sig_list that is the list of signals currently written to.
|
||||
static volatile int active_list = 0;
|
||||
|
||||
typedef std::vector<event_t *> event_list_t;
|
||||
|
||||
/**
|
||||
List of event handlers.
|
||||
*/
|
||||
/// List of event handlers.
|
||||
static event_list_t s_event_handlers;
|
||||
/**
|
||||
List of event handlers that should be removed
|
||||
*/
|
||||
/// List of event handlers that should be removed.
|
||||
static event_list_t killme;
|
||||
|
||||
/**
|
||||
List of events that have been sent but have not yet been delivered because they are blocked.
|
||||
*/
|
||||
/// List of events that have been sent but have not yet been delivered because they are blocked.
|
||||
static event_list_t blocked;
|
||||
|
||||
/** Variables (one per signal) set when a signal is observed. This is inspected by a signal handler. */
|
||||
/// Variables (one per signal) set when a signal is observed. This is inspected by a signal handler.
|
||||
static volatile bool s_observed_signals[NSIG] = {};
|
||||
static void set_signal_observed(int sig, bool val)
|
||||
{
|
||||
static void set_signal_observed(int sig, bool val) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
if (sig >= 0 && (size_t)sig < sizeof s_observed_signals / sizeof *s_observed_signals)
|
||||
{
|
||||
if (sig >= 0 && (size_t)sig < sizeof s_observed_signals / sizeof *s_observed_signals) {
|
||||
s_observed_signals[sig] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Tests if one event instance matches the definition of a event
|
||||
class. If both the class and the instance name a function,
|
||||
they must name the same function.
|
||||
|
||||
*/
|
||||
static int event_match(const event_t &classv, const event_t &instance)
|
||||
{
|
||||
|
||||
/* If the function names are both non-empty and different, then it's not a match */
|
||||
if (! classv.function_name.empty() &&
|
||||
! instance.function_name.empty() &&
|
||||
classv.function_name != instance.function_name)
|
||||
{
|
||||
/// Tests if one event instance matches the definition of a event class. If both the class and the
|
||||
/// instance name a function, they must name the same function.
|
||||
static int event_match(const event_t &classv, const event_t &instance) {
|
||||
// If the function names are both non-empty and different, then it's not a match.
|
||||
if (!classv.function_name.empty() && !instance.function_name.empty() &&
|
||||
classv.function_name != instance.function_name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (classv.type == EVENT_ANY)
|
||||
return 1;
|
||||
if (classv.type == EVENT_ANY) return 1;
|
||||
if (classv.type != instance.type) return 0;
|
||||
|
||||
if (classv.type != instance.type)
|
||||
return 0;
|
||||
|
||||
|
||||
switch (classv.type)
|
||||
{
|
||||
|
||||
case EVENT_SIGNAL:
|
||||
if (classv.param1.signal == EVENT_ANY_SIGNAL)
|
||||
return 1;
|
||||
switch (classv.type) {
|
||||
case EVENT_SIGNAL: {
|
||||
if (classv.param1.signal == EVENT_ANY_SIGNAL) return 1;
|
||||
return classv.param1.signal == instance.param1.signal;
|
||||
|
||||
case EVENT_VARIABLE:
|
||||
}
|
||||
case EVENT_VARIABLE: {
|
||||
return instance.str_param1 == classv.str_param1;
|
||||
|
||||
case EVENT_EXIT:
|
||||
if (classv.param1.pid == EVENT_ANY_PID)
|
||||
return 1;
|
||||
}
|
||||
case EVENT_EXIT: {
|
||||
if (classv.param1.pid == EVENT_ANY_PID) return 1;
|
||||
return classv.param1.pid == instance.param1.pid;
|
||||
|
||||
case EVENT_JOB_ID:
|
||||
}
|
||||
case EVENT_JOB_ID: {
|
||||
return classv.param1.job_id == instance.param1.job_id;
|
||||
|
||||
case EVENT_GENERIC:
|
||||
}
|
||||
case EVENT_GENERIC: {
|
||||
return instance.str_param1 == classv.str_param1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This should never be reached
|
||||
*/
|
||||
// This should never be reached.
|
||||
debug(0, "Warning: Unreachable code reached in event_match in event.cpp\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Test if specified event is blocked
|
||||
*/
|
||||
static int event_is_blocked(const event_t &e)
|
||||
{
|
||||
/// Test if specified event is blocked.
|
||||
static int event_is_blocked(const event_t &e) {
|
||||
const block_t *block;
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
|
||||
size_t idx = 0;
|
||||
while ((block = parser.block_at_index(idx++)))
|
||||
{
|
||||
if (event_block_list_blocks_type(block->event_blocks, e.type))
|
||||
return true;
|
||||
|
||||
while ((block = parser.block_at_index(idx++))) {
|
||||
if (event_block_list_blocks_type(block->event_blocks, e.type)) return true;
|
||||
}
|
||||
return event_block_list_blocks_type(parser.global_event_blocks, e.type);
|
||||
}
|
||||
|
||||
wcstring event_get_desc(const event_t &e)
|
||||
{
|
||||
|
||||
wcstring event_get_desc(const event_t &e) {
|
||||
wcstring result;
|
||||
switch (e.type)
|
||||
{
|
||||
|
||||
case EVENT_SIGNAL:
|
||||
result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e.param1.signal), signal_get_desc(e.param1.signal));
|
||||
break;
|
||||
|
||||
case EVENT_VARIABLE:
|
||||
result = format_string(_(L"handler for variable '%ls'"), e.str_param1.c_str());
|
||||
break;
|
||||
|
||||
case EVENT_EXIT:
|
||||
if (e.param1.pid > 0)
|
||||
{
|
||||
result = format_string(_(L"exit handler for process %d"), e.param1.pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
job_t *j = job_get_from_pid(-e.param1.pid);
|
||||
if (j)
|
||||
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr());
|
||||
else
|
||||
result = format_string(_(L"exit handler for job with process group %d"), -e.param1.pid);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EVENT_JOB_ID:
|
||||
{
|
||||
job_t *j = job_get(e.param1.job_id);
|
||||
if (j)
|
||||
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr());
|
||||
else
|
||||
result = format_string(_(L"exit handler for job with job id %d"), e.param1.job_id);
|
||||
|
||||
switch (e.type) {
|
||||
case EVENT_SIGNAL: {
|
||||
result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e.param1.signal),
|
||||
signal_get_desc(e.param1.signal));
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_GENERIC:
|
||||
case EVENT_VARIABLE: {
|
||||
result = format_string(_(L"handler for variable '%ls'"), e.str_param1.c_str());
|
||||
break;
|
||||
}
|
||||
case EVENT_EXIT: {
|
||||
if (e.param1.pid > 0) {
|
||||
result = format_string(_(L"exit handler for process %d"), e.param1.pid);
|
||||
} else {
|
||||
job_t *j = job_get_from_pid(-e.param1.pid);
|
||||
if (j)
|
||||
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id,
|
||||
j->command_wcstr());
|
||||
else
|
||||
result = format_string(_(L"exit handler for job with process group %d"),
|
||||
-e.param1.pid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_JOB_ID: {
|
||||
job_t *j = job_get(e.param1.job_id);
|
||||
if (j) {
|
||||
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id,
|
||||
j->command_wcstr());
|
||||
} else {
|
||||
result = format_string(_(L"exit handler for job with job id %d"), e.param1.job_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_GENERIC: {
|
||||
result = format_string(_(L"handler for generic event '%ls'"), e.str_param1.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
result = format_string(_(L"Unknown event type '0x%x'"), e.type);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void show_all_handlers(void)
|
||||
{
|
||||
static void show_all_handlers(void) {
|
||||
puts("event handlers:");
|
||||
for (event_list_t::const_iterator iter = events.begin(); iter != events.end(); ++iter)
|
||||
{
|
||||
for (event_list_t::const_iterator iter = events.begin(); iter != events.end(); ++iter) {
|
||||
const event_t *foo = *iter;
|
||||
wcstring tmp = event_get_desc(foo);
|
||||
printf(" handler now %ls\n", tmp.c_str());
|
||||
@@ -228,104 +166,84 @@ static void show_all_handlers(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Give a more condensed description of \c event compared to \c event_get_desc.
|
||||
It includes what function will fire if the \c event is an event handler.
|
||||
*/
|
||||
static wcstring event_desc_compact(const event_t &event)
|
||||
{
|
||||
/// Give a more condensed description of \c event compared to \c event_get_desc. It includes what
|
||||
/// function will fire if the \c event is an event handler.
|
||||
static wcstring event_desc_compact(const event_t &event) {
|
||||
wcstring res;
|
||||
wchar_t const *temp;
|
||||
int sig;
|
||||
switch (event.type)
|
||||
{
|
||||
case EVENT_ANY:
|
||||
switch (event.type) {
|
||||
case EVENT_ANY: {
|
||||
res = L"EVENT_ANY";
|
||||
break;
|
||||
case EVENT_VARIABLE:
|
||||
if (event.str_param1.c_str())
|
||||
{
|
||||
}
|
||||
case EVENT_VARIABLE: {
|
||||
if (event.str_param1.c_str()) {
|
||||
res = format_string(L"EVENT_VARIABLE($%ls)", event.str_param1.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
res = L"EVENT_VARIABLE([any])";
|
||||
}
|
||||
break;
|
||||
case EVENT_SIGNAL:
|
||||
}
|
||||
case EVENT_SIGNAL: {
|
||||
sig = event.param1.signal;
|
||||
if (sig == EVENT_ANY_SIGNAL)
|
||||
{
|
||||
if (sig == EVENT_ANY_SIGNAL) {
|
||||
temp = L"[all signals]";
|
||||
}
|
||||
else if (sig == 0)
|
||||
{
|
||||
} else if (sig == 0) {
|
||||
temp = L"not set";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
temp = sig2wcs(sig);
|
||||
}
|
||||
res = format_string(L"EVENT_SIGNAL(%ls)", temp);
|
||||
break;
|
||||
case EVENT_EXIT:
|
||||
if (event.param1.pid == EVENT_ANY_PID)
|
||||
{
|
||||
}
|
||||
case EVENT_EXIT: {
|
||||
if (event.param1.pid == EVENT_ANY_PID) {
|
||||
res = wcstring(L"EVENT_EXIT([all child processes])");
|
||||
}
|
||||
else if (event.param1.pid > 0)
|
||||
{
|
||||
} else if (event.param1.pid > 0) {
|
||||
res = format_string(L"EVENT_EXIT(pid %d)", event.param1.pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
job_t *j = job_get_from_pid(-event.param1.pid);
|
||||
if (j)
|
||||
res = format_string(L"EVENT_EXIT(jobid %d: \"%ls\")", j->job_id, j->command_wcstr());
|
||||
res = format_string(L"EVENT_EXIT(jobid %d: \"%ls\")", j->job_id,
|
||||
j->command_wcstr());
|
||||
else
|
||||
res = format_string(L"EVENT_EXIT(pgid %d)", -event.param1.pid);
|
||||
}
|
||||
break;
|
||||
case EVENT_JOB_ID:
|
||||
{
|
||||
}
|
||||
case EVENT_JOB_ID: {
|
||||
job_t *j = job_get(event.param1.job_id);
|
||||
if (j)
|
||||
res = format_string(L"EVENT_JOB_ID(job %d: \"%ls\")", j->job_id, j->command_wcstr());
|
||||
res =
|
||||
format_string(L"EVENT_JOB_ID(job %d: \"%ls\")", j->job_id, j->command_wcstr());
|
||||
else
|
||||
res = format_string(L"EVENT_JOB_ID(jobid %d)", event.param1.job_id);
|
||||
break;
|
||||
}
|
||||
case EVENT_GENERIC:
|
||||
case EVENT_GENERIC: {
|
||||
res = format_string(L"EVENT_GENERIC(%ls)", event.str_param1.c_str());
|
||||
break;
|
||||
default:
|
||||
res = format_string(L"unknown/illegal event(%x)", event.type);
|
||||
}
|
||||
default: { res = format_string(L"unknown/illegal event(%x)", event.type); }
|
||||
}
|
||||
if (event.function_name.size())
|
||||
{
|
||||
if (event.function_name.size()) {
|
||||
return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void event_add_handler(const event_t &event)
|
||||
{
|
||||
void event_add_handler(const event_t &event) {
|
||||
event_t *e;
|
||||
|
||||
if (debug_level >= 3)
|
||||
{
|
||||
if (debug_level >= 3) {
|
||||
wcstring desc = event_desc_compact(event);
|
||||
debug(3, "register: %ls\n", desc.c_str());
|
||||
}
|
||||
|
||||
e = new event_t(event);
|
||||
|
||||
if (e->type == EVENT_SIGNAL)
|
||||
{
|
||||
if (e->type == EVENT_SIGNAL) {
|
||||
signal_handle(e->param1.signal, 1);
|
||||
set_signal_observed(e->param1.signal, true);
|
||||
}
|
||||
@@ -333,106 +251,75 @@ void event_add_handler(const event_t &event)
|
||||
s_event_handlers.push_back(e);
|
||||
}
|
||||
|
||||
void event_remove(const event_t &criterion)
|
||||
{
|
||||
void event_remove(const event_t &criterion) {
|
||||
event_list_t new_list;
|
||||
|
||||
if (debug_level >= 3)
|
||||
{
|
||||
if (debug_level >= 3) {
|
||||
wcstring desc = event_desc_compact(criterion);
|
||||
debug(3, "unregister: %ls\n", desc.c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
Because of concurrency issues (env_remove could remove an event
|
||||
that is currently being executed), env_remove does not actually
|
||||
free any events - instead it simply moves all events that should
|
||||
be removed from the event list to the killme list, and the ones
|
||||
that shouldn't be killed to new_list, and then drops the empty
|
||||
events-list.
|
||||
*/
|
||||
// Because of concurrency issues (env_remove could remove an event that is currently being
|
||||
// executed), env_remove does not actually free any events - instead it simply moves all events
|
||||
// that should be removed from the event list to the killme list, and the ones that shouldn't be
|
||||
// killed to new_list, and then drops the empty events-list.
|
||||
if (s_event_handlers.empty()) return;
|
||||
|
||||
if (s_event_handlers.empty())
|
||||
return;
|
||||
|
||||
for (size_t i=0; i<s_event_handlers.size(); i++)
|
||||
{
|
||||
for (size_t i = 0; i < s_event_handlers.size(); i++) {
|
||||
event_t *n = s_event_handlers.at(i);
|
||||
if (event_match(criterion, *n))
|
||||
{
|
||||
if (event_match(criterion, *n)) {
|
||||
killme.push_back(n);
|
||||
|
||||
/*
|
||||
If this event was a signal handler and no other handler handles
|
||||
the specified signal type, do not handle that type of signal any
|
||||
more.
|
||||
*/
|
||||
if (n->type == EVENT_SIGNAL)
|
||||
{
|
||||
// If this event was a signal handler and no other handler handles the specified signal
|
||||
// type, do not handle that type of signal any more.
|
||||
if (n->type == EVENT_SIGNAL) {
|
||||
event_t e = event_t::signal_event(n->param1.signal);
|
||||
if (event_get(e, 0) == 1)
|
||||
{
|
||||
if (event_get(e, 0) == 1) {
|
||||
signal_handle(e.param1.signal, 0);
|
||||
set_signal_observed(e.param1.signal, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
new_list.push_back(n);
|
||||
}
|
||||
}
|
||||
s_event_handlers.swap(new_list);
|
||||
}
|
||||
|
||||
int event_get(const event_t &criterion, std::vector<event_t *> *out)
|
||||
{
|
||||
int event_get(const event_t &criterion, std::vector<event_t *> *out) {
|
||||
int found = 0;
|
||||
for (size_t i=0; i < s_event_handlers.size(); i++)
|
||||
{
|
||||
for (size_t i = 0; i < s_event_handlers.size(); i++) {
|
||||
event_t *n = s_event_handlers.at(i);
|
||||
if (event_match(criterion, *n))
|
||||
{
|
||||
if (event_match(criterion, *n)) {
|
||||
found++;
|
||||
if (out)
|
||||
out->push_back(n);
|
||||
if (out) out->push_back(n);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool event_is_signal_observed(int sig)
|
||||
{
|
||||
/* We are in a signal handler! Don't allocate memory, etc.
|
||||
*/
|
||||
bool event_is_signal_observed(int sig) {
|
||||
// We are in a signal handler! Don't allocate memory, etc.
|
||||
bool result = false;
|
||||
if (sig >= 0 && sig < sizeof s_observed_signals / sizeof *s_observed_signals)
|
||||
{
|
||||
if (sig >= 0 && sig < sizeof s_observed_signals / sizeof *s_observed_signals) {
|
||||
result = s_observed_signals[sig];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
Free all events in the kill list
|
||||
*/
|
||||
static void event_free_kills()
|
||||
{
|
||||
/// Free all events in the kill list.
|
||||
static void event_free_kills() {
|
||||
for_each(killme.begin(), killme.end(), event_free);
|
||||
killme.resize(0);
|
||||
}
|
||||
|
||||
/**
|
||||
Test if the specified event is waiting to be killed
|
||||
*/
|
||||
static int event_is_killed(const event_t &e)
|
||||
{
|
||||
/// Test if the specified event is waiting to be killed.
|
||||
static int event_is_killed(const event_t &e) {
|
||||
return std::find(killme.begin(), killme.end(), &e) != killme.end();
|
||||
}
|
||||
|
||||
/* Callback for firing (and then deleting) an event */
|
||||
static void fire_event_callback(void *arg)
|
||||
{
|
||||
/// Callback for firing (and then deleting) an event.
|
||||
static void fire_event_callback(void *arg) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
assert(arg != NULL);
|
||||
event_t *event = static_cast<event_t *>(arg);
|
||||
@@ -440,83 +327,53 @@ static void fire_event_callback(void *arg)
|
||||
delete event;
|
||||
}
|
||||
|
||||
/**
|
||||
Perform the specified event. Since almost all event firings will
|
||||
not be matched by even a single event handler, we make sure to
|
||||
optimize the 'no matches' path. This means that nothing is
|
||||
allocated/initialized unless needed.
|
||||
*/
|
||||
static void event_fire_internal(const event_t &event)
|
||||
{
|
||||
|
||||
/// Perform the specified event. Since almost all event firings will not be matched by even a single
|
||||
/// event handler, we make sure to optimize the 'no matches' path. This means that nothing is
|
||||
/// allocated/initialized unless needed.
|
||||
static void event_fire_internal(const event_t &event) {
|
||||
event_list_t fire;
|
||||
|
||||
/*
|
||||
First we free all events that have been removed, but only if this
|
||||
invocation of event_fire_internal is not a recursive call.
|
||||
*/
|
||||
if (is_event <= 1)
|
||||
event_free_kills();
|
||||
// First we free all events that have been removed, but only if this invocation of
|
||||
// event_fire_internal is not a recursive call.
|
||||
if (is_event <= 1) event_free_kills();
|
||||
|
||||
if (s_event_handlers.empty())
|
||||
return;
|
||||
if (s_event_handlers.empty()) return;
|
||||
|
||||
/*
|
||||
Then we iterate over all events, adding events that should be
|
||||
fired to a second list. We need to do this in a separate step
|
||||
since an event handler might call event_remove or
|
||||
event_add_handler, which will change the contents of the \c
|
||||
events list.
|
||||
*/
|
||||
for (size_t i=0; i<s_event_handlers.size(); i++)
|
||||
{
|
||||
// Then we iterate over all events, adding events that should be fired to a second list. We need
|
||||
// to do this in a separate step since an event handler might call event_remove or
|
||||
// event_add_handler, which will change the contents of the \c events list.
|
||||
for (size_t i = 0; i < s_event_handlers.size(); i++) {
|
||||
event_t *criterion = s_event_handlers.at(i);
|
||||
|
||||
/*
|
||||
Check if this event is a match
|
||||
*/
|
||||
if (event_match(*criterion, event))
|
||||
{
|
||||
// Check if this event is a match.
|
||||
if (event_match(*criterion, event)) {
|
||||
fire.push_back(criterion);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
No matches. Time to return.
|
||||
*/
|
||||
if (fire.empty())
|
||||
return;
|
||||
// No matches. Time to return.
|
||||
if (fire.empty()) return;
|
||||
|
||||
if (signal_is_blocked())
|
||||
{
|
||||
/* Fix for https://github.com/fish-shell/fish-shell/issues/608. Don't run event handlers while signals are blocked. */
|
||||
if (signal_is_blocked()) {
|
||||
// Fix for https://github.com/fish-shell/fish-shell/issues/608. Don't run event handlers
|
||||
// while signals are blocked.
|
||||
event_t *heap_event = new event_t(event);
|
||||
input_common_add_callback(fire_event_callback, heap_event);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over our list of matching events
|
||||
*/
|
||||
|
||||
for (size_t i=0; i<fire.size(); i++)
|
||||
{
|
||||
// Iterate over our list of matching events.
|
||||
for (size_t i = 0; i < fire.size(); i++) {
|
||||
event_t *criterion = fire.at(i);
|
||||
int prev_status;
|
||||
|
||||
/*
|
||||
Check if this event has been removed, if so, dont fire it
|
||||
*/
|
||||
if (event_is_killed(*criterion))
|
||||
continue;
|
||||
// Check if this event has been removed, if so, dont fire it.
|
||||
if (event_is_killed(*criterion)) continue;
|
||||
|
||||
/*
|
||||
Fire event
|
||||
*/
|
||||
// Fire event.
|
||||
wcstring buffer = criterion->function_name;
|
||||
|
||||
for (size_t j=0; j < event.arguments.size(); j++)
|
||||
{
|
||||
for (size_t j = 0; j < event.arguments.size(); j++) {
|
||||
wcstring arg_esc = escape_string(event.arguments.at(j), 1);
|
||||
buffer += L" ";
|
||||
buffer += arg_esc;
|
||||
@@ -524,10 +381,8 @@ static void event_fire_internal(const event_t &event)
|
||||
|
||||
// debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );
|
||||
|
||||
/*
|
||||
Event handlers are not part of the main flow of code, so
|
||||
they are marked as non-interactive
|
||||
*/
|
||||
// Event handlers are not part of the main flow of code, so they are marked as
|
||||
// non-interactive.
|
||||
proc_push_interactive(0);
|
||||
prev_status = proc_get_last_status();
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
@@ -540,39 +395,24 @@ static void event_fire_internal(const event_t &event)
|
||||
proc_set_last_status(prev_status);
|
||||
}
|
||||
|
||||
/*
|
||||
Free killed events
|
||||
*/
|
||||
if (is_event <= 1)
|
||||
event_free_kills();
|
||||
|
||||
// Free killed events.
|
||||
if (is_event <= 1) event_free_kills();
|
||||
}
|
||||
|
||||
/**
|
||||
Handle all pending signal events
|
||||
*/
|
||||
static void event_fire_delayed()
|
||||
{
|
||||
/*
|
||||
If is_event is one, we are running the event-handler non-recursively.
|
||||
|
||||
When the event handler has called a piece of code that triggers
|
||||
another event, we do not want to fire delayed events because of
|
||||
concurrency problems.
|
||||
*/
|
||||
if (! blocked.empty() && is_event==1)
|
||||
{
|
||||
/// Handle all pending signal events.
|
||||
static void event_fire_delayed() {
|
||||
// If is_event is one, we are running the event-handler non-recursively.
|
||||
//
|
||||
// When the event handler has called a piece of code that triggers another event, we do not want
|
||||
// to fire delayed events because of concurrency problems.
|
||||
if (!blocked.empty() && is_event == 1) {
|
||||
event_list_t new_blocked;
|
||||
|
||||
for (size_t i=0; i<blocked.size(); i++)
|
||||
{
|
||||
for (size_t i = 0; i < blocked.size(); i++) {
|
||||
event_t *e = blocked.at(i);
|
||||
if (event_is_blocked(*e))
|
||||
{
|
||||
if (event_is_blocked(*e)) {
|
||||
new_blocked.push_back(new event_t(*e));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
event_fire_internal(*e);
|
||||
event_free(e);
|
||||
}
|
||||
@@ -582,89 +422,59 @@ static void event_fire_delayed()
|
||||
|
||||
int al = active_list;
|
||||
|
||||
while (sig_list[al].count > 0)
|
||||
{
|
||||
while (sig_list[al].count > 0) {
|
||||
signal_list_t *lst;
|
||||
|
||||
/*
|
||||
Switch signal lists
|
||||
*/
|
||||
sig_list[1-al].count=0;
|
||||
sig_list[1-al].overflow=0;
|
||||
al = 1-al;
|
||||
active_list=al;
|
||||
// Switch signal lists.
|
||||
sig_list[1 - al].count = 0;
|
||||
sig_list[1 - al].overflow = 0;
|
||||
al = 1 - al;
|
||||
active_list = al;
|
||||
|
||||
/*
|
||||
Set up
|
||||
*/
|
||||
lst = &sig_list[1-al];
|
||||
// Set up.
|
||||
lst = &sig_list[1 - al];
|
||||
event_t e = event_t::signal_event(0);
|
||||
e.arguments.resize(1);
|
||||
|
||||
if (lst->overflow)
|
||||
{
|
||||
if (lst->overflow) {
|
||||
debug(0, _(L"Signal list overflow. Signals have been ignored."));
|
||||
}
|
||||
|
||||
/*
|
||||
Send all signals in our private list
|
||||
*/
|
||||
for (int i=0; i < lst->count; i++)
|
||||
{
|
||||
// Send all signals in our private list.
|
||||
for (int i = 0; i < lst->count; i++) {
|
||||
e.param1.signal = lst->signal[i];
|
||||
e.arguments.at(0) = sig2wcs(e.param1.signal);
|
||||
if (event_is_blocked(e))
|
||||
{
|
||||
if (event_is_blocked(e)) {
|
||||
blocked.push_back(new event_t(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
event_fire_internal(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void event_fire_signal(int signal)
|
||||
{
|
||||
/*
|
||||
This means we are in a signal handler. We must be very
|
||||
careful not do do anything that could cause a memory
|
||||
allocation or something else that might be bad when in a
|
||||
signal handler.
|
||||
*/
|
||||
void event_fire_signal(int signal) {
|
||||
// This means we are in a signal handler. We must be very careful not do do anything that could
|
||||
// cause a memory allocation or something else that might be bad when in a signal handler.
|
||||
if (sig_list[active_list].count < SIG_UNHANDLED_MAX)
|
||||
sig_list[active_list].signal[sig_list[active_list].count++]=signal;
|
||||
sig_list[active_list].signal[sig_list[active_list].count++] = signal;
|
||||
else
|
||||
sig_list[active_list].overflow=1;
|
||||
sig_list[active_list].overflow = 1;
|
||||
}
|
||||
|
||||
|
||||
void event_fire(const event_t *event)
|
||||
{
|
||||
|
||||
if (event && event->type == EVENT_SIGNAL)
|
||||
{
|
||||
void event_fire(const event_t *event) {
|
||||
if (event && event->type == EVENT_SIGNAL) {
|
||||
event_fire_signal(event->param1.signal);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
is_event++;
|
||||
|
||||
/*
|
||||
Fire events triggered by signals
|
||||
*/
|
||||
// Fire events triggered by signals.
|
||||
event_fire_delayed();
|
||||
|
||||
if (event)
|
||||
{
|
||||
if (event_is_blocked(*event))
|
||||
{
|
||||
if (event) {
|
||||
if (event_is_blocked(*event)) {
|
||||
blocked.push_back(new event_t(*event));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
event_fire_internal(*event);
|
||||
}
|
||||
}
|
||||
@@ -672,14 +482,9 @@ void event_fire(const event_t *event)
|
||||
}
|
||||
}
|
||||
|
||||
void event_init() {}
|
||||
|
||||
void event_init()
|
||||
{
|
||||
}
|
||||
|
||||
void event_destroy()
|
||||
{
|
||||
|
||||
void event_destroy() {
|
||||
for_each(s_event_handlers.begin(), s_event_handlers.end(), event_free);
|
||||
s_event_handlers.clear();
|
||||
|
||||
@@ -687,49 +492,38 @@ void event_destroy()
|
||||
killme.clear();
|
||||
}
|
||||
|
||||
void event_free(event_t *e)
|
||||
{
|
||||
CHECK(e,);
|
||||
void event_free(event_t *e) {
|
||||
CHECK(e, );
|
||||
delete e;
|
||||
}
|
||||
|
||||
void event_fire_generic(const wchar_t *name, wcstring_list_t *args)
|
||||
{
|
||||
CHECK(name,);
|
||||
void event_fire_generic(const wchar_t *name, wcstring_list_t *args) {
|
||||
CHECK(name, );
|
||||
|
||||
event_t ev(EVENT_GENERIC);
|
||||
ev.str_param1 = name;
|
||||
if (args)
|
||||
ev.arguments = *args;
|
||||
if (args) ev.arguments = *args;
|
||||
event_fire(&ev);
|
||||
}
|
||||
|
||||
event_t::event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments()
|
||||
{
|
||||
}
|
||||
event_t::event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() {}
|
||||
|
||||
event_t::~event_t()
|
||||
{
|
||||
}
|
||||
event_t::~event_t() {}
|
||||
|
||||
event_t event_t::signal_event(int sig)
|
||||
{
|
||||
event_t event_t::signal_event(int sig) {
|
||||
event_t event(EVENT_SIGNAL);
|
||||
event.param1.signal = sig;
|
||||
return event;
|
||||
}
|
||||
|
||||
event_t event_t::variable_event(const wcstring &str)
|
||||
{
|
||||
event_t event_t::variable_event(const wcstring &str) {
|
||||
event_t event(EVENT_VARIABLE);
|
||||
event.str_param1 = str;
|
||||
return event;
|
||||
}
|
||||
|
||||
event_t event_t::generic_event(const wcstring &str)
|
||||
{
|
||||
event_t event_t::generic_event(const wcstring &str) {
|
||||
event_t event(EVENT_GENERIC);
|
||||
event.str_param1 = str;
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
205
src/event.h
205
src/event.h
@@ -1,91 +1,76 @@
|
||||
/** \file event.h
|
||||
|
||||
Functions for handling event triggers
|
||||
|
||||
Because most of these functions can be called by signal
|
||||
handler, it is important to make it well defined when these
|
||||
functions produce output or perform memory allocations, since
|
||||
such functions may not be safely called by signal handlers.
|
||||
|
||||
|
||||
*/
|
||||
// Functions for handling event triggers
|
||||
//
|
||||
// Because most of these functions can be called by signal handler, it is important to make it well
|
||||
// defined when these functions produce output or perform memory allocations, since such functions
|
||||
// may not be safely called by signal handlers.
|
||||
#ifndef FISH_EVENT_H
|
||||
#define FISH_EVENT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
The signal number that is used to match any signal
|
||||
*/
|
||||
/// The signal number that is used to match any signal.
|
||||
#define EVENT_ANY_SIGNAL -1
|
||||
|
||||
/**
|
||||
The process id that is used to match any process id
|
||||
*/
|
||||
/// The process id that is used to match any process id.
|
||||
#define EVENT_ANY_PID 0
|
||||
|
||||
/**
|
||||
Enumeration of event types
|
||||
*/
|
||||
enum
|
||||
{
|
||||
EVENT_ANY, /**< Matches any event type (Not always any event, as the function name may limit the choice as well */
|
||||
EVENT_SIGNAL, /**< An event triggered by a signal */
|
||||
EVENT_VARIABLE, /**< An event triggered by a variable update */
|
||||
EVENT_EXIT, /**< An event triggered by a job or process exit */
|
||||
EVENT_JOB_ID, /**< An event triggered by a job exit */
|
||||
EVENT_GENERIC, /**< A generic event */
|
||||
}
|
||||
;
|
||||
/// Enumeration of event types.
|
||||
enum {
|
||||
/// Matches any event type (Not always any event, as the function name may limit the choice as
|
||||
/// well.
|
||||
EVENT_ANY,
|
||||
/// An event triggered by a signal.
|
||||
EVENT_SIGNAL,
|
||||
/// An event triggered by a variable update.
|
||||
EVENT_VARIABLE,
|
||||
/// An event triggered by a job or process exit.
|
||||
EVENT_EXIT,
|
||||
/// An event triggered by a job exit.
|
||||
EVENT_JOB_ID,
|
||||
/// A generic event.
|
||||
EVENT_GENERIC,
|
||||
};
|
||||
|
||||
/**
|
||||
The structure which represents an event. The event_t struct has
|
||||
several event-related use-cases:
|
||||
|
||||
- When used as a parameter to event_add, it represents a class of events, and function_name is the name of the function which will be called whenever an event matching the specified class occurs. This is also how events are stored internally.
|
||||
- When used as a parameter to event_get, event_remove and event_fire, it represents a class of events, and if the function_name field is non-zero, only events which call the specified function will be returned.
|
||||
*/
|
||||
struct event_t
|
||||
{
|
||||
public:
|
||||
|
||||
/** Type of event */
|
||||
/// The structure which represents an event. The event_t struct has several event-related use-cases:
|
||||
///
|
||||
/// - When used as a parameter to event_add, it represents a class of events, and function_name is
|
||||
/// the name of the function which will be called whenever an event matching the specified class
|
||||
/// occurs. This is also how events are stored internally.
|
||||
///
|
||||
/// - When used as a parameter to event_get, event_remove and event_fire, it represents a class of
|
||||
/// events, and if the function_name field is non-zero, only events which call the specified
|
||||
/// function will be returned.
|
||||
struct event_t {
|
||||
public:
|
||||
/// Type of event.
|
||||
int type;
|
||||
|
||||
/** The type-specific parameter. The int types are one of the following:
|
||||
|
||||
signal: Signal number for signal-type events.Use EVENT_ANY_SIGNAL to match any signal
|
||||
pid: Process id for process-type events. Use EVENT_ANY_PID to match any pid.
|
||||
job_id: Job id for EVENT_JOB_ID type events
|
||||
*/
|
||||
union
|
||||
{
|
||||
/// The type-specific parameter. The int types are one of the following:
|
||||
///
|
||||
/// signal: Signal number for signal-type events.Use EVENT_ANY_SIGNAL to match any signal
|
||||
/// pid: Process id for process-type events. Use EVENT_ANY_PID to match any pid.
|
||||
/// job_id: Job id for EVENT_JOB_ID type events
|
||||
union {
|
||||
int signal;
|
||||
int job_id;
|
||||
pid_t pid;
|
||||
} param1;
|
||||
|
||||
/** The string types are one of the following:
|
||||
|
||||
variable: Variable name for variable-type events.
|
||||
param: The parameter describing this generic event.
|
||||
*/
|
||||
/// The string types are one of the following:
|
||||
///
|
||||
/// variable: Variable name for variable-type events.
|
||||
/// param: The parameter describing this generic event.
|
||||
wcstring str_param1;
|
||||
|
||||
/**
|
||||
The name of the event handler function
|
||||
*/
|
||||
/// The name of the event handler function.
|
||||
wcstring function_name;
|
||||
|
||||
/**
|
||||
The argument list. Only used when sending a new event using
|
||||
event_fire. In all other situations, the value of this variable
|
||||
is ignored.
|
||||
*/
|
||||
/// The argument list. Only used when sending a new event using event_fire. In all other
|
||||
/// situations, the value of this variable is ignored.
|
||||
wcstring_list_t arguments;
|
||||
|
||||
explicit event_t(int t);
|
||||
@@ -96,83 +81,61 @@ struct event_t
|
||||
static event_t generic_event(const wcstring &str);
|
||||
};
|
||||
|
||||
/**
|
||||
Add an event handler
|
||||
|
||||
May not be called by a signal handler, since it may allocate new memory.
|
||||
*/
|
||||
/// Add an event handler.
|
||||
///
|
||||
/// May not be called by a signal handler, since it may allocate new memory.
|
||||
void event_add_handler(const event_t &event);
|
||||
|
||||
/**
|
||||
Remove all events matching the specified criterion.
|
||||
|
||||
May not be called by a signal handler, since it may free allocated memory.
|
||||
*/
|
||||
/// Remove all events matching the specified criterion.
|
||||
///
|
||||
/// May not be called by a signal handler, since it may free allocated memory.
|
||||
void event_remove(const event_t &event);
|
||||
|
||||
/**
|
||||
Return all events which match the specified event class
|
||||
|
||||
This function is safe to call from a signal handler _ONLY_ if the
|
||||
out parameter is null.
|
||||
|
||||
\param criterion Is the class of events to return. If the criterion has a non-null function_name, only events which trigger the specified function will return.
|
||||
\param out the list to add events to. May be 0, in which case no events will be added, but the result count will still be valid
|
||||
|
||||
\return the number of found matches
|
||||
*/
|
||||
/// Return all events which match the specified event class
|
||||
///
|
||||
/// This function is safe to call from a signal handler _ONLY_ if the out parameter is null.
|
||||
///
|
||||
/// \param criterion Is the class of events to return. If the criterion has a non-null
|
||||
/// function_name, only events which trigger the specified function will return.
|
||||
/// \param out the list to add events to. May be 0, in which case no events will be added, but the
|
||||
/// result count will still be valid
|
||||
///
|
||||
/// \return the number of found matches
|
||||
int event_get(const event_t &criterion, std::vector<event_t *> *out);
|
||||
|
||||
/**
|
||||
Returns whether an event listener is registered for the given signal.
|
||||
This is safe to call from a signal handler.
|
||||
*/
|
||||
/// Returns whether an event listener is registered for the given signal. This is safe to call from
|
||||
/// a signal handler.
|
||||
bool event_is_signal_observed(int signal);
|
||||
|
||||
/**
|
||||
Fire the specified event. The function_name field of the event must
|
||||
be set to 0. If the event is of type EVENT_SIGNAL, no the event is
|
||||
queued, and will be dispatched the next time event_fire is
|
||||
called. If event is a null-pointer, all pending events are
|
||||
dispatched.
|
||||
|
||||
This function is safe to call from a signal handler _ONLY_ if the
|
||||
event parameter is for a signal. Signal events not be fired, by the
|
||||
call to event_fire, instead they will be fired the next time
|
||||
event_fire is called with anull argument. This is needed to make
|
||||
sure that no code evaluation is ever performed by a signal handler.
|
||||
|
||||
\param event the specific event whose handlers should fire. If
|
||||
null, then all delayed events will be fired.
|
||||
*/
|
||||
/// Fire the specified event. The function_name field of the event must be set to 0. If the event is
|
||||
/// of type EVENT_SIGNAL, no the event is queued, and will be dispatched the next time event_fire is
|
||||
/// called. If event is a null-pointer, all pending events are dispatched.
|
||||
///
|
||||
/// This function is safe to call from a signal handler _ONLY_ if the event parameter is for a
|
||||
/// signal. Signal events not be fired, by the call to event_fire, instead they will be fired the
|
||||
/// next time event_fire is called with anull argument. This is needed to make sure that no code
|
||||
/// evaluation is ever performed by a signal handler.
|
||||
///
|
||||
/// \param event the specific event whose handlers should fire. If null, then all delayed events
|
||||
/// will be fired.
|
||||
void event_fire(const event_t *event);
|
||||
|
||||
/** Like event_fire, but takes a signal directly. */
|
||||
/// Like event_fire, but takes a signal directly.
|
||||
void event_fire_signal(int signal);
|
||||
|
||||
/**
|
||||
Initialize the event-handling library
|
||||
*/
|
||||
/// Initialize the event-handling library.
|
||||
void event_init();
|
||||
|
||||
/**
|
||||
Destroy the event-handling library
|
||||
*/
|
||||
/// Destroy the event-handling library.
|
||||
void event_destroy();
|
||||
|
||||
/**
|
||||
Free all memory used by the specified event
|
||||
*/
|
||||
/// Free all memory used by the specified event.
|
||||
void event_free(event_t *e);
|
||||
|
||||
/**
|
||||
Returns a string describing the specified event.
|
||||
*/
|
||||
/// Returns a string describing the specified event.
|
||||
wcstring event_get_desc(const event_t &e);
|
||||
|
||||
/**
|
||||
Fire a generic event with the specified name
|
||||
*/
|
||||
/// Fire a generic event with the specified name.
|
||||
void event_fire_generic(const wchar_t *name, wcstring_list_t *args = NULL);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user