From b4f53143b0e05fd3061cdf2e65e17a6a2904090b Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 24 Jul 2015 00:50:58 -0700 Subject: Migrate source files into src/ directory This change moves source files into a src/ directory, and puts object files into an obj/ directory. The Makefile and xcode project are updated accordingly. Fixes #1866 --- src/event.cpp | 742 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 src/event.cpp (limited to 'src/event.cpp') diff --git a/src/event.cpp b/src/event.cpp new file mode 100644 index 00000000..14def5f4 --- /dev/null +++ b/src/event.cpp @@ -0,0 +1,742 @@ +/** \file event.c + + Functions for handling event triggers + +*/ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fallback.h" +#include "util.h" + +#include "wutil.h" +#include "function.h" +#include "input_common.h" +#include "proc.h" +#include "parser.h" +#include "common.h" +#include "event.h" +#include "signal.h" + + +/** + 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 + */ + volatile int count; + /** + Whether signals have been skipped + */ + volatile int overflow; + /** + Array of signal events + */ + volatile int signal[SIG_UNHANDLED_MAX]; +} +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 index of sig_list that is the list of signals currently written to +*/ +static volatile int active_list=0; + +typedef std::vector event_list_t; + +/** + List of event handlers. +*/ +static event_list_t s_event_handlers; +/** + 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. +*/ +static event_list_t blocked; + +/** 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) +{ + ASSERT_IS_MAIN_THREAD(); + 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) + { + return 0; + } + + if (classv.type == EVENT_ANY) + return 1; + + if (classv.type != instance.type) + return 0; + + + 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: + return instance.str_param1 == classv.str_param1; + + case EVENT_EXIT: + if (classv.param1.pid == EVENT_ANY_PID) + return 1; + return classv.param1.pid == instance.param1.pid; + + case EVENT_JOB_ID: + return classv.param1.job_id == instance.param1.job_id; + + case EVENT_GENERIC: + return instance.str_param1 == classv.str_param1; + + } + + /** + 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) +{ + 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; + + } + return event_block_list_blocks_type(parser.global_event_blocks, e.type); +} + +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); + + break; + } + + case EVENT_GENERIC: + result = format_string(_(L"handler for generic event '%ls'"), e.str_param1.c_str()); + break; + + default: + result = format_string(_(L"Unknown event type '0x%x'"), e.type); + break; + + } + + return result; +} + +#if 0 +static void show_all_handlers(void) +{ + puts("event handlers:"); + 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()); + } +} +#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) +{ + wcstring res; + wchar_t const *temp; + int sig; + switch (event.type) + { + case EVENT_ANY: + res = L"EVENT_ANY"; + break; + case EVENT_VARIABLE: + if (event.str_param1.c_str()) + { + res = format_string(L"EVENT_VARIABLE($%ls)", event.str_param1.c_str()); + } + else + { + res = L"EVENT_VARIABLE([any])"; + } + break; + case EVENT_SIGNAL: + sig = event.param1.signal; + if (sig == EVENT_ANY_SIGNAL) + { + temp = L"[all signals]"; + } + else if (sig == 0) + { + temp = L"not set"; + } + else + { + temp = sig2wcs(sig); + } + res = format_string(L"EVENT_SIGNAL(%ls)", temp); + break; + case EVENT_EXIT: + if (event.param1.pid == EVENT_ANY_PID) + { + res = wcstring(L"EVENT_EXIT([all child processes])"); + } + else if (event.param1.pid > 0) + { + res = format_string(L"EVENT_EXIT(pid %d)", event.param1.pid); + } + 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()); + else + res = format_string(L"EVENT_EXIT(pgid %d)", -event.param1.pid); + } + break; + 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()); + else + res = format_string(L"EVENT_JOB_ID(jobid %d)", event.param1.job_id); + break; + } + 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); + } + if (event.function_name.size()) + { + return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str()); + } + else + { + return res; + } +} + + +void event_add_handler(const event_t &event) +{ + event_t *e; + + 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) + { + signal_handle(e->param1.signal, 1); + set_signal_observed(e->param1.signal, true); + } + + s_event_handlers.push_back(e); +} + +void event_remove(const event_t &criterion) +{ + event_list_t new_list; + + 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. + */ + + if (s_event_handlers.empty()) + return; + + for (size_t i=0; itype == EVENT_SIGNAL) + { + event_t e = event_t::signal_event(n->param1.signal); + if (event_get(e, 0) == 1) + { + signal_handle(e.param1.signal, 0); + set_signal_observed(e.param1.signal, 0); + } + } + } + else + { + new_list.push_back(n); + } + } + s_event_handlers.swap(new_list); +} + +int event_get(const event_t &criterion, std::vector *out) +{ + int found = 0; + for (size_t i=0; i < s_event_handlers.size(); i++) + { + event_t *n = s_event_handlers.at(i); + if (event_match(criterion, *n)) + { + found++; + 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 result = false; + 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() +{ + 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) +{ + 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) +{ + ASSERT_IS_MAIN_THREAD(); + assert(arg != NULL); + event_t *event = static_cast(arg); + event_fire(event); + 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) +{ + + 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(); + + 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; ifunction_name; + + 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; + } + + // 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 + */ + proc_push_interactive(0); + prev_status = proc_get_last_status(); + parser_t &parser = parser_t::principal_parser(); + + block_t *block = new event_block_t(event); + parser.push_block(block); + parser.eval(buffer, io_chain_t(), TOP); + parser.pop_block(); + proc_pop_interactive(); + proc_set_last_status(prev_status); + } + + /* + 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) + { + event_list_t new_blocked; + + for (size_t i=0; i 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; + + /* + Set up + */ + lst = &sig_list[1-al]; + event_t e = event_t::signal_event(0); + e.arguments.resize(1); + + 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++) + { + e.param1.signal = lst->signal[i]; + e.arguments.at(0) = sig2wcs(e.param1.signal); + if (event_is_blocked(e)) + { + blocked.push_back(new event_t(e)); + } + 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. + */ + if (sig_list[active_list].count < SIG_UNHANDLED_MAX) + sig_list[active_list].signal[sig_list[active_list].count++]=signal; + else + sig_list[active_list].overflow=1; +} + + +void event_fire(const event_t *event) +{ + + if (event && event->type == EVENT_SIGNAL) + { + event_fire_signal(event->param1.signal); + } + else + { + is_event++; + + /* + Fire events triggered by signals + */ + event_fire_delayed(); + + if (event) + { + if (event_is_blocked(*event)) + { + blocked.push_back(new event_t(*event)); + } + else + { + event_fire_internal(*event); + } + } + is_event--; + } +} + + +void event_init() +{ +} + +void event_destroy() +{ + + for_each(s_event_handlers.begin(), s_event_handlers.end(), event_free); + s_event_handlers.clear(); + + for_each(killme.begin(), killme.end(), event_free); + killme.clear(); +} + +void event_free(event_t *e) +{ + CHECK(e,); + delete e; +} + +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; + event_fire(&ev); +} + +event_t::event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() +{ +} + +event_t::~event_t() +{ +} + +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(EVENT_VARIABLE); + event.str_param1 = str; + return event; +} + +event_t event_t::generic_event(const wcstring &str) +{ + event_t event(EVENT_GENERIC); + event.str_param1 = str; + return event; +} + -- cgit v1.2.3