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; i < s_event_handlers.size(); i++) { event_t *n = s_event_handlers.at(i); 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) { 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); }
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()); } }
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++) { event_t *n = s_event_handlers.at(i); if (event_match(criterion, *n)) { found++; if (out) out->push_back(n); } } return found; }
/// 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++) { event_t *e = blocked.at(i); if (event_is_blocked(*e)) { new_blocked.push_back(new event_t(*e)); } else { event_fire_internal(*e); event_free(e); } } blocked.swap(new_blocked); } int al = active_list; 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; // 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_remove(event_t *criterion) { size_t i; event_list_t new_list; CHECK(criterion,); /* 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 (events.empty()) return; for (i=0; i<events.size(); i++) { event_t *n = events.at(i); 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) { event_t e = event_t::signal_event(n->param1.signal); if (event_get(&e, 0) == 1) { signal_handle(e.param1.signal, 0); } } } else { new_list.push_back(n); } } signal_block(); events.swap(new_list); signal_unblock(); }
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--; } }
int event_get(const event_t &criterion, std::vector<event_t *> *out) { size_t i; int found = 0; if (events.empty()) return 0; for (i=0; i<events.size(); i++) { event_t *n = events.at(i); if (event_match(criterion, *n)) { found++; if (out) out->push_back(n); } } return found; }
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_destroy() { for_each(events.begin(), events.end(), event_free); events.clear(); for_each(killme.begin(), killme.end(), event_free); killme.clear(); }
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_add_handler(const event_t *event) { event_t *e; CHECK(event,); e = event_copy(event, 0); if (e->type == EVENT_SIGNAL) { signal_handle(e->param1.signal, 1); } // Block around updating the events vector signal_block(); events.push_back(e); signal_unblock(); }
bool event_is_signal_observed(int sig) { /* We are in a signal handler! Don't allocate memory, etc. This does what event_match does, except it doesn't require passing in an event_t. */ size_t i, max = events.size(); for (i=0; i < max; i++) { const event_t *event = events[i]; if (event->type == EVENT_ANY) { return true; } else if (event->type == EVENT_SIGNAL) { if (event->param1.signal == EVENT_ANY_SIGNAL || event->param1.signal == sig) return true; } } return false; }
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); } // Block around updating the events vector signal_block(); events.push_back(e); signal_unblock(); }
/** Handle all pending signal events */ static void event_fire_delayed() { size_t i; /* 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 (i=0; i<blocked.size(); i++) { event_t *e = blocked.at(i); if (event_is_blocked(e)) { new_blocked.push_back(e); } else { event_fire_internal(e); event_free(e); } } blocked.swap(new_blocked); } while (sig_list[active_list].count > 0) { signal_list_t *lst; /* Switch signal lists */ sig_list[1-active_list].count=0; sig_list[1-active_list].overflow=0; active_list=1-active_list; /* Set up */ event_t e = event_t::signal_event(0); e.arguments.reset(new wcstring_list_t(1)); //one element lst = &sig_list[1-active_list]; 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(event_copy(&e, 1)); } else { event_fire_internal(&e); } } e.arguments.reset(NULL); } }
/** 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 (events.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<events.size(); i++) { event_t *criterion = events.at(i); /* 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; 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++) { 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; /* Fire event */ wcstring buffer = criterion->function_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(); }
/** 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(); }
/** 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(event_t *e) { return std::find(killme.begin(), killme.end(), e) != killme.end(); }
/** 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) { size_t i, j; event_list_t fire; /* First we free all events that have been removed */ event_free_kills(); if (events.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 (i=0; i<events.size(); i++) { event_t *criterion = events.at(i); /* 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; /* Iterate over our list of matching events */ for (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; /* Fire event */ wcstring buffer = criterion->function_name; if (event->arguments.get()) { for (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 */ event_free_kills(); }