Skip to content

Commit 7768ecb

Browse files
mp.input: remove race conditions when calling input.terminate()
This commit ensures that `input.terminate()` can only close input requests made by the same script, and prevents any in-transit events for old input requests from being processed. Previously, there was no way to guarantee that the input request being terminated was the one intended; the asynchronous nature of the API meant that it was always possible (though unlikely) that another client may have activated its own input request while the termination request was in transit. This commit removes the race condition between different scripts calling `input.terminate()` by sending the script name alongside the termination message. In addition, when a script overwrites one of its own input requests, there may be incoming events still in transit. Some of these events may have a decent chance of calling `input.terminate()` if they are processed (e.g., `submit`). This commit avoids this issue by only processing `closed` requests once a new `input.get()` request is made.
1 parent 103df92 commit 7768ecb

4 files changed

Lines changed: 28 additions & 10 deletions

File tree

DOCS/man/lua.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,8 @@ REPL.
976976
script name with ``prompt`` appended.
977977

978978
``input.terminate()``
979-
Close the console.
979+
Closes any currently active input request. This will not close
980+
requests made by other scripts.
980981

981982
``input.log(message, style, terminal_style)``
982983
Add a line to the log buffer. ``style`` can contain additional ASS tags to

player/javascript/defaults.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -654,12 +654,16 @@ mp.options = { read_options: read_options };
654654
* input
655655
*********************************************************************/
656656
var input_handle_counter = 1;
657+
var latest_handler_id
657658

658659
function register_event_handler(t) {
659660
var handler_id = "input-event/" + input_handle_counter;
660661
input_handle_counter += 1;
662+
latest_handler_id = handler_id
661663

662664
mp.register_script_message(handler_id, function (type, args) {
665+
if (latest_handler_id !== handler_id && type !== 'closed') return;
666+
663667
if (t[type]) {
664668
args = args ? JSON.parse(args) : [];
665669
var result = t[type].apply(null, args);
@@ -680,13 +684,14 @@ function register_event_handler(t) {
680684
mp.input = {
681685
get: function(t) {
682686
t.has_completions = t.complete !== undefined;
683-
var handler_id = register_event_handler(t);
684687

685688
mp.commandv("script-message-to", "console", "get-input",
686-
mp.script_name, handler_id, JSON.stringify(t));
689+
mp.script_name, register_event_handler(t), JSON.stringify(t));
687690
},
688691
terminate: function () {
689-
mp.commandv("script-message-to", "console", "disable");
692+
mp.commandv("script-message-to", "console", "disable", JSON.stringify({
693+
script_name: mp.script_name
694+
}));
690695
},
691696
log: function (message, style, terminal_style) {
692697
mp.commandv("script-message-to", "console", "log", JSON.stringify({

player/lua/console.lua

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,8 +1659,12 @@ set_active = function (active)
16591659
render()
16601660
end
16611661

1662-
mp.register_script_message("disable", function()
1663-
set_active(false)
1662+
mp.register_script_message("disable", function(message)
1663+
message = utils.parse_json(message)
1664+
1665+
if not message or message.script_name == input_caller then
1666+
set_active(false)
1667+
end
16641668
end)
16651669

16661670
mp.register_script_message("get-input", function (script_name, handler_id, args)

player/lua/input.lua

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ License along with mpv. If not, see <http://www.gnu.org/licenses/>.
1818
local utils = require "mp.utils"
1919
local input = {}
2020
local handle_counter = 1
21+
local latest_handler_id
2122

2223
local function get_non_callbacks(t)
2324
local non_callbacks = {}
@@ -34,8 +35,14 @@ end
3435
local function register_event_handler(t)
3536
local handler_id = "input-event/"..handle_counter
3637
handle_counter = handle_counter + 1
38+
latest_handler_id = handler_id
3739

3840
mp.register_script_message(handler_id, function (type, args)
41+
-- do not process events (other than closed) for an input that has been overwritten
42+
if latest_handler_id ~= handler_id and type ~= 'closed' then
43+
return
44+
end
45+
3946
if t[type] then
4047
local completions, completion_pos, completion_append =
4148
t[type](unpack(utils.parse_json(args or "") or {}))
@@ -58,15 +65,16 @@ end
5865
function input.get(t)
5966
t.has_completions = t.complete ~= nil
6067

61-
local handler_id = register_event_handler(t)
62-
mp.commandv("script-message-to", "console", "get-input",
63-
mp.get_script_name(), handler_id, utils.format_json(get_non_callbacks(t)))
68+
mp.commandv("script-message-to", "console", "get-input", mp.get_script_name(),
69+
register_event_handler(t), utils.format_json(get_non_callbacks(t)))
6470
end
6571

6672
input.select = input.get
6773

6874
function input.terminate()
69-
mp.commandv("script-message-to", "console", "disable")
75+
mp.commandv("script-message-to", "console", "disable", utils.format_json({
76+
script_name = mp.get_script_name()
77+
}))
7078
end
7179

7280
function input.log(message, style, terminal_style)

0 commit comments

Comments
 (0)