Single instance of a form

Posted by jmls on 28-Jan-2009 10:56

I have a window / form that I must only ever have one instance of. Is there any way of using static or a form property to automatically enable this functionality, or must we do the same as ABL and write code to manage windows ?

All Replies

Posted by Peter Judge on 28-Jan-2009 11:01

I have a window / form that I must only ever have one

instance of. Is there any way of using static or a

form property to automatically enable this

functionality, or must we do the same as ABL and

write code to manage windows ?

Would form1:ShowDialog() work for you?

Also, I would think that a form can be launched as a singleton* which might satisfy your requirements.

However, in either case, there's going to be some ABL code that invokes the window, and at that point you could make the decision whether to show once.

-- peter

  • but am in the middle of an extremely slow install at the mo, so can't test it myself

Posted by Simon de Kraa on 28-Jan-2009 11:02

Search for singleton form.

Posted by jmls on 28-Jan-2009 11:30

gaakk!. /me's head explodes

Posted by jmls on 28-Jan-2009 11:52

now to convert this to ABL ...

{}

Posted by Peter Judge on 28-Jan-2009 11:57

Easy-peasy

Frozen-finger typing corrected by:

Peter Judge

Message was edited by:

Peter Judge

Posted by jmls on 28-Jan-2009 12:03

Easy-peasy

showoff.

why a method rather than a property ?

oh, and shouldn't

be

added extra question ...

Message was edited by:

Julian Lyndon-Smith

Posted by Admin on 28-Jan-2009 12:03

First remove the sealed keyword...

And change the second line to:

DEFINE PRIVATE STATIC VARIABLE instance AS Singleton NO-UNDO.

And in an additional STATIC CONSTRUCTOR:

instance = NEW Singleton();

As you see, the only constructor in the sample you have given is private (and not static). So the class is the only one who can create an instance and that happens only once - the first time somebody asks for Singleton:Instance.

Usually you'd replace "Singleton" with the name of your class.

But I'm not sure, if the Visual Designer will allow a class to be designed with only a private constructor. Somewhere I read, that there needs to be a public constructor in order to satisfy the VD. But that would be an easy test. If you are able to reopen such a Form, it should be o.k..

I usually maintain a static variable/property in the Form class. In the public non static Constructor, I check if it is set (VALID-OBJECT) and UNDO, THROW ... when so - because there's already an instance. If not, I set that variable to THIS-OBJECT, so that the 2nd instance will detect that it should not be there.

Mike

Posted by Matt Baker on 28-Jan-2009 12:11

VD shouldn't have a problem opening the file. You won't be able to extend it though which would be prevented because of the private constructor. In this case you might as well mark the class as FINAL.

The problem you WILL have with OEA is that you won't be able to run it by just pressing the launch button since OEA doesn't know about the Singleton property or whatever you decide to call it. You will only be able to run it in context of your app.

Posted by jmls on 28-Jan-2009 12:13

I usually maintain a static variable/property in the

Form class. In the public non static Constructor, I

check if it is set (VALID-OBJECT) and UNDO, THROW ...

when so - because there's already an instance. If

not, I set that variable to THIS-OBJECT, so that the

2nd instance will detect that it should not be

there.

but how do you return the value of the first instance to the calling program if you've thrown an error ?

Posted by Peter Judge on 28-Jan-2009 12:14

why a method rather than a property ?

Good question.

From http://en.wikipedia.org/wiki/Singleton_pattern, C# uses a property. Java uses a method. I've a tendency to follow the Java style, but in the ABL either will suffice and they'll both perform the same.

Actually, I like this approach more , now that I think about it. /me will now update my singletons.

be

Yes. I will claim that was a Frozen Finger Foulup ...

-- peter

Posted by Matt Baker on 28-Jan-2009 12:20

The example is incomplete. What if someone calls dispose? The object handle would still be valid, but trying to call Show() on it would be a disaster

foo:GetInstance():Dispose().

You should override the dispose method and set the mFoo variable to unknown(?)

Message was edited by:

Matthew Baker

Posted by jmls on 28-Jan-2009 12:46

won't the instance property return a new foo every time ? Don't you have to add a static var, set that if not a valid object, and return the var ?

Posted by jmls on 28-Jan-2009 12:49

urk. something is not right - probably in my mind

run this code:

you get a "new foo" message everytime you run the program. What am I missing ?

Posted by Matt Baker on 28-Jan-2009 13:00

How are you running this? New prowin32.exe session each time, or same session?

Posted by Peter Judge on 28-Jan-2009 13:10

How are you running this? New prowin32.exe session

each time, or same session?

Note that the Procedure editor also cleans up after itself on each run (in this case, a private destructor may foil these cleanup attempts).

-- peter

Posted by Admin on 28-Jan-2009 13:13

I doubt there's a solution for that in the ABL. NEW always returns a brand new instance.

I usually check that static property, if the object is there already. In any cases (my and Peters solution) the caller needs to know, that he's executing a singleton object.

Posted by jmls on 28-Jan-2009 16:30

I thought that static "things" could not be destroyed until a session restart ?

Also, perhaps you know something we don't :

gives "Destructors must be public (12996)"

-- peterNote that the Procedure editor also cleans up after

itself on each run (in this case, a private

destructor may foil these cleanup attempts).

Posted by jmls on 28-Jan-2009 16:32

I doubt there's a solution for that in the ABL. NEW

always returns a brand new instance.

Curses! foiled again.

 * jmls goes back to stroking the moustache ...

I usually check that static property, if the object

is there already. In any cases (my and Peters

solution) the caller needs to know, that he's

executing a singleton object.

Yeah ... it's not nice ... but yeah ...

Posted by jmls on 28-Jan-2009 16:38

err, hold on ... I'm not "newing" an object everytime (supposedly). The instantiating code only referenced the "instance" property - a public static property, which references a private STATIC property. Foo was only created if said property was not valid

Why would a static property not be valid ..... dammit ..... just thought why ....

I'll leave this here for some other poor sod to trip over and save him from the pain.

Answer:

Whilst the static property is kept during re-runs, when the editor returns the garbage collection kicks in, and deletes the foo instance so that the next time the "instance property" is referenced, that original foo object has been put into the great garbage bin in the sky, and therefore is no longer valid. So a new foo instance is created.

Am I right ?

 * jmls thinks that he had better be, or a very quick edit of the message will be needed.

urk. something is not right - probably in my mind

run this code:

you get a "new foo" message everytime you run the

program. What am I missing ?

Posted by jmls on 28-Jan-2009 16:41

So, my next question would be:

Can a constructor return an object of a different instance to the one that would be created ?

Posted by Thomas Mercer-Hursh on 28-Jan-2009 17:05

This sounds wrong to me. A garbage collector has no business cleaning up a static or the static would have little point.

Posted by jmls on 28-Jan-2009 17:07

This sounds wrong to me. A garbage collector has no

business cleaning up a static or the static would

have little point.

You misread what I said : I think that the static part is kept, but the foo object created by the new foo() was garbage collected. That's why valid-object is not, and that's why I get a new foo() when I run it again

Posted by Thomas Mercer-Hursh on 28-Jan-2009 17:11

I haven't really followed through on every twist and turn here, but in Peter's original easy-peasy code, mFoo is a static variable so it should not get GC'd.

Posted by jmls on 28-Jan-2009 17:16

I haven't really followed through on every twist and

turn here, but in Peter's original easy-peasy code,

mFoo is a static variable so it should not get GC'd.

mfoo itself is not GC'd - it probably still holds the value of the original foo object. However, the foo object that was created with the new foo() (let's call it foo1) is not static and therefore would be GC'd. Therefore the next time the static method / property was called, foo1 is no longer a valid object and so new foo() is called again

Posted by Thomas Mercer-Hursh on 28-Jan-2009 17:39

Since we have had some typos and other confusions, let's go back to the code:

Is this the code we are all talking about?

So, run the first time an mFoo gets created with let's say object ID 12345. Its static, so should not get GC'd. Next time it is run, mFoo 12345 should still be there and now new will happen. That's the point of static, no?

Posted by jmls on 28-Jan-2009 17:56

static var mfoo is set to 12345, agreed.

mfoo is static, so it is not GC'd

however, the object foo that was created is not a static object and thus gets GC'd

on the second run, mfoo is still set to 12345, but object 12345 has been GC'd and is not valid, therefore a new object is created.

Posted by Thomas Mercer-Hursh on 28-Jan-2009 18:19

Well, one of the problems here is that you've been coding and I've been doing other things so I haven't actually used this stuff. So, let me comment a bit on what we have been looking at and then back up a bit.

With the class whose code we have been looking at (all due reservations considering what follows below) there will be a calling context in which we set some local class equal to the instance of mFoo . This might be called jFoo in that context. The context of jFoo might go away at the end of the session, but mFoo is still there and, if we are doing the singleton thing right, the next time we run this context code jFoo will get assigned to the pre-existing instance of mFoo rather than a new one. So, the class containing jFoo may be GC'd, but really jFoo isn't because it is really just a pointer to the same mFoo object that is used in both contexts.

Which said, I took Peter's code without thinking about it much, both because I assume that you folks have you sleeves rolled up actually using this stuff while I haven't written a line of ABL in weeks. I suppose it seemed like sensible code since the idea is similar to my old pseudo-singleton trick from before statics. But if we go to the manual ... with all due caution about manual examples, we find:

No if valid test involved.

Posted by jmls on 29-Jan-2009 01:19

and if you run that code, it doesn't work

a) you can't use a = new singletonprop()

b) if you use the singletonprop.instance it works the first time, but subsequent runs give you the invalid object error

Posted by rbf on 29-Jan-2009 06:30

Just to avoid confusion: Julian, how are you invoking your singleton?

Posted by jmls on 29-Jan-2009 06:36

def var a as classfoo.

a = foo:Instance.

message a:myprop view-as alert-box.

run this from the editor

Posted by Admin on 29-Jan-2009 06:40

Ok. If the editor cleans the instance of the class after the first run, but the static "object" remains in memory (which I know it does) and you instantiate the instance in the static constructor, that observation makes sense.

Posted by rbf on 29-Jan-2009 09:51

So now I am utterly confused. Does this mean that singletons are no longer possible because we now have Garbage Collection?

Posted by Admin on 29-Jan-2009 09:55

No - no - no! It's not a problem with GC - it's the Editor that after a run probably does delete all object instances. (From oObjectToDelete = SESSION:FIRST-OBJECT while VALID-OBJECT oObjectToDelete:NEXT-SIBLING ... DELETE oObjectToDelete)

The GC would not delete the "singleton", because the static property keeps a reference to the one and only instance as long as the session lives.

Posted by jmls on 29-Jan-2009 10:11

No - no - no! It's not a problem with GC - it's the

Editor that after a run probably does delete all

object instances. (From oObjectToDelete =

SESSION:FIRST-OBJECT while VALID-OBJECT

oObjectToDelete:NEXT-SIBLING ... DELETE

oObjectToDelete)

This was not the case with 10.1A.

The GC would not delete the "singleton", because the

static property keeps a reference to the one and only

instance as long as the session lives.

True

Posted by Admin on 29-Jan-2009 10:25

It's implemented in src/adecomm/_runcode.p

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/_5F00_runcode.p:550:0]

Procedure BuildClass and DeleteClass. So the procedure editor builds a list of running object instances before the run and kill all the have been created during the run after the run...

According to the available procedure editor source code, this has already been implemented in 10.1A (the file has not changed since then).

Posted by Thomas Mercer-Hursh on 29-Jan-2009 11:00

So, this is behavior that is unique to OEA and would not occur in a separate application?

Posted by Simon de Kraa on 29-Jan-2009 11:06

Yes, but if you iterate a couple of times in a single procedure editor run you can see that the object is new'd only once so what is the problem?

Posted by Peter Judge on 29-Jan-2009 12:11

So, this is behavior that is unique to OEA and would

It's the Procedure Editor's underlying code that does this. So OE Studio has it too.

not occur in a separate application?

Yep.

-- peter

Posted by Thomas Mercer-Hursh on 29-Jan-2009 12:18

Is there any approach which would allow testing of this type of code from within OEA/OES?

Posted by Peter Judge on 29-Jan-2009 12:26

Is there any approach which would allow testing of

this type of code from within OEA/OES?

Well, in OEA a Run Config forks an entirely new session (and so it doesn't really matter whether _runcode is used or not; the cleanup will executes when the session terminates , and it would be cleaned up anyway). It's possible to re-use the project's AVM, but I prefer to spawn the new session.

In OES, you'd have to run your app's start.p from ProTools, which, TTBOMK, doesn't use runcode.p. But you're still in the same AVM, and that makes life harder for testing stuff with statics (since they only die when the session goes away). It may not matter when dealing with singletons, since you're dealing with an instance, but it's good to keep in mind.

-- peter

Posted by Admin on 29-Jan-2009 12:31

Run a .p like this:

DO i = 1 TO 10:

RUN thedotpthataccessesthesingleton.p

END.

This thread is closed