Eliminating Singleton Objects

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

Eliminating Singleton Objects

John W. Eaton
Administrator
In this changeset,

   http://hg.savannah.gnu.org/hgweb/octave/rev/d24d01273bd0

I eliminated the load_path singleton object with a global load_path
object (actually owned by the interpreter class, but with global access
allowed).

In Octave, singleton objects are typically (always?) used to manage
global data, so providing global access is nothing new.  What is new is
that the code is simpler if we don't use the singleton idiom because  we
can eliminate many static functions that simply provide access to a
global object.  The only advantage to having the singleton is that you
don't have to worry about when initialization of the global object will
happen; it occurs on the first use.  But then special treatment is also
needed to clean up and delete the the global object managed by the
singleton.  So I'd rather have these objects converted to normal
classes, and if possible, converted from global to local objects.

For the load_path object, it makes sense to me that it is owned by the
interpreter.  So that's where it is now initialized and it exists as
long as the interpreter object exists.

We currently have ordinary functions outside of the interpreter that
need access to the load_path (for example, any DEFUN like path, addpath,
rmpath, etc. that accesses the load_path object).  These functions
currently access the the global load_path object using the new function
octave::__get_load_path__.  This function is not exported (the header
that declares it is not installed) and I'd like to eliminate it.  To do
that, we need a way to pass a pointer (or reference) to the interpreter
to DEFUN functions.  I'm thinking about inserting a pointer to the
interpreter into the argument list that is passed to DEFUN functions.
Then functions like path could extract that info from the argument list
and not have to access the load_path using a global variable.  This may
involve a change to DEFUN so that it accepts something like
defun_arg_list instead of octave_value_list.  But defun_arg_list would
be convertible to octave_value_list, so existing functions should not
have to change.

One question is what should happen to DEFUN functions called using
feval?  We can provide an interpreter::feval method that can pass a
pointer to the interpreter object, but the current feval function would
not do that.  Is this a problem for existing user code (anything inside
Octave can easily be changed, so I'm not worried about what changes are
required there).

If this problem can be solved for load_path, then there are a number of
other singleton objects that we can eliminate, also without needing to
provide global access to them.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

Olaf Till-2
On Tue, Apr 25, 2017 at 03:43:20PM -0400, John W. Eaton wrote:
> One question is what should happen to DEFUN functions called using feval?
> We can provide an interpreter::feval method that can pass a pointer to the
> interpreter object, but the current feval function would not do that.  Is
> this a problem for existing user code

Probably yes, at least potentially.  In remote execution with the
'parallel' package, each command is executed with Octaves eval_string
function.  The remotely executed command can principally call any
function in any package, so it may call a package function which is
already revised to access the loadpath via this planned pointer to the
interpreter.

With the planned changes to the 'parallel' package, this will also
affect local (multicore) parallel execution in some cases.

But this issue only means that these functions in the affected
packages can't be parallelized in clusters.  They should still work
outside parallelization.  So I'd see no problem if we have to wait a
bit for a changed eval_string function in Octaves development branch.


Of course there may be further problems with existing package code
that I'm currently not aware of.

Olaf

> (anything inside Octave can easily be
> changed, so I'm not worried about what changes are required there).

--
public key id EAFE0591, e.g. on x-hkp://pool.sks-keyservers.net

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

John W. Eaton
Administrator
On 04/28/2017 07:04 AM, Olaf Till wrote:

> On Tue, Apr 25, 2017 at 03:43:20PM -0400, John W. Eaton wrote:
>> One question is what should happen to DEFUN functions called using feval?
>> We can provide an interpreter::feval method that can pass a pointer to the
>> interpreter object, but the current feval function would not do that.  Is
>> this a problem for existing user code
>
> Probably yes, at least potentially.  In remote execution with the
> 'parallel' package, each command is executed with Octaves eval_string
> function.  The remotely executed command can principally call any
> function in any package, so it may call a package function which is
> already revised to access the loadpath via this planned pointer to the
> interpreter.

Any function that uses eval or feval has always needed the interpreter
to exist ("interpreter" meaning the symbol table, load-path, etc.).  The
difference is only whether those things are accessed globally or as
members of an interpreter object.  If you are currently calling eval or
feval, then the fix would be to call interpreter->eval or
interpreter->feval instead.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

Rik-4
In reply to this post by John W. Eaton
On 04/25/2017 01:11 PM, [hidden email] wrote:
Subject:
Eliminating Singleton Objects
From:
"John W. Eaton" [hidden email]
Date:
04/25/2017 12:43 PM
To:
Octave Maintainers List [hidden email]
List-Post:
[hidden email]
Content-Transfer-Encoding:
7bit
Precedence:
list
MIME-Version:
1.0
Message-ID:
[hidden email]
Content-Type:
text/plain; charset=utf-8; format=flowed
Message:
5

In this changeset,

  http://hg.savannah.gnu.org/hgweb/octave/rev/d24d01273bd0

I eliminated the load_path singleton object with a global load_path object (actually owned by the interpreter class, but with global access allowed).

In Octave, singleton objects are typically (always?) used to manage global data, so providing global access is nothing new.  What is new is that the code is simpler if we don't use the singleton idiom because  we can eliminate many static functions that simply provide access to a global object.  The only advantage to having the singleton is that you don't have to worry about when initialization of the global object will happen; it occurs on the first use.  But then special treatment is also needed to clean up and delete the the global object managed by the singleton.  So I'd rather have these objects converted to normal classes, and if possible, converted from global to local objects.

For the load_path object, it makes sense to me that it is owned by the interpreter.  So that's where it is now initialized and it exists as long as the interpreter object exists.

We currently have ordinary functions outside of the interpreter that need access to the load_path (for example, any DEFUN like path, addpath, rmpath, etc. that accesses the load_path object).  These functions currently access the the global load_path object using the new function octave::__get_load_path__.  This function is not exported (the header that declares it is not installed) and I'd like to eliminate it.  To do that, we need a way to pass a pointer (or reference) to the interpreter to DEFUN functions.  I'm thinking about inserting a pointer to the interpreter into the argument list that is passed to DEFUN functions. Then functions like path could extract that info from the argument list and not have to access the load_path using a global variable.  This may involve a change to DEFUN so that it accepts something like defun_arg_list instead of octave_value_list.  But defun_arg_list would be convertible to octave_value_list, so existing functions should not have to change.


Is there a performance issue here?  If you start passing too many arguments to a function, more than can conveniently fit into the number of scratch registers that your CPU has, then you have to pass arguments on the stack which will slow things down.  But I can't quite tell if you are suggesting another parameter, or whether you want to include the pointer to the interpreter as say the first entry in the octave_value_list passed as inputs to the function.  Presumably the octave_value_list is already passed as a pointer/reference since it could be large.  In that case it wouldn't have an impact.

--Rik

 


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

John W. Eaton
Administrator
On 04/28/2017 07:49 PM, Rik wrote:

> Is there a performance issue here?  If you start passing too many
> arguments to a function, more than can conveniently fit into the number
> of scratch registers that your CPU has, then you have to pass arguments
> on the stack which will slow things down.  But I can't quite tell if you
> are suggesting another parameter, or whether you want to include the
> pointer to the interpreter as say the first entry in the
> octave_value_list passed as inputs to the function.  Presumably the
> octave_value_list is already passed as a pointer/reference since it
> could be large.  In that case it wouldn't have an impact.

At first, I thought it would be easier to just add a pointer to the
interpreter object to the octave_value_list that is passed to functions.
  But it turned out to be difficult to chase down all the places where
it is needed.  There are many locations where it could be needed and I
couldn't figure out an easy way to get help from the compiler.

So I started working on passing the pointer to the interpreter as an
additional parameter to DEFUN functions.  It seems much cleaner this
way.  It also means that these functions will now accept three arguments
instead of two.  One is a pointer to the interpreter, one is a const
reference to the octave_value_list containing the arguments, and one is
an int for nargout.  I doubt that passing one additional pointer to
these functions will make a big difference overall, but we can certainly
measure it to be sure.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

Rik-4
On 04/28/2017 06:17 PM, John W. Eaton wrote:

> On 04/28/2017 07:49 PM, Rik wrote:
>
>> Is there a performance issue here?  If you start passing too many
>> arguments to a function, more than can conveniently fit into the number
>> of scratch registers that your CPU has, then you have to pass arguments
>> on the stack which will slow things down.  But I can't quite tell if you
>> are suggesting another parameter, or whether you want to include the
>> pointer to the interpreter as say the first entry in the
>> octave_value_list passed as inputs to the function.  Presumably the
>> octave_value_list is already passed as a pointer/reference since it
>> could be large.  In that case it wouldn't have an impact.
>
> At first, I thought it would be easier to just add a pointer to the
> interpreter object to the octave_value_list that is passed to functions.
>  But it turned out to be difficult to chase down all the places where it
> is needed.  There are many locations where it could be needed and I
> couldn't figure out an easy way to get help from the compiler.
>
> So I started working on passing the pointer to the interpreter as an
> additional parameter to DEFUN functions.  It seems much cleaner this
> way.  It also means that these functions will now accept three arguments
> instead of two.  One is a pointer to the interpreter, one is a const
> reference to the octave_value_list containing the arguments, and one is
> an int for nargout.  I doubt that passing one additional pointer to these
> functions will make a big difference overall, but we can certainly
> measure it to be sure.

It's true.  I don't think register pressure is going to develop at 2 or 3
function parameters.  I thought we might already be at the 4-5 parameter
range in which case another could make a difference.

If the compiler is no longer doing initialization of the singleton, is this
the cause of certain data race bugs between the interpreter and the GUI
thread?  Bug #50880 and #50396 both seem to be caused by the GUI looking
for interpreter static variables that have not been initialized in time.

Also, if the pointer to the interpreter really is used all over the code
base, maybe it does make sense to have it be a global?  I know it is
generally frowned upon, but it might actually be simpler then re-working
the API to pass the pointer through to all functions.

--Rik



Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

John W. Eaton
Administrator
On 04/30/2017 11:28 PM, Rik wrote:

> It's true.  I don't think register pressure is going to develop at 2 or 3
> function parameters.  I thought we might already be at the 4-5 parameter
> range in which case another could make a difference.
>
> If the compiler is no longer doing initialization of the singleton, is this
> the cause of certain data race bugs between the interpreter and the GUI
> thread?  Bug #50880 and #50396 both seem to be caused by the GUI looking
> for interpreter static variables that have not been initialized in time.

With the singleton idiom, there is no way for the object to be used
before it is initialized.  That's the one nice thing about singletons.
But, with multiple threads, I suppose it's possible that the
initialization code could be called from two threads simultaneously, and
that will certainly cause trouble.

> Also, if the pointer to the interpreter really is used all over the code
> base, maybe it does make sense to have it be a global?  I know it is
> generally frowned upon, but it might actually be simpler then re-working
> the API to pass the pointer through to all functions.

For now it will have to remain global because we need a way to access
the objects like the symbol table and the load path that are singletons.
  But I would eventually like to eliminate the global because I would
like to be able to allow a single program to contain multiple
interpreter objects that are completely independent of one another.  So
all data for an interpreter instance would need to be owned by the
interpreter object.  Maybe that's an impossible goal, but for now I'd
like to aim for it.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects (Re: Coloring error and warning messages in the gui console)

Daniel Sebald
In reply to this post by Rik-4
On 04/30/2017 10:28 PM, Rik wrote:

> On 04/28/2017 06:17 PM, John W. Eaton wrote:
>> On 04/28/2017 07:49 PM, Rik wrote:
>>
>>> Is there a performance issue here?  If you start passing too many
>>> arguments to a function, more than can conveniently fit into the number
>>> of scratch registers that your CPU has, then you have to pass arguments
>>> on the stack which will slow things down.  But I can't quite tell if you
>>> are suggesting another parameter, or whether you want to include the
>>> pointer to the interpreter as say the first entry in the
>>> octave_value_list passed as inputs to the function.  Presumably the
>>> octave_value_list is already passed as a pointer/reference since it
>>> could be large.  In that case it wouldn't have an impact.
>>
>> At first, I thought it would be easier to just add a pointer to the
>> interpreter object to the octave_value_list that is passed to functions.
>>  But it turned out to be difficult to chase down all the places where it
>> is needed.  There are many locations where it could be needed and I
>> couldn't figure out an easy way to get help from the compiler.
>>
>> So I started working on passing the pointer to the interpreter as an
>> additional parameter to DEFUN functions.  It seems much cleaner this
>> way.  It also means that these functions will now accept three arguments
>> instead of two.  One is a pointer to the interpreter, one is a const
>> reference to the octave_value_list containing the arguments, and one is
>> an int for nargout.  I doubt that passing one additional pointer to these
>> functions will make a big difference overall, but we can certainly
>> measure it to be sure.
>
> It's true.  I don't think register pressure is going to develop at 2 or 3
> function parameters.  I thought we might already be at the 4-5 parameter
> range in which case another could make a difference.
>
> If the compiler is no longer doing initialization of the singleton, is this
> the cause of certain data race bugs between the interpreter and the GUI
> thread?  Bug #50880 and #50396 both seem to be caused by the GUI looking
> for interpreter static variables that have not been initialized in time.
>
> Also, if the pointer to the interpreter really is used all over the code
> base, maybe it does make sense to have it be a global?  I know it is
> generally frowned upon, but it might actually be simpler then re-working
> the API to pass the pointer through to all functions.

(I wrote this a couple weeks back, but I think JWE had followed up on
Rik's idea.  However, a similar issue came up with a post regarding GUI
error colors, so I'm posting this more as a general reply.)

The global variable idea is frowned upon for good reason.  Global
variables get abused for quick fixes which leads to strange bugs that
are difficult to track.

Philosophically "pointer to interpreter all over code base" is something
I've tried to deter because I think it is going the wrong direction.
For example, intermixing of GUI and core/interpreter is unnecessary, as
I feel there is a wealth of built-in commands that offer up all the
information the GUI needs, if only there were a simple routine to return
the result of an interpreter command (which can be done across a thread
by passing a pointer to an octave_value_list rather than a reference to
an octave_value_list).

Not only is keeping things separate good programming practice, it also
achieves a couple things.  One, the concept of passing an interpreter
command result back to the GUI is a narrow pathway such that any GUI,
not just this particular GUI, can benefit if it just utilizes that
pathway.  Two, if the interpreter and GUI become too much like one, it
means expertise in the callbacks, memory sharing, etc. is required by
the programmer.  In other words, people like JWE are required to make
the whole thing work.  Keeping things separate means people who've
worked on the core can just concentrate on that--and the interpreter
seems pretty stable in recent years so that means freeing up some
people's effort.

The only argument I could see for meshing the GUI and interpreter is to
do very efficient realtime processing like streaming data from some
exterior application into Octave, but such a thing is unlikely to happen
any time soon, if ever.

Dan

Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

John W. Eaton
Administrator
In reply to this post by Rik-4
On 04/30/2017 11:28 PM, Rik wrote:

> Also, if the pointer to the interpreter really is used all over the code
> base, maybe it does make sense to have it be a global?  I know it is
> generally frowned upon, but it might actually be simpler then re-working
> the API to pass the pointer through to all functions.

I'm beginning to think the idea of being able to start multiple
interpreters in a single program is just never going to be possible, at
least not with the current libraries we use and APIs we need to support.

But even so, would it still be beneficial to more clearly define which
objects own which data?  For example, the interpreter currently owns the
evaluator and the load-path object.  It seems to me that most other
global objects like the symbol table, type info, the call stack, etc.,
should probably also be part of the interpreter or evaluator rather than
separate global singleton objects.

I guess the question I'm asking is whether we should have an "Octave
interpreter" object that is composed of all the things that make up the
interpreter, or whether we should have a bunch of global objects
(implemented as singletons or not; that's really just an implementation
detail) that are loosely organized to make up the interpreter.

One nice thing about having the objects that make up the interpreter
organized as a single object is that all resource allocation for that
interpreter object can be managed by the constructor and destructor for
the object.  So, if things are done correctly, one may create/destroy
interpreter objects as needed.  This could be useful for embedding the
interpreter in applications.

Another advantage is that the components of the interpreter can be
assumed to be valid as long as the interpreter object is valid.

Objects that are owned by the interpreter may (if needed) be constructed
with a reference to the interpreter that contains them.  Then they can
have access to other interpreter objects without having to use a global
object.  To me, this seems similar to the idea of a parent widget in Qt.

I know this is not strictly necessary since we could always just use a
global variable (and why not, if there is only ever going to be one
interpreter object?).  But avoiding the global means we are at least
moving in the direction of having a self-contained interpreter object
that could allow for simultaneous instances.

Finally, here's what I'm thinking of for DEFUN-like built-in functions.
I propose adding another signature for built-in functions that accepts a
reference to the interpreter that invoked them.  For example

   DEFMETHOD (fun, interp, args, nargout)
   {
     // Here, interp is a reference, so
     // guaranteed to be valid.  Just use it.

     interp.do_something ();

     ...
   }

To me, this seems better than accessing a global pointer, or even
passing a pointer, because a reference must be valid so there's no need
to do something like

   if (! interp)
     error ("fun: no interpreter context!");

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects (Re: Coloring error and warning messages in the gui console)

John W. Eaton
Administrator
In reply to this post by Daniel Sebald
On 05/09/2017 04:13 PM, Daniel J Sebald wrote:

> Philosophically "pointer to interpreter all over code base" is something
> I've tried to deter because I think it is going the wrong direction.

Please see the reply to one of Rik's messages that I just posted about
this topic.  I'd appreciate comments there about whether we should have
many global objects that together make up "the interpreter" or an
interpreter object that is composed of the objects that make up the
interpreter.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects (Re: Coloring error and warning messages in the gui console)

John W. Eaton
Administrator
In reply to this post by Daniel Sebald
On 05/09/2017 04:13 PM, Daniel J Sebald wrote:

> For
> example, intermixing of GUI and core/interpreter is unnecessary, as I
> feel there is a wealth of built-in commands that offer up all the
> information the GUI needs, if only there were a simple routine to return
> the result of an interpreter command (which can be done across a thread
> by passing a pointer to an octave_value_list rather than a reference to
> an octave_value_list).

I don't see how a reference is different from a pointer for that
purpose.  Can you explain?

In any case, yes, I agree that it would be better to have a better
mechanism for GUI <-> interpreter communication.  I know you had a
proposal for this some time ago.  I would be glad to take another look
at that now.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

Daniel Sebald
In reply to this post by John W. Eaton
On 05/18/2017 03:40 PM, John W. Eaton wrote:

> On 04/30/2017 11:28 PM, Rik wrote:
>
>> Also, if the pointer to the interpreter really is used all over the code
>> base, maybe it does make sense to have it be a global?  I know it is
>> generally frowned upon, but it might actually be simpler then re-working
>> the API to pass the pointer through to all functions.
>
> I'm beginning to think the idea of being able to start multiple
> interpreters in a single program is just never going to be possible, at
> least not with the current libraries we use and APIs we need to support.
>
> But even so, would it still be beneficial to more clearly define which
> objects own which data?  For example, the interpreter currently owns the
> evaluator and the load-path object.  It seems to me that most other
> global objects like the symbol table, type info, the call stack, etc.,
> should probably also be part of the interpreter or evaluator rather than
> separate global singleton objects.
>
> I guess the question I'm asking is whether we should have an "Octave
> interpreter" object that is composed of all the things that make up the
> interpreter, or whether we should have a bunch of global objects
> (implemented as singletons or not; that's really just an implementation
> detail) that are loosely organized to make up the interpreter.
>
> One nice thing about having the objects that make up the interpreter
> organized as a single object is that all resource allocation for that
> interpreter object can be managed by the constructor and destructor for
> the object.  So, if things are done correctly, one may create/destroy
> interpreter objects as needed.  This could be useful for embedding the
> interpreter in applications.
>
> Another advantage is that the components of the interpreter can be
> assumed to be valid as long as the interpreter object is valid.

Some good ideas here, of which I like this one the most.  Doing so would
probably free just a tiny fraction of code, but it's nice not having to
do the validity check.

Dan


> Objects that are owned by the interpreter may (if needed) be constructed
> with a reference to the interpreter that contains them.  Then they can
> have access to other interpreter objects without having to use a global
> object.  To me, this seems similar to the idea of a parent widget in Qt.
>
> I know this is not strictly necessary since we could always just use a
> global variable (and why not, if there is only ever going to be one
> interpreter object?).  But avoiding the global means we are at least
> moving in the direction of having a self-contained interpreter object
> that could allow for simultaneous instances.
>
> Finally, here's what I'm thinking of for DEFUN-like built-in functions.
> I propose adding another signature for built-in functions that accepts a
> reference to the interpreter that invoked them.  For example
>
>   DEFMETHOD (fun, interp, args, nargout)
>   {
>     // Here, interp is a reference, so
>     // guaranteed to be valid.  Just use it.
>
>     interp.do_something ();
>
>     ...
>   }
>
> To me, this seems better than accessing a global pointer, or even
> passing a pointer, because a reference must be valid so there's no need
> to do something like
>
>   if (! interp)
>     error ("fun: no interpreter context!");
>
> jwe

Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects (Re: Coloring error and warning messages in the gui console)

Daniel Sebald
In reply to this post by John W. Eaton
On 05/18/2017 03:50 PM, John W. Eaton wrote:

> On 05/09/2017 04:13 PM, Daniel J Sebald wrote:
>
>> For
>> example, intermixing of GUI and core/interpreter is unnecessary, as I
>> feel there is a wealth of built-in commands that offer up all the
>> information the GUI needs, if only there were a simple routine to return
>> the result of an interpreter command (which can be done across a thread
>> by passing a pointer to an octave_value_list rather than a reference to
>> an octave_value_list).
>
> I don't see how a reference is different from a pointer for that
> purpose.  Can you explain?

My recollection is that it's different.  Mike Miller may have pointed
out the problem with reference counting across the thread, so I dug into
just what it is that Qt is doing when queuing signals (at the time I
built the latest Qt so I could print out some debugging info deep in the
signal/slot mechanism).  I can't recall exactly, but I'll try to explain
from what I remember.

So my thought on any mechanism that communicates data across the thread
is that the GUI should think of the core as a resource and not hog that
resource by doing graphics-related activity when the core is
communicating it's function result.  For example, don't do plotting or
complicated table-updates, etc. while the core is frozen waiting for an
acknowledgement that the octave_value_list result is no longer needed.
That means quickly copying the data on the GUI thread, then free the
interpreter thread, then do the time-consuming graphical items.

That copying of the data, I believe is what the issue is.  I think that
when a reference was passed over, when the GUI copied the data as a
reference, it increased that object's reference count by 1.  How to undo
that from the GUI side, once the core is acting independently again
(i.e., not frozen waiting on the GUI), I don't know.  Maybe a system
allowing reference counting in and across both threads can be done, but
I thought it better to only let the GUI make immediate copies of data
and that is all.  Otherwise, maintenance might get too complicated.
Stated differently, I think it best if only core in its one thread deal
with reference counting.

In any case, when I reconfigured that communication to use the
octave_value_list pointer rather than the reference, copying via pointer
did not affect the reference count.  At the time of this GUI copy, the
core was frozen in a sleep state, so that the data can't disappear and
result in a segmentation fault.  From the core's standpoint, once the
GUI wakes it again, it's as if nothing changed.

Dan

Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects (Re: Coloring error and warning messages in the gui console)

Daniel Sebald
On 05/18/2017 04:55 PM, Daniel J Sebald wrote:

> Maybe a system
> allowing reference counting in and across both threads can be done, but
> I thought it better to only let the GUI make immediate copies of data
> and that is all.  Otherwise, maintenance might get too complicated.
> Stated differently, I think it best if only core in its one thread deal
> with reference counting.

I suppose a variation on this that wouldn't require the core going into
a sleep state would be to have the core copy the result to some mutually
shared memory, let the GUI know it is ready, then leave it the
responsibility of the GUI to clean up that memory (or the GUI sends an
acknowledgement that the memory is no longer needed...that's a bit like
garbage collection though, which I suspect isn't desirable).

Dan

Reply | Threaded
Open this post in threaded view
|

Re: Eliminating Singleton Objects

Rik-4
In reply to this post by John W. Eaton
On 05/18/2017 01:40 PM, John W. Eaton wrote:

> On 04/30/2017 11:28 PM, Rik wrote:
>
>> Also, if the pointer to the interpreter really is used all over the code
>> base, maybe it does make sense to have it be a global?  I know it is
>> generally frowned upon, but it might actually be simpler then re-working
>> the API to pass the pointer through to all functions.
>
> I'm beginning to think the idea of being able to start multiple
> interpreters in a single program is just never going to be possible, at
> least not with the current libraries we use and APIs we need to support.
>
> But even so, would it still be beneficial to more clearly define which
> objects own which data?  For example, the interpreter currently owns the
> evaluator and the load-path object.  It seems to me that most other
> global objects like the symbol table, type info, the call stack, etc.,
> should probably also be part of the interpreter or evaluator rather than
> separate global singleton objects.
>
> I guess the question I'm asking is whether we should have an "Octave
> interpreter" object that is composed of all the things that make up the
> interpreter, or whether we should have a bunch of global objects
> (implemented as singletons or not; that's really just an implementation
> detail) that are loosely organized to make up the interpreter.
>
> One nice thing about having the objects that make up the interpreter
> organized as a single object is that all resource allocation for that
> interpreter object can be managed by the constructor and destructor for
> the object.  So, if things are done correctly, one may create/destroy
> interpreter objects as needed.  This could be useful for embedding the
> interpreter in applications.
>
> Another advantage is that the components of the interpreter can be
> assumed to be valid as long as the interpreter object is valid.
>
> Objects that are owned by the interpreter may (if needed) be constructed
> with a reference to the interpreter that contains them.  Then they can
> have access to other interpreter objects without having to use a global
> object.  To me, this seems similar to the idea of a parent widget in Qt.
>
> I know this is not strictly necessary since we could always just use a
> global variable (and why not, if there is only ever going to be one
> interpreter object?).  But avoiding the global means we are at least
> moving in the direction of having a self-contained interpreter object
> that could allow for simultaneous instances.

In principle, it seems that even loosely related objects are probably
better off together.  On the other hand, I don't see the point in doing a
lot of proactive work on a topic that hasn't been figured out
definitively.  If the eventual resolution for enabling multiple
interpreters is going to require a different architecture then maybe we
should just leave the current code alone until that change is ready to be
made.  It seems that there are other topics, such as making cross-thread
data transfer reliable, that would be better uses of time.

>
> Finally, here's what I'm thinking of for DEFUN-like built-in functions. I
> propose adding another signature for built-in functions that accepts a
> reference to the interpreter that invoked them.  For example
>
>   DEFMETHOD (fun, interp, args, nargout)
>   {
>     // Here, interp is a reference, so
>     // guaranteed to be valid.  Just use it.
>
>     interp.do_something ();
>
>     ...
>   }

How many lines of code needs to change to accommodate the new macro?  Since
the benefits are fairly nebulous at this point, I wouldn't want to change
the 871 DEFUN instances in libinterp.

--Rik