Skip to content

Commit ae0243f

Browse files
mp.input: send buffer ids with logs to avoid race conditions
This commit modifies the log methods in mp.input to always send the id of the latest `input.get()` request with log entries, and allows manually specifying an id to use instead. Previously, the log methods applied to whichever input request happened to be open when the log message was received. Even when scripts used these methods correctly, there was the risk of sending a log to the wrong log buffer if the active input request changed while the log message was in transit; a race condition. Now an id for the log buffer is always sent, either the id of the latest `input.get()` request (preventing data races between scripts), or a manually specified id passed as an extra parameter to the log methods (preventing data races between multiple requests in the same script). A side effect of this change is that logs can now be sent to specific log buffers at any time, regardless of the currently active input request (or lack thereof). I have not explicitly documented this as I don't know if it's something we want to encourage.
1 parent 82ec383 commit ae0243f

File tree

5 files changed

+80
-36
lines changed

5 files changed

+80
-36
lines changed

DOCS/man/javascript.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ string/boolean/number)
202202

203203
``mp.input.terminate([id])``
204204

205-
``mp.input.log(message, style)``
205+
``mp.input.log(message, style, terminal_style [, id])``
206206

207-
``mp.input.log_error(message)``
207+
``mp.input.log_error(message [, id])``
208208

209-
``mp.input.set_log(log)``
209+
``mp.input.set_log(log [, id])``
210210

211211
``exit()`` (global)
212212

DOCS/man/lua.rst

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,21 +1012,32 @@ REPL.
10121012
end,
10131013
})
10141014

1015-
``input.log(message, style, terminal_style)``
1015+
``input.log(message, style, terminal_style [, id])``
10161016
Add a line to the log buffer. ``style`` can contain additional ASS tags to
10171017
apply to ``message``, and ``terminal_style`` can contain escape sequences
10181018
that are used when the console is displayed in the terminal.
10191019

1020-
``input.log_error(message)``
1020+
``id`` is a string identifying the specific log buffer to write to, and
1021+
defaults to the ``id`` of the latest ``input.get()`` request.
1022+
This can be used to avoid race conditions for scripts that send multiple
1023+
input requests with different log buffers.
1024+
1025+
``input.log_error(message [, id])``
10211026
Helper to add a line to the log buffer with the same color as the one used
10221027
for commands that error. Useful when the user submits invalid input.
1028+
1029+
``id`` is a string identifying the specific log buffer to write to, and
1030+
defaults to the ``id`` of the latest ``input.get()`` request.
10231031

1024-
``input.set_log(log)``
1025-
Replace the entire log buffer.
1032+
``input.set_log(log [, id])``
1033+
Replaces the entire log buffer with a new list of log entries.
10261034

10271035
``log`` is a table of strings, or tables with ``text``, ``style`` and
10281036
``terminal_style`` keys.
10291037

1038+
``id`` is a string identifying the specific log buffer to overwrite, and
1039+
defaults to the ``id`` of the latest ``input.get()`` request.
1040+
10301041
Example:
10311042

10321043
::

player/javascript/defaults.js

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

658659
function register_event_handler(t) {
659660
var handler_id = "input-event/" + input_handle_counter;
@@ -682,41 +683,51 @@ function register_event_handler(t) {
682683
return handler_id;
683684
}
684685

685-
mp.input = {
686-
get: function(t) {
687-
t.has_completions = t.complete !== undefined;
688-
var handler_id = register_event_handler(t);
686+
function input_request(t) {
687+
t.has_completions = t.complete !== undefined;
689688

690-
mp.commandv("script-message-to", "console", "get-input",
691-
mp.script_name, handler_id, JSON.stringify(t));
689+
var handler_id = register_event_handler(t);
690+
mp.commandv("script-message-to", "console", "get-input",
691+
mp.script_name, handler_id, JSON.stringify(t));
692692

693-
return {
694-
handler_id: handler_id
695-
}
693+
return {
694+
handler_id: handler_id
695+
}
696+
}
697+
698+
mp.input = {
699+
get: function(t) {
700+
latest_id = t.id || mp.script_name + (t.prompt || "");
701+
return input_request(t);
696702
},
697703
terminate: function (t) {
698704
mp.commandv("script-message-to", "console", "disable", JSON.stringify({
699705
script_name: mp.script_name,
700706
handler_id: t ? t.handler_id : undefined,
701707
}));
702708
},
703-
log: function (message, style, terminal_style) {
709+
log: function (message, style, terminal_style, log_id) {
704710
mp.commandv("script-message-to", "console", "log", JSON.stringify({
711+
log_id: log_id || latest_id,
705712
text: message,
706713
style: style,
707714
terminal_style: terminal_style,
708715
}));
709716
},
710-
log_error: function (message) {
711-
mp.commandv("script-message-to", "console", "log",
712-
JSON.stringify({ text: message, error: true }));
717+
log_error: function (message, log_id) {
718+
mp.commandv("script-message-to", "console", "log", JSON.stringify({
719+
log_id: log_id || latest_id,
720+
text: message,
721+
error: true
722+
}));
713723
},
714-
set_log: function (log) {
724+
set_log: function (log, log_id) {
725+
if (!(log_id || latest_id)) return;
715726
mp.commandv("script-message-to", "console", "set-log",
716-
JSON.stringify(log));
727+
JSON.stringify(log), log_id || latest_id);
717728
}
718729
}
719-
mp.input.select = mp.input.get
730+
mp.input.select = input_request
720731

721732
/**********************************************************************
722733
* various

player/lua/console.lua

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,8 +1743,14 @@ end)
17431743

17441744
-- Add a line to the log buffer
17451745
mp.register_script_message("log", function (message)
1746-
local log_buffer = log_buffers[id]
17471746
message = utils.parse_json(message)
1747+
if not message.log_id then return end
1748+
1749+
if not log_buffers[message.log_id] then
1750+
log_buffers[message.log_id] = {}
1751+
end
1752+
1753+
local log_buffer = log_buffers[message.log_id]
17481754

17491755
log_buffer[#log_buffer + 1] = {
17501756
text = message.text,
@@ -1757,7 +1763,7 @@ mp.register_script_message("log", function (message)
17571763
table.remove(log_buffer, 1)
17581764
end
17591765

1760-
if not open then
1766+
if not open or message.log_id ~= id then
17611767
return
17621768
end
17631769

@@ -1773,9 +1779,10 @@ mp.register_script_message("log", function (message)
17731779
end
17741780
end)
17751781

1776-
mp.register_script_message("set-log", function (log)
1782+
mp.register_script_message("set-log", function (log, log_id)
1783+
if not log_id then return end
17771784
log = utils.parse_json(log)
1778-
log_buffers[id] = {}
1785+
log_buffers[log_id] = {}
17791786

17801787
for i = 1, #log do
17811788
if type(log[i]) == "table" then
@@ -1792,7 +1799,9 @@ mp.register_script_message("set-log", function (log)
17921799
end
17931800
end
17941801

1795-
render()
1802+
if log_id == id then
1803+
render()
1804+
end
17961805
end)
17971806

17981807
mp.register_script_message("complete", function (message)

player/lua/input.lua

Lines changed: 21 additions & 8 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_id
2122

2223
local function get_non_callbacks(t)
2324
local non_callbacks = {}
@@ -59,7 +60,7 @@ local function register_event_handler(t)
5960
return handler_id
6061
end
6162

62-
function input.get(t)
63+
local function input_request(t)
6364
t.has_completions = t.complete ~= nil
6465

6566
local handler_id = register_event_handler(t)
@@ -71,7 +72,13 @@ function input.get(t)
7172
}
7273
end
7374

74-
input.select = input.get
75+
function input.get(t)
76+
-- input.select does not support log buffers, so cannot override the latest id.
77+
latest_id = t.id or mp.get_script_name()..(t.prompt or "")
78+
return input_request(t)
79+
end
80+
81+
input.select = input_request
7582

7683
function input.terminate(t)
7784
mp.commandv("script-message-to", "console", "disable", utils.format_json({
@@ -80,21 +87,27 @@ function input.terminate(t)
8087
}))
8188
end
8289

83-
function input.log(message, style, terminal_style)
90+
function input.log(message, style, terminal_style, log_id)
8491
mp.commandv("script-message-to", "console", "log", utils.format_json({
92+
log_id = log_id or latest_id,
8593
text = message,
8694
style = style,
8795
terminal_style = terminal_style,
8896
}))
8997
end
9098

91-
function input.log_error(message)
92-
mp.commandv("script-message-to", "console", "log",
93-
utils.format_json({ text = message, error = true }))
99+
function input.log_error(message, log_id)
100+
mp.commandv("script-message-to", "console", "log", utils.format_json({
101+
log_id = log_id or latest_id,
102+
text = message,
103+
error = true
104+
}))
94105
end
95106

96-
function input.set_log(log)
97-
mp.commandv("script-message-to", "console", "set-log", utils.format_json(log))
107+
function input.set_log(log, log_id)
108+
if not log_id or latest_id then return end
109+
mp.commandv("script-message-to", "console", "set-log",
110+
utils.format_json(log), log_id or latest_id)
98111
end
99112

100113
return input

0 commit comments

Comments
 (0)