[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Update on Eliminating Singleton Objects

From: John W. Eaton
Subject: Update on Eliminating Singleton Objects
Date: Sat, 6 Jan 2018 17:50:23 -0500
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.4.0

[I'm quoting the whole message since it's been nearly 8 months...]

On 05/18/2017 04: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.

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!");

Now that we've had some experience with this approach, I'd say my reaction is mostly positive. Today, I eliminated the singleton pattern from the octave_value_typeinfo class and now there are about 15 objects remaining that use the singleton idiom. I'm still hoping to remove the singleton pattern from all of them.

There are some locations where accessing an object owned by the interpreter can't be done easily without asking for a global reference to the interpreter. That's what the functions in the interpreter-private.{h,cc} files allow.

Over time, as more things are added to the interpreter object, or as more DEFUN functions become DEFMETHOD functions, I've been able to remove a few calls to the interpreter-private functions. But I don't think it will be reasonable to eliminate all uses of the global object.

Even so, we still may be able to have multiple interpreter objects active in the same program if we are careful to use thread_local data (standard in C++11) instead of the usual static (class or file scope) or global data.

So my new goal is to eliminate all the singleton objects, pass references to the interpreter and related objects where it is reasonable to do so, and make all global and static objects that are related to the interpreter be thread_local instead of plain old global or static variables, and hopefully one day be able to start a program that uses multiple Octave interperters simultaneously, even if they must run in separate threads.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]