aboutsummaryrefslogtreecommitdiffstats
path: root/lib/wx/test
diff options
context:
space:
mode:
authorDan Gudmundsson <[email protected]>2013-12-20 09:20:22 +0100
committerDan Gudmundsson <[email protected]>2014-01-14 13:26:53 +0100
commit74f6d6d74f7a3be5aece37a0b419e40d96e036d4 (patch)
tree73ffc54f3b8b9b827f6f2a9aedd46b566dce7ffa /lib/wx/test
parent3f9b80b1e635f3b5f39bd7380b3bd777ebd98338 (diff)
downloadotp-74f6d6d74f7a3be5aece37a0b419e40d96e036d4.tar.gz
otp-74f6d6d74f7a3be5aece37a0b419e40d96e036d4.tar.bz2
otp-74f6d6d74f7a3be5aece37a0b419e40d96e036d4.zip
wx: Fix crash when garbage collect event handlers (debugger caused seg fault)
wxStyledTextCtrl had no wrapper class so, because it was not detected that it had virtual destructors, thus references to it was not cleaned up on destruction. When a process dies and wx cleans up the event handlers access was made to the deleted object and caused a seg fault. Added a testcase which I thought could provoke the bug but didn't.
Diffstat (limited to 'lib/wx/test')
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl13
-rw-r--r--lib/wx/test/wx_event_SUITE.erl33
-rw-r--r--lib/wx/test/wx_obj_test.erl72
3 files changed, 82 insertions, 36 deletions
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index d55a037599..c5b0927bf3 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -324,7 +324,18 @@ data_types(_Config) ->
wx_object(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
wx_object(Config) ->
wx:new(),
- Frame = ?mt(wxFrame, wx_obj_test:start([])),
+ Me = self(),
+ Init = fun() ->
+ Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Test wx_object", [{size, {500, 400}}]),
+ Sz = wxBoxSizer:new(?wxHORIZONTAL),
+ Panel = wxPanel:new(Frame),
+ wxSizer:add(Sz, Panel, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ wxPanel:connect(Panel, size, [{skip, true}]),
+ wxPanel:connect(Panel, paint, [callback, {userData, Me}]),
+ wxWindow:show(Frame),
+ {Frame, {Frame, Panel}}
+ end,
+ Frame = ?mt(wxFrame, wx_obj_test:start([{init, Init}])),
timer:sleep(500),
?m(ok, check_events(flush())),
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index 6a9f19ad51..bbb5294d08 100644
--- a/lib/wx/test/wx_event_SUITE.erl
+++ b/lib/wx/test/wx_event_SUITE.erl
@@ -484,6 +484,7 @@ callback_clean(Config) ->
%% timer:sleep(infinity),
%% ok.
+
white_box_check_event_handlers() ->
{_,_,Server,_} = wx:get_env(),
{status, _, _, [Env, _, _, _, Data]} = sys:get_status(Server),
@@ -495,3 +496,35 @@ white_box_check_event_handlers() ->
gb_trees:to_list(CBs),
[Funs || Funs = {Id, {Fun,_}} <- Env, is_integer(Id), is_function(Fun)]
}.
+
+handler_clean(TestInfo) when is_atom(TestInfo) ->
+ wx_test_lib:tc_info(TestInfo);
+handler_clean(_Config) ->
+ wx:new(),
+ Init = fun() -> create_window() end,
+ Frame1 = wx_obj_test:start([{init, Init}]),
+ ?mt(wxFrame, Frame1),
+ wxWindow:show(Frame1),
+ ?m([_|_], lists:sort(wx_test_lib:flush())),
+ ?m({stop,_}, wx_obj_test:stop(Frame1, fun(_) -> normal end)),
+ ?m([{terminate,normal}], lists:sort(wx_test_lib:flush())),
+
+ Frame2 = wx_obj_test:start([{init, Init}]),
+ wxWindow:show(Frame2),
+ ?m([_|_], lists:sort(wx_test_lib:flush())),
+ ?m({stop,_}, wx_obj_test:stop(Frame2, fun(_) -> wxWindow:destroy(Frame2), normal end)),
+ ?m([{terminate,normal}], lists:sort(wx_test_lib:flush())),
+ timer:sleep(104),
+ ?m({[],[],[]}, white_box_check_event_handlers()),
+ ?m(ok, wx:destroy()),
+ ok.
+
+create_window() ->
+ Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Test wx_object", [{size, {500, 400}}]),
+ Sz = wxBoxSizer:new(?wxHORIZONTAL),
+ Panel = wxPanel:new(Frame),
+ wxSizer:add(Sz, Panel, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ wxWindow:connect(Frame, show),
+ %% wxPanel:connect(Panel, paint, [callback, {userData, foobar}]),
+ wxWindow:connect(Panel, size, [callback]),
+ {Frame, {Frame, Panel}}.
diff --git a/lib/wx/test/wx_obj_test.erl b/lib/wx/test/wx_obj_test.erl
index b4d7640c7e..f47f2fbc46 100644
--- a/lib/wx/test/wx_obj_test.erl
+++ b/lib/wx/test/wx_obj_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,69 +18,71 @@
-module(wx_obj_test).
-include_lib("wx/include/wx.hrl").
--export([start/1]).
+-export([start/1, stop/2]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_sync_event/3, handle_event/2, handle_cast/2]).
--record(state, {frame, panel, opts}).
+-record(state, {parent, opts, user_state}).
start(Opts) ->
- wx_object:start_link(?MODULE, [{parent, self()}, Opts], []).
+ wx_object:start_link(?MODULE, [{parent, self()}| Opts], []).
+
+stop(Object, Fun) ->
+ wx_object:call(Object, {stop, Fun}).
init(Opts) ->
- put(parent_pid, proplists:get_value(parent, Opts)),
- Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Test wx_object", [{size, {500, 400}}]),
- Sz = wxBoxSizer:new(?wxHORIZONTAL),
- Panel = wxPanel:new(Frame),
- wxSizer:add(Sz, Panel, [{flag, ?wxEXPAND}, {proportion, 1}]),
- wxPanel:connect(Panel, size, [{skip, true}]),
- wxPanel:connect(Panel, paint, [callback, {userData, proplists:get_value(parent, Opts)}]),
- wxWindow:show(Frame),
- {Frame, #state{frame=Frame, panel=Panel, opts=Opts}}.
+ Parent = proplists:get_value(parent, Opts),
+ put(parent_pid, Parent),
+ Init = proplists:get_value(init, Opts),
+ {Obj, UserState} = Init(),
+ {Obj, #state{parent=Parent, opts=Opts, user_state=UserState}}.
-handle_sync_event(Event = #wx{obj=Panel}, WxEvent, #state{opts=Opts}) ->
- DC=wxPaintDC:new(Panel), %% We must create & destroy paintDC, or call wxEvent:skip(WxEvent))
- wxPaintDC:destroy(DC), %% in sync_event. Otherwise wx on windows keeps sending the events.
- Pid = proplists:get_value(parent, Opts),
- true = is_pid(Pid),
- Pid ! {sync_event, Event, WxEvent},
+handle_sync_event(Event = #wx{obj=Panel, event=#wxPaint{}},
+ WxEvent, #state{parent=Parent, user_state=US, opts=Opts}) ->
+ case proplists:get_value(redraw, Opts) of
+ undefined ->
+ DC=wxPaintDC:new(Panel), %% We must create & destroy paintDC, or call wxEvent:skip(WxEvent))
+ wxPaintDC:destroy(DC), %% in sync_event. Otherwise wx on windows keeps sending the events.
+ Parent ! {sync_event, Event, WxEvent};
+ Redraw ->
+ Redraw(Event, WxEvent, US)
+ end,
+ ok;
+handle_sync_event(Event, WxEvent, #state{parent=Parent}) ->
+ Parent ! {sync_event, Event, WxEvent},
ok.
-handle_event(Event, State = #state{opts=Opts}) ->
- Pid = proplists:get_value(parent, Opts),
- Pid ! {event, Event},
+handle_event(Event, State = #state{parent=Parent}) ->
+ Parent ! {event, Event},
{noreply, State}.
-handle_call(What, From, State) when is_function(What) ->
- Result = What(State),
+handle_call(What, From, State = #state{user_state=US}) when is_function(What) ->
+ Result = What(US),
{reply, {call, Result, From}, State};
+handle_call({stop, Fun}, From, State = #state{user_state=US}) ->
+ {stop, Fun(US), {stop, From}, State};
handle_call(What, From, State) ->
{reply, {call, What, From}, State}.
-handle_cast(What, State = #state{opts=Opts}) when is_function(What) ->
- Result = What(State),
- Pid = proplists:get_value(parent, Opts),
+handle_cast(What, State = #state{parent=Pid, user_state=US}) when is_function(What) ->
+ Result = What(US),
Pid ! {cast, Result},
{noreply, State};
-handle_cast(What, State = #state{opts=Opts}) ->
- Pid = proplists:get_value(parent, Opts),
+handle_cast(What, State = #state{parent=Pid}) ->
Pid ! {cast, What},
{noreply, State}.
-handle_info(What, State = #state{opts=Opts}) ->
- Pid = proplists:get_value(parent, Opts),
+handle_info(What, State = #state{parent=Pid}) ->
Pid ! {info, What},
{noreply, State}.
-terminate(What, #state{opts=Opts}) ->
- Pid = proplists:get_value(parent, Opts),
+terminate(What, #state{parent=Pid}) ->
Pid ! {terminate, What},
ok.
-code_change(Ver1, Ver2, State = #state{opts=Opts}) ->
- Pid = proplists:get_value(parent, Opts),
+code_change(Ver1, Ver2, State = #state{parent=Pid}) ->
Pid ! {code_change, Ver1, Ver2},
State.