aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Gudmundsson <[email protected]>2014-01-17 16:57:29 +0100
committerDan Gudmundsson <[email protected]>2014-01-22 16:33:01 +0100
commitfb767602c08159daa2190129622ebf185606fd35 (patch)
treead89eb12ac2f554a2151f3605c9122882b7eb386
parent6459bb3e5c82cdd5474bdd77d8aff3a12ce88910 (diff)
downloadotp-fb767602c08159daa2190129622ebf185606fd35.tar.gz
otp-fb767602c08159daa2190129622ebf185606fd35.tar.bz2
otp-fb767602c08159daa2190129622ebf185606fd35.zip
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.
-rw-r--r--lib/wx/c_src/wxe_impl.cpp59
-rw-r--r--lib/wx/c_src/wxe_impl.h3
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl16
-rw-r--r--lib/wx/test/wx_event_SUITE.erl51
4 files changed, 73 insertions, 56 deletions
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).