From fb767602c08159daa2190129622ebf185606fd35 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 17 Jan 2014 16:57:29 +0100 Subject: wx: Delay memory cleanup until safe Previously we could do a cleanup while we where recursed down and thus delete the objects we where invoking. --- lib/wx/c_src/wxe_impl.cpp | 59 +++++++++++++++++++++++++++++++----------- lib/wx/c_src/wxe_impl.h | 3 +++ lib/wx/test/wx_basic_SUITE.erl | 16 +----------- lib/wx/test/wx_event_SUITE.erl | 51 ++++++++++++++++++------------------ 4 files changed, 73 insertions(+), 56 deletions(-) (limited to 'lib') diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp index 5205e4a2c1..5c10881eda 100644 --- a/lib/wx/c_src/wxe_impl.cpp +++ b/lib/wx/c_src/wxe_impl.cpp @@ -123,6 +123,8 @@ bool WxeApp::OnInit() wxe_batch = new wxList; wxe_batch_cb_saved = new wxList; cb_buff = NULL; + recurse_level = 0; + delayed_cleanup = new wxList; wxe_ps_init2(); // wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED); // Hmm printpreview doesn't work in 2.9 with this @@ -186,23 +188,40 @@ void handle_event_callback(ErlDrvPort port, ErlDrvTermData process) { WxeApp * app = (WxeApp *) wxTheApp; ErlDrvMonitor monitor; + // Is thread safe if pdl have been incremented driver_monitor_process(port, process, &monitor); // Should we be able to handle commands when recursing? probably erl_drv_mutex_lock(wxe_batch_locker_m); //fprintf(stderr, "\r\nCB EV Start %lu \r\n", process);fflush(stderr); + app->recurse_level++; app->dispatch_cb(wxe_batch, wxe_batch_cb_saved, process); + app->recurse_level--; //fprintf(stderr, "CB EV done %lu \r\n", process);fflush(stderr); wxe_batch_caller = 0; erl_drv_mutex_unlock(wxe_batch_locker_m); driver_demonitor_process(port, &monitor); } -void WxeApp::dispatch_cmds() { +void WxeApp::dispatch_cmds() +{ erl_drv_mutex_lock(wxe_batch_locker_m); + recurse_level++; int level = dispatch(wxe_batch_cb_saved, 0, WXE_STORED); dispatch(wxe_batch, level, WXE_NORMAL); + recurse_level--; wxe_batch_caller = 0; erl_drv_mutex_unlock(wxe_batch_locker_m); + // Cleanup old memenv's + if(recurse_level == 0 && delayed_cleanup->size() > 0) { + for( wxList::compatibility_iterator node = delayed_cleanup->GetFirst(); + node; + node = delayed_cleanup->GetFirst()) { + wxeMetaCommand *event = (wxeMetaCommand *)node->GetData(); + delayed_cleanup->Erase(node); + destroyMemEnv(*event); + delete event; + } + } } // Should have erl_drv_mutex_lock(wxe_batch_locker_m); @@ -370,9 +389,11 @@ void WxeApp::newMemEnv(wxeMetaCommand& Ecmd) { erl_drv_send_term(WXE_DRV_PORT,Ecmd.caller,rt,2); } -void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd) { +void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd) +{ // Clear incoming cmd queue first // dispatch_cmds(); + int delay = false; wxWindow *parent = NULL; wxeMemEnv * memenv = refmap[Ecmd.port]; @@ -389,26 +410,34 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd) { ptrMap::iterator it = ptr2ref.find(ptr); if(it != ptr2ref.end()) { wxeRefData *refd = it->second; - if(refd->alloc_in_erl) { - if(refd->type == 2) { - wxDialog *win = (wxDialog *) ptr; - if(win->IsModal()) { - win->EndModal(-1); - } - parent = win->GetParent(); - if(parent) { - ptrMap::iterator parentRef = ptr2ref.find(parent); - if(parentRef == ptr2ref.end()) { - // The parent is already dead delete the parent ref - win->SetParent(NULL); - } + if(refd->alloc_in_erl && refd->type == 2) { + wxDialog *win = (wxDialog *) ptr; + if(win->IsModal()) { + win->EndModal(-1); + } + parent = win->GetParent(); + if(parent) { + ptrMap::iterator parentRef = ptr2ref.find(parent); + if(parentRef == ptr2ref.end()) { + // The parent is already dead delete the parent ref + win->SetParent(NULL); } + } + if(recurse_level > 0) { + // Delay delete until we are out of dispatch* + delayed_cleanup->Append(Ecmd.Clone()); + delay = true; + } else { delete win; } } } } } + + if(delay) + return; + // First pass, delete all top parents/windows of all linked objects // fprintf(stderr, "close port %x\r\n", Ecmd.port);fflush(stderr); diff --git a/lib/wx/c_src/wxe_impl.h b/lib/wx/c_src/wxe_impl.h index 10ef0862ab..6beb0eb9b9 100644 --- a/lib/wx/c_src/wxe_impl.h +++ b/lib/wx/c_src/wxe_impl.h @@ -79,6 +79,9 @@ public: ptrMap ptr2ref; wxeMemEnv * global_me; + int recurse_level; + wxList * delayed_cleanup; + // Temp container for callbacks char *cb_buff; int cb_len; diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl index 9174b80d52..79dbea0575 100644 --- a/lib/wx/test/wx_basic_SUITE.erl +++ b/lib/wx/test/wx_basic_SUITE.erl @@ -241,21 +241,6 @@ wx_misc(Config) -> %% wx:shutdown() %% How do you test this? - case os:type() of - {win32, _} -> %% These hangs when running automatic tests - skip; %% through ssh on windows. Works otherwise - _ -> - wx_misc:shell([{command,"echo TESTING close the popup shell"}]) - end, - - case wx_test_lib:user_available(Config) of - true -> - wx_misc:shell(); - false -> - %% Don't want to spawn a shell if no user - skip %% is available - end, - ?m(false, wx_misc:isBusy()), ?m(ok, wx_misc:beginBusyCursor([])), ?m(true, wx_misc:isBusy()), @@ -356,6 +341,7 @@ wx_object(Config) -> %% Which it did in my buggy handling of the sync_callback wxWindow:refresh(Frame), ?m([{sync_event, #wx{event=#wxPaint{}}, _}], flush()), + timer:sleep(500), ?m([{cast, slept}], flush()), Monitor = erlang:monitor(process, FramePid), diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl index 2711943e5d..b9c2fafe0e 100644 --- a/lib/wx/test/wx_event_SUITE.erl +++ b/lib/wx/test/wx_event_SUITE.erl @@ -409,32 +409,31 @@ dialog(Config) -> wxFrame:show(Frame), Env = wx:get_env(), Tester = self(), - spawn_link( - fun() -> - wx:set_env(Env), - PD = wxProgressDialog:new("Dialog","Testing", - [%%{parent, Frame}, - {maximum,10}, - {style, ?wxPD_SMOOTH bor ?wxPD_AUTO_HIDE}]), - wxDialog:connect(PD, init_dialog - , [{callback, fun(#wx{event=#wxInitDialog{}}, Ev) -> - ?mt(wxInitDialogEvent, Ev), - io:format("Heyhoo~n", []), - wxEvent:skip(Ev), - Tester ! {progress_dialog,PD} - end}] - ), - wxProgressDialog:showModal(PD), - wxDialog:destroy(PD) - end), - receive {progress_dialog,PD} -> - wxDialog:endModal(PD, ?wxID_OK) - after 5000 -> - exit(timeout) - end, - - wx_test_lib:flush(), - + PD = wxProgressDialog:new("Dialog","Testing", + [%%{parent, Frame}, + {maximum,101}, + {style, ?wxPD_SMOOTH bor ?wxPD_AUTO_HIDE}]), + Forward = fun(#wx{event=#wxInitDialog{}}, Ev) -> + ?mt(wxInitDialogEvent, Ev), + io:format("Heyhoo~n", []), + wxEvent:skip(Ev), + Tester ! {progress_dialog,PD} + end, + wxDialog:connect(PD, init_dialog, [{callback, Forward}]), + Recurse = fun(Recurse, N) -> + true = wxProgressDialog:update(PD, min(N,100)), + timer:sleep(5), + Recurse(Recurse,N+1) + end, + Run = fun() -> + wx:set_env(Env), + Recurse(Recurse, 0) + end, + Worker = spawn_link(Run), + timer:sleep(500), + io:format("Got ~p~n", [wx_test_lib:flush()]), + unlink(Worker), + wxProgressDialog:destroy(PD), wx_test_lib:wx_destroy(Frame, Config). -- cgit v1.2.3