From 43a640c20adaaae7942d44c91cf3df3e5b4495ef Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 7 May 2022 03:20:35 +0000 Subject: [PATCH] Fix race conditions in the Haiku file dialog * src/haiku_support.cc (current_file_panel_port): Delete variable. (class EmacsWindow, MessageReceived): Stop handling file panel events here. (class EmacsFilePanelCallbackLooper): New class. (be_popup_file_dialog): Use a separate looper to handle file panel events. --- src/haiku_support.cc | 228 +++++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 95 deletions(-) diff --git a/src/haiku_support.cc b/src/haiku_support.cc index b8fa963c627..300a78cd73b 100644 --- a/src/haiku_support.cc +++ b/src/haiku_support.cc @@ -195,10 +195,6 @@ static void *grab_view = NULL; static BLocker grab_view_locker; static bool drag_and_drop_in_progress; -/* Port used to send data to the main thread while a file panel is - active. */ -static port_id volatile current_file_panel_port; - /* Many places require us to lock the child frame data, and then lock the locker of some random window. Unfortunately, locking such a window might be delayed due to an arriving message, which then @@ -853,8 +849,6 @@ public: void MessageReceived (BMessage *msg) { - int32 old_what = 0; - if (msg->WasDropped ()) { BPoint whereto; @@ -886,49 +880,6 @@ public: haiku_write (MENU_BAR_SELECT_EVENT, &rq); } - else if (msg->what == FILE_PANEL_SELECTION - || ((msg->FindInt32 ("old_what", &old_what) == B_OK - && old_what == FILE_PANEL_SELECTION))) - { - const char *str_path, *name; - char *file_name, *str_buf; - BEntry entry; - BPath path; - entry_ref ref; - - file_name = NULL; - - if (msg->FindRef ("refs", &ref) == B_OK && - entry.SetTo (&ref, 0) == B_OK && - entry.GetPath (&path) == B_OK) - { - str_path = path.Path (); - - if (str_path) - file_name = strdup (str_path); - } - - if (msg->FindRef ("directory", &ref), - entry.SetTo (&ref, 0) == B_OK && - entry.GetPath (&path) == B_OK) - { - name = msg->GetString ("name"); - str_path = path.Path (); - - if (name) - { - str_buf = (char *) alloca (std::strlen (str_path) - + std::strlen (name) + 2); - snprintf (str_buf, std::strlen (str_path) - + std::strlen (name) + 2, "%s/%s", - str_path, name); - file_name = strdup (str_buf); - } - } - - write_port (current_file_panel_port, 0, - &file_name, sizeof file_name); - } else BWindow::MessageReceived (msg); } @@ -3026,6 +2977,125 @@ public: } }; +class EmacsFilePanelCallbackLooper : public BLooper +{ + port_id comm_port; + + void + MessageReceived (BMessage *msg) + { + const char *str_path, *name; + char *file_name, *str_buf; + BEntry entry; + BPath path; + entry_ref ref; + int old_what; + + if (msg->what == FILE_PANEL_SELECTION + || ((msg->FindInt32 ("old_what", &old_what) == B_OK + && old_what == FILE_PANEL_SELECTION))) + { + file_name = NULL; + + if (msg->FindRef ("refs", &ref) == B_OK + && entry.SetTo (&ref, 0) == B_OK + && entry.GetPath (&path) == B_OK) + { + str_path = path.Path (); + + if (str_path) + file_name = strdup (str_path); + } + else if (msg->FindRef ("directory", &ref) == B_OK + && entry.SetTo (&ref, 0) == B_OK + && entry.GetPath (&path) == B_OK) + { + name = msg->GetString ("name"); + str_path = path.Path (); + + if (name) + { + str_buf = (char *) alloca (std::strlen (str_path) + + std::strlen (name) + 2); + snprintf (str_buf, std::strlen (str_path) + + std::strlen (name) + 2, "%s/%s", + str_path, name); + file_name = strdup (str_buf); + } + } + + write_port (comm_port, 0, &file_name, sizeof file_name); + } + + BLooper::MessageReceived (msg); + } + +public: + EmacsFilePanelCallbackLooper (void) : BLooper () + { + comm_port = create_port (1, "file panel port"); + } + + ~EmacsFilePanelCallbackLooper (void) + { + delete_port (comm_port); + } + + char * + ReadFileName (void (*process_pending_signals_function) (void)) + { + object_wait_info infos[2]; + ssize_t status; + int32 reply_type; + char *file_name; + + file_name = NULL; + + infos[0].object = port_application_to_emacs; + infos[0].type = B_OBJECT_TYPE_PORT; + infos[0].events = B_EVENT_READ; + + infos[1].object = comm_port; + infos[1].type = B_OBJECT_TYPE_PORT; + infos[1].events = B_EVENT_READ; + + while (true) + { + status = wait_for_objects (infos, 2); + + if (status == B_INTERRUPTED || status == B_WOULD_BLOCK) + continue; + + if (infos[0].events & B_EVENT_READ) + process_pending_signals_function (); + + if (infos[1].events & B_EVENT_READ) + { + status = read_port (comm_port, + &reply_type, &file_name, + sizeof file_name); + + if (status < B_OK) + file_name = NULL; + + goto out; + } + + infos[0].events = B_EVENT_READ; + infos[1].events = B_EVENT_READ; + } + + out: + return file_name; + } + + status_t + InitCheck (void) + { + return comm_port >= B_OK ? B_OK : comm_port; + } +}; + static int32 start_running_application (void *data) { @@ -4403,23 +4473,23 @@ be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, const char *prompt, void (*process_pending_signals_function) (void)) { - BWindow *w, *panel_window; + BWindow *panel_window; BEntry path; BMessage msg (FILE_PANEL_SELECTION); BFilePanel panel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL, NULL, NULL, (dir_only_p ? B_DIRECTORY_NODE : B_FILE_NODE | B_DIRECTORY_NODE)); - object_wait_info infos[2]; - ssize_t status; - int32 reply_type; char *file_name; + EmacsFilePanelCallbackLooper *looper; - current_file_panel_port = create_port (1, "file panel port"); - file_name = NULL; + looper = new EmacsFilePanelCallbackLooper; - if (current_file_panel_port < B_OK) - return NULL; + if (looper->InitCheck () < B_OK) + { + delete looper; + return NULL; + } if (default_dir) { @@ -4427,11 +4497,8 @@ be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, default_dir = NULL; } - w = (BWindow *) window; panel_window = panel.Window (); - panel.SetMessage (&msg); - if (default_dir) panel.SetPanelDirectory (&path); @@ -4442,45 +4509,16 @@ be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, panel_window->SetFeel (B_MODAL_APP_WINDOW_FEEL); panel.SetHideWhenDone (false); - panel.SetTarget (BMessenger (w)); + panel.SetTarget (BMessenger (looper)); + panel.SetMessage (&msg); panel.Show (); - infos[0].object = port_application_to_emacs; - infos[0].type = B_OBJECT_TYPE_PORT; - infos[0].events = B_EVENT_READ; - - infos[1].object = current_file_panel_port; - infos[1].type = B_OBJECT_TYPE_PORT; - infos[1].events = B_EVENT_READ; - - while (true) - { - status = wait_for_objects (infos, 2); + looper->Run (); + file_name = looper->ReadFileName (process_pending_signals_function); - if (status == B_INTERRUPTED || status == B_WOULD_BLOCK) - continue; - - if (infos[0].events & B_EVENT_READ) - process_pending_signals_function (); - - if (infos[1].events & B_EVENT_READ) - { - status = read_port (current_file_panel_port, - &reply_type, &file_name, - sizeof file_name); - - if (status < B_OK) - file_name = NULL; - - goto out; - } - - infos[0].events = B_EVENT_READ; - infos[1].events = B_EVENT_READ; - } + if (looper->Lock ()) + looper->Quit (); - out: - delete_port (current_file_panel_port); return file_name; } -- 2.39.2