I am working on a gs-style interface to wx, and was hoping someone may
have an opinion on a couple of design issues:
1) I've been maintaining an ets table to cope with naming/references
and command events. In order to keep the references unique across
multiple UI instances (processes), I've used a key format that is
basically {self(), name}/{reference, data}. I've had problems
convincing myself that these tables are safe as they must be marked
"public". However, I'm now thinking that these references would be
better off in the process dictionary (something I am usually cautious
of doing) as these stored references are, or should be (a) private to
the process, (b) "transient" with the process, and (c) irrelevant
outside the process. So my question here is: in this case, is it as
valid as it seems to me now to the use of the process dictionary to
store a fair amount of data, and if so what limitations should I watch
out for?
2) Starting the UI instance process merely uses spawn (and returns the
pid to the calling process). It's troubled me that perhaps the GUI
process should be more "OTP". However, given that this is a framework
that builds on wxe (with the graphics server already there), my
thinking is that the ownership (and supervision) of the UI process
would be better implemented by the calling application, since it can
link and supervise the UI. Making the UI run off a gen_server seems an
unnecessary complexity in the design of the framework. My question
here is: Simple though this is, is my thinking correct?
Thanks!
/s
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://www.erlang.org/mailman/listinfo/erlang-questions
> Hi all,
>
> I am working on a gs-style interface to wx, and was hoping someone may
> have an opinion on a couple of design issues:
>
> 1) I've been maintaining an ets table to cope with naming/references
> and command events. In order to keep the references unique across
> multiple UI instances (processes), I've used a key format that is
> basically {self(), name}/{reference, data}. I've had problems
> convincing myself that these tables are safe as they must be marked
> "public". However, I'm now thinking that these references would be
> better off in the process dictionary (something I am usually cautious
> of doing) as these stored references are, or should be (a) private to
> the process, (b) "transient" with the process, and (c) irrelevant
> outside the process. So my question here is: in this case, is it as
> valid as it seems to me now to the use of the process dictionary to
> store a fair amount of data, and if so what limitations should I watch
> out for?
Oldtimers avoid the process dict because it used to be slow. "Real"
functional programmers avoid both ets and the dict for religious
reasons. AFAIK, there's no real reason to avoid the process dict in
favor of ets.
> 2) Starting the UI instance process merely uses spawn (and returns the
> pid to the calling process). It's troubled me that perhaps the GUI
> process should be more "OTP". However, given that this is a framework
> that builds on wxe (with the graphics server already there), my
> thinking is that the ownership (and supervision) of the UI process
> would be better implemented by the calling application, since it can
> link and supervise the UI. Making the UI run off a gen_server seems an
> unnecessary complexity in the design of the framework. My question
> here is: Simple though this is, is my thinking correct?
In my experience, it pays off to use gen_server right from the
start. Otherwise, once the state machine starts to grow, you'll
either refactor to a gen_server or wish you had.
gen_server has no noticeable performance penalty, but you do need to
write a bunch of boilerplate. You might want to check out this(*)
code for an example of how to abstract away the boilerplate.
mats
(*) gen_serv implements a gen_server callback. It will delegate to its
own callback if possible (it's some sort of inheritance). E.g. given
this module;
-module('foo').
-author('Mats Cronqvist').
-export([handle_call/3]).
handle_call(Msg,From,State) ->
io:fwrite("~p~n",[{Msg,From,State}]),
{ok,State}.
you can do this;
1> gen_serv:start(foo).
2> gen_server:call(foo,bla).
{bla,{<0.28519.2>,#Ref<0.0.12.74776>},undefined}
http://code.google.com/p/eper/source/browse/trunk/src/gen_serv.erl
I agree, sort of. I've argued before that the process
dictionary is better than its reputation suggests. (:
There are some different tradeoffs, though:
- No GC on ets data, but data in the process dict will
be subject to GC. Good or bad depending on how you
look at it.
- Objects in the process dict will be included in a
crash report, while objects in ets will simply go
away silently. Good or bad depending on your needs.
- No copying when accessing the PD, while ETS accesses
always copy (except large binaries, of course).
The fact that the PD is GC:d will to some degree cancel
out this advantage.
- No lock contention when accessing the PD. Not sure
exactly how this looks when other processes try to
peek at your PD, but please don't use that for anything
but debugging. (:
In Steve's case, I guess there are no significant
disadvantages in using the PD over ETS.
BR,
Ulf W
--
Ulf Wiger
CTO, Erlang Training & Consulting Ltd
http://www.erlang-consulting.com
Thanks for the assistance and guidance.
re: 1) process dictionary - I've done this refactor and it has improved
the code clarity a great deal. My only rule with this was to make the
process dictionary entries "write only" as advised by Mr Armstrong in
his excellent book. The code looks a good deal more sane, and indeed is
considerably safer because it's not using the public ets that was
apparently necessary before.
re: 2) gen_server - I did notice that I had already begun implementing
some remote calls for the more experimental features, so your answer
here rings very, very true. I'm doing the gen_server refactor now rather
than later.
Thanks again :)
Regards,
Steve
Thanks for confirming that the PD was the right way to go.
There's some very useful insights in your post that I'm sure are
useful for others as well as me personally!)
I'll let everyone know how I get on, and perhaps try this interface
when it's stable and sufficiently documented.
If you are interested in peeking at my current successes and disasters
with this, it's all on github at http://github.com/komone/gx
Regards,
Steve
Right now, gx is down to one UI instance per module temporarily, as
I've chosen to use ?MODULE to register the server, but that's just a
wrinkle.
What was a surprise to me was that the WX events appeared in
handle_info rather than handle_cast. You learn something new every
day :)
Thanks, to both Mats and Ulf for the timely and very useful input on
this.
Regards,
Steve