mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Improve thread safety in input.cpp
This commit is contained in:
parent
36998eee55
commit
56fd6f696b
3 changed files with 35 additions and 37 deletions
|
@ -133,7 +133,7 @@ void env_dispatch_init(const environment_t &vars) {
|
||||||
run_inits(vars);
|
run_inits(vars);
|
||||||
// Note this deliberately leaks; the dispatch table is immortal.
|
// Note this deliberately leaks; the dispatch table is immortal.
|
||||||
// Via this construct we can avoid invoking destructors at shutdown.
|
// Via this construct we can avoid invoking destructors at shutdown.
|
||||||
s_var_dispatch_table = create_dispatch_table().release();
|
s_var_dispatch_table = create_dispatch_table();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Properly sets all timezone information.
|
/// Properly sets all timezone information.
|
||||||
|
|
|
@ -63,17 +63,19 @@ class latch_t : detail::fixed_t {
|
||||||
T *operator->() { return value_; }
|
T *operator->() { return value_; }
|
||||||
const T *operator->() const { return value_; }
|
const T *operator->() const { return value_; }
|
||||||
|
|
||||||
void operator=(T *value) {
|
void operator=(std::unique_ptr<T> value) {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
assert(value_ == nullptr && "Latch variable initialized multiple times");
|
assert(value_ == nullptr && "Latch variable initialized multiple times");
|
||||||
assert(value != nullptr && "Latch variable initialized with null");
|
assert(value != nullptr && "Latch variable initialized with null");
|
||||||
value_ = value;
|
// Note: deliberate leak.
|
||||||
|
value_ = value.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void operator=(T &&value) { *this = make_unique<T>(std::move(value)); }
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void emplace(Args &&... args) {
|
void emplace(Args &&... args) {
|
||||||
// Note: deliberate leak.
|
*this = make_unique<T>(std::forward<Args>(args)...);
|
||||||
*this = new T(std::forward<Args>(args)...);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "fallback.h" // IWYU pragma: keep
|
#include "fallback.h" // IWYU pragma: keep
|
||||||
|
#include "global_safety.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "input_common.h"
|
#include "input_common.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
@ -147,20 +148,18 @@ wcstring describe_char(wint_t c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mappings for the current input mode.
|
/// Mappings for the current input mode.
|
||||||
static std::vector<input_mapping_t> mapping_list;
|
using mapping_list_t = std::vector<input_mapping_t>;
|
||||||
static std::vector<input_mapping_t> preset_mapping_list;
|
static mainthread_t<mapping_list_t> s_mapping_list;
|
||||||
|
static mainthread_t<mapping_list_t> s_preset_mapping_list;
|
||||||
|
|
||||||
/// Terminfo map list.
|
/// Terminfo map list.
|
||||||
static std::vector<terminfo_mapping_t> terminfo_mappings;
|
static latch_t<std::vector<terminfo_mapping_t>> s_terminfo_mappings;
|
||||||
|
|
||||||
#define TERMINFO_ADD(key) \
|
#define TERMINFO_ADD(key) \
|
||||||
{ (L## #key) + 4, key }
|
{ (L## #key) + 4, key }
|
||||||
|
|
||||||
/// List of all terminfo mappings.
|
/// \return the input terminfo.
|
||||||
static std::vector<terminfo_mapping_t> mappings;
|
static std::vector<terminfo_mapping_t> create_input_terminfo();
|
||||||
|
|
||||||
/// Initialize terminfo.
|
|
||||||
static void init_input_terminfo();
|
|
||||||
|
|
||||||
static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS];
|
static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS];
|
||||||
static bool input_function_status;
|
static bool input_function_status;
|
||||||
|
@ -212,10 +211,8 @@ static bool specification_order_is_less_than(const input_mapping_t &m1, const in
|
||||||
/// Inserts an input mapping at the correct position. We sort them in descending order by length, so
|
/// Inserts an input mapping at the correct position. We sort them in descending order by length, so
|
||||||
/// that we test longer sequences first.
|
/// that we test longer sequences first.
|
||||||
static void input_mapping_insert_sorted(const input_mapping_t &new_mapping, bool user = true) {
|
static void input_mapping_insert_sorted(const input_mapping_t &new_mapping, bool user = true) {
|
||||||
auto& ml = user ? mapping_list : preset_mapping_list;
|
mapping_list_t &ml = user ? s_mapping_list : s_preset_mapping_list;
|
||||||
|
auto loc = std::lower_bound(ml.begin(), ml.end(), new_mapping, length_is_greater_than);
|
||||||
std::vector<input_mapping_t>::iterator loc = std::lower_bound(
|
|
||||||
ml.begin(), ml.end(), new_mapping, length_is_greater_than);
|
|
||||||
ml.insert(loc, new_mapping);
|
ml.insert(loc, new_mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +227,7 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands,
|
||||||
// Remove existing mappings with this sequence.
|
// Remove existing mappings with this sequence.
|
||||||
const wcstring_list_t commands_vector(commands, commands + commands_len);
|
const wcstring_list_t commands_vector(commands, commands + commands_len);
|
||||||
|
|
||||||
auto& ml = user ? mapping_list : preset_mapping_list;
|
mapping_list_t &ml = user ? s_mapping_list : s_preset_mapping_list;
|
||||||
|
|
||||||
for (input_mapping_t& m : ml) {
|
for (input_mapping_t& m : ml) {
|
||||||
if (m.seq == sequence && m.mode == mode) {
|
if (m.seq == sequence && m.mode == mode) {
|
||||||
|
@ -279,9 +276,10 @@ void init_input() {
|
||||||
input_initialized.store(true, std::memory_order_relaxed);
|
input_initialized.store(true, std::memory_order_relaxed);
|
||||||
|
|
||||||
input_common_init(&interrupt_handler);
|
input_common_init(&interrupt_handler);
|
||||||
init_input_terminfo();
|
s_terminfo_mappings = create_input_terminfo();
|
||||||
|
|
||||||
// If we have no keybindings, add a few simple defaults.
|
// If we have no keybindings, add a few simple defaults.
|
||||||
|
mapping_list_t &preset_mapping_list = s_preset_mapping_list;
|
||||||
if (preset_mapping_list.empty()) {
|
if (preset_mapping_list.empty()) {
|
||||||
input_mapping_add(L"", L"self-insert", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
|
input_mapping_add(L"", L"self-insert", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
|
||||||
input_mapping_add(L"\n", L"execute", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
|
input_mapping_add(L"\n", L"execute", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false);
|
||||||
|
@ -423,9 +421,10 @@ static const input_mapping_t *find_mapping() {
|
||||||
const auto &vars = parser_t::principal_parser().vars();
|
const auto &vars = parser_t::principal_parser().vars();
|
||||||
const wcstring bind_mode = input_get_bind_mode(vars);
|
const wcstring bind_mode = input_get_bind_mode(vars);
|
||||||
|
|
||||||
const auto lists = {&mapping_list, &preset_mapping_list};
|
const auto lists = {&s_mapping_list, &s_preset_mapping_list};
|
||||||
for (const auto *listp : lists) {
|
for (const auto *listp : lists) {
|
||||||
for (const auto &m : *listp) {
|
const mapping_list_t &ml = *listp;
|
||||||
|
for (const auto &m : ml) {
|
||||||
if (m.mode != bind_mode) {
|
if (m.mode != bind_mode) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -517,7 +516,7 @@ char_event_t input_readch(bool allow_commands) {
|
||||||
std::vector<input_mapping_name_t> input_mapping_get_names(bool user) {
|
std::vector<input_mapping_name_t> input_mapping_get_names(bool user) {
|
||||||
// Sort the mappings by the user specification order, so we can return them in the same order
|
// Sort the mappings by the user specification order, so we can return them in the same order
|
||||||
// that the user specified them in.
|
// that the user specified them in.
|
||||||
std::vector<input_mapping_t> local_list = user ? mapping_list : preset_mapping_list;
|
std::vector<input_mapping_t> local_list = user ? s_mapping_list : s_preset_mapping_list;
|
||||||
std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than);
|
std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than);
|
||||||
std::vector<input_mapping_name_t> result;
|
std::vector<input_mapping_name_t> result;
|
||||||
result.reserve(local_list.size());
|
result.reserve(local_list.size());
|
||||||
|
@ -531,7 +530,7 @@ std::vector<input_mapping_name_t> input_mapping_get_names(bool user) {
|
||||||
|
|
||||||
void input_mapping_clear(const wchar_t *mode, bool user) {
|
void input_mapping_clear(const wchar_t *mode, bool user) {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
auto &ml = user ? mapping_list : preset_mapping_list;
|
mapping_list_t &ml = user ? s_mapping_list : s_preset_mapping_list;
|
||||||
auto should_erase = [=](const input_mapping_t &m) { return mode == NULL || mode == m.mode; };
|
auto should_erase = [=](const input_mapping_t &m) { return mode == NULL || mode == m.mode; };
|
||||||
ml.erase(std::remove_if(ml.begin(), ml.end(), should_erase), ml.end());
|
ml.erase(std::remove_if(ml.begin(), ml.end(), should_erase), ml.end());
|
||||||
}
|
}
|
||||||
|
@ -539,7 +538,7 @@ void input_mapping_clear(const wchar_t *mode, bool user) {
|
||||||
bool input_mapping_erase(const wcstring &sequence, const wcstring &mode, bool user) {
|
bool input_mapping_erase(const wcstring &sequence, const wcstring &mode, bool user) {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
bool result = false;
|
bool result = false;
|
||||||
auto& ml = user ? mapping_list : preset_mapping_list;
|
mapping_list_t &ml = user ? s_mapping_list : s_preset_mapping_list;
|
||||||
for (std::vector<input_mapping_t>::iterator it = ml.begin(), end = ml.end();
|
for (std::vector<input_mapping_t>::iterator it = ml.begin(), end = ml.end();
|
||||||
it != end; ++it) {
|
it != end; ++it) {
|
||||||
if (sequence == it->seq && mode == it->mode) {
|
if (sequence == it->seq && mode == it->mode) {
|
||||||
|
@ -554,7 +553,7 @@ bool input_mapping_erase(const wcstring &sequence, const wcstring &mode, bool us
|
||||||
bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, bool user,
|
bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, bool user,
|
||||||
wcstring *out_sets_mode) {
|
wcstring *out_sets_mode) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
auto& ml = user ? mapping_list : preset_mapping_list;
|
mapping_list_t &ml = user ? s_mapping_list : s_preset_mapping_list;
|
||||||
for (const input_mapping_t &m : ml) {
|
for (const input_mapping_t &m : ml) {
|
||||||
if (sequence == m.seq && mode == m.mode) {
|
if (sequence == m.seq && mode == m.mode) {
|
||||||
*out_cmds = m.commands;
|
*out_cmds = m.commands;
|
||||||
|
@ -566,11 +565,11 @@ bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add all terminfo mappings and cache other terminfo facts we care about.
|
/// Create a list of terminfo mappings.
|
||||||
static void init_input_terminfo() {
|
static std::vector<terminfo_mapping_t> create_input_terminfo() {
|
||||||
assert(curses_initialized);
|
assert(curses_initialized);
|
||||||
if (!cur_term) return; // setupterm() failed so we can't referency any key definitions
|
if (!cur_term) return {}; // setupterm() failed so we can't referency any key definitions
|
||||||
const terminfo_mapping_t tinfos[] = {
|
return {
|
||||||
TERMINFO_ADD(key_a1),
|
TERMINFO_ADD(key_a1),
|
||||||
TERMINFO_ADD(key_a3),
|
TERMINFO_ADD(key_a3),
|
||||||
TERMINFO_ADD(key_b2),
|
TERMINFO_ADD(key_b2),
|
||||||
|
@ -726,9 +725,6 @@ static void init_input_terminfo() {
|
||||||
TERMINFO_ADD(key_undo),
|
TERMINFO_ADD(key_undo),
|
||||||
TERMINFO_ADD(key_up)
|
TERMINFO_ADD(key_up)
|
||||||
};
|
};
|
||||||
const size_t count = sizeof tinfos / sizeof *tinfos;
|
|
||||||
terminfo_mappings.reserve(terminfo_mappings.size() + count);
|
|
||||||
terminfo_mappings.insert(terminfo_mappings.end(), tinfos, tinfos + count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
||||||
|
@ -739,7 +735,7 @@ bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
||||||
const char *res = 0;
|
const char *res = 0;
|
||||||
int err = ENOENT;
|
int err = ENOENT;
|
||||||
|
|
||||||
for (const terminfo_mapping_t &m : terminfo_mappings) {
|
for (const terminfo_mapping_t &m : *s_terminfo_mappings) {
|
||||||
if (!std::wcscmp(name, m.name)) {
|
if (!std::wcscmp(name, m.name)) {
|
||||||
res = m.seq;
|
res = m.seq;
|
||||||
err = EILSEQ;
|
err = EILSEQ;
|
||||||
|
@ -759,7 +755,7 @@ bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
||||||
bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
||||||
assert(input_initialized);
|
assert(input_initialized);
|
||||||
|
|
||||||
for (const terminfo_mapping_t &m : terminfo_mappings) {
|
for (const terminfo_mapping_t &m : *s_terminfo_mappings) {
|
||||||
if (!m.seq) {
|
if (!m.seq) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -777,9 +773,9 @@ bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
||||||
wcstring_list_t input_terminfo_get_names(bool skip_null) {
|
wcstring_list_t input_terminfo_get_names(bool skip_null) {
|
||||||
assert(input_initialized);
|
assert(input_initialized);
|
||||||
wcstring_list_t result;
|
wcstring_list_t result;
|
||||||
result.reserve(terminfo_mappings.size());
|
const auto &mappings = *s_terminfo_mappings;
|
||||||
|
result.reserve(mappings.size());
|
||||||
for (const terminfo_mapping_t &m : terminfo_mappings) {
|
for (const terminfo_mapping_t &m : mappings) {
|
||||||
if (skip_null && !m.seq) {
|
if (skip_null && !m.seq) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue