Use pattern for multiple object instances with dedicated resources?
ags
Posts: 386
I am wondering if I'm using an anti-pattern for use of multiple instances of an object that relies on resources exclusive to the object. In other words, if I have a top object ("TOP") that has two child objects ("CHILD1" and "CHILD2"), and both of the child objects each want access to a third object ("SHARE") - how is that done? Objects referenced in one object are not visible in that object's child objects - the child objects must each include the common object to access it. For an object that has only PUB|PRI methods, and VAR symbols, this works fine because there is no shared resource for conflict: the PUB|PRI methods have their own stack space so arguments and locals don't collide. In the case of an object with DAT symbols, this can be used to ensure a consistent state between multiple object instances, but there could be conflict between the object instances.
Specific example: ParallaxSerialTerminal (PST). How does one access functionality provided in PST from two distinct child objects of a parent object? For most applications, PST will require two pins (rx/tx) which cannot be shared - they are mutually exclusive resources. So unless I want to really have two PST instances (each communicating on separate rx/tx pin pairs) I need to have just one PST object in the application. However, I need to include PST in both child objects. Since PST requires a dedicated cog, it requires initialization. So I must only initialize one instance of the PST object. But PST uses VAR symbols, which are unique to each object instance, so only the PST instance that was started will operate.
So is the accepted practice to modify PST so all VAR symbols are moved to DAT symbols (shared across instances), include as many PST object instances as necessary where that functionality will be needed (including the TOP object), and initialize only the PST object instance in the TOP object?
Of course it gets even messier if the child objects that each include a PST object instance are running in separate cogs. Now there is a chance for collisions when calling the PST methods from different SPIN interpreters running independently in different cogs, but both competing for access to the exclusive resources of the PST object instance in the application TOP object (rx/tx pins and DAT symbols).
Am I understanding this correct? Is this the way it is done? Is there a singleton version of PST available?
Specific example: ParallaxSerialTerminal (PST). How does one access functionality provided in PST from two distinct child objects of a parent object? For most applications, PST will require two pins (rx/tx) which cannot be shared - they are mutually exclusive resources. So unless I want to really have two PST instances (each communicating on separate rx/tx pin pairs) I need to have just one PST object in the application. However, I need to include PST in both child objects. Since PST requires a dedicated cog, it requires initialization. So I must only initialize one instance of the PST object. But PST uses VAR symbols, which are unique to each object instance, so only the PST instance that was started will operate.
So is the accepted practice to modify PST so all VAR symbols are moved to DAT symbols (shared across instances), include as many PST object instances as necessary where that functionality will be needed (including the TOP object), and initialize only the PST object instance in the TOP object?
Of course it gets even messier if the child objects that each include a PST object instance are running in separate cogs. Now there is a chance for collisions when calling the PST methods from different SPIN interpreters running independently in different cogs, but both competing for access to the exclusive resources of the PST object instance in the application TOP object (rx/tx pins and DAT symbols).
Am I understanding this correct? Is this the way it is done? Is there a singleton version of PST available?
Comments
I generally try to keep all debug statements in the top object. I often modify child objects to return the location of variables I may want displayed so the top object can take care displaying these variables.
Not only do I try to limit the debug statements to the top object but I also try to keep all the debug statements in one cog.
There are multiple ways of using debug statements in more than one cog and object, but they are kind of messy IMO.
I have a version of FullDuplexSerial I use where I moved the variables from the VAR section to the DAT section.
I also added this method so I knew which cog was sending the information.
(I would start all debug string with a carriage return so the above method would show the cog's ID number.)
You need to make sure two cogs aren't trying to send debug information at the same time. Locks are very useful for this purpose.
I've occasionally resorted to moving all child objects to the top object and just had one really big file. By using the "Summary" view option in the Prop Tool, the very large object wasn't too bad to work with.
It sounds like you've got the general idea of how the VAR and DAT sections work. One thing to keep in mind is if you want to use two copies of an object and not just two instances, you need to make a change in the file. Just renaming the object isn't enough to keep the Prop Tool from using only one copy.
Are you saying that there must be some difference in the contents of the file or that just the name of the file must be different?
Collision, in the case of PST, means simply that the bytes sent or received from different cogs could become intermingled, mixed up, nothing more serious. Both the spin code and the pasm code interact but putting bytes into a buffer at the head or taking them out at the tail.
The thread introducing SerialMirror might be helpful. Also, there is MultiCogSerialDebug. Those don't include all the spin methods of PST though.
That is truly surprising. I almost didn't ask the question as the answer seemed obvious (to me). So the SPIN compiler is so smart that it will compare the contents of all files "included" in the OBJ block, and if any are exact duplicates of any other, reduce them to instances of the common definition. That is not at all what I expected.
I tell you, this caused me a lot of head scratching.
I often set one of the variables in the DAT section to a different value than zero is the variable will be initialized elsewhere in code anyway. Just changing a zero to a one is enough to force the compiler to use a second copy.