Introduction
GUTS (Global UnThreaded
object Store) is a low-level class which can be used by
other classes. By itself it does nothing
and adds no value to a program.
However it allows other objects to communicate with each other, across
threads, even when the object is not the topmost window on the thread.
This is a required layer for some other CapeSoft Accessories, and is
provided free of charge.
ClarionLive
An introduction to this class, and the way in which it works, was
described in
ClarionLive
session #234 , recorded on 1 November 2013.
During this webinar a small example class, LeftRight was created. The
LeftRight class is included in the GUTS install. The LeftRight class is
not documented here, for more discussion on this class see the webinar.
Installation
Run the supplied installation file:
Jump Start
Add the global extension to your app file.
Support
Your questions, comments and suggestions are welcome.
Check our web page (
www.capesoft.com)
for new versions. You can also contact us in one of the following ways:
CapeSoft Support |
Support Page |
Find support page with various options here |
Email |
|
Telephone |
+27 87 828 0123 |
Fax |
+27 21 715 2535 |
Post |
PO Box 511, Plumstead, 7801, South Africa |
Distribution
No additional files are required for shipping.
Basic Description
A single GUTS object, called ThisGuts is
added to the application. This object is unthreaded, and thus is available
to other objects on all threads.
The object maintains a queue of Interfaces. Other objects can add or
remove themselves to this queue. Objects can Post an Event to other
objects in the queue. Events are just a number, and any number can be
used. It is envisaged that a number of "common events" may be added to
GUTS at some point in the future, so it is recommended that custom events
are equal to Guts:User or more.
Each object in the queue has a family name, the number of the thread it is
on and an ID. Messages can be posted to all objects in a family, all
objects
on a thread, or a single object (if you know its ID).
Classes implement the Interface, and when an event is posted into ThisGuts
it is passed on to the class via this interface. What the class
does with the event is entirely up to the class.
How to use GUTS in a class
Implement the Interface in the
Class
- In INC file: Add Include('Guts.inc'),Once
near the top of the file.
- In INC file: Add ,IMPLEMENTS(GutsInterface)
to the Class declaration
- In INC file: Add two new properties;
Guts &Guts
GutsId Long
- In CLW File: Add classname.GutsInterface.TakeEvent
method. For Example;
something.GutsInterface.TakeEvent
PROCEDURE(Long pEvent, <Long Param1>, <Long Param2>,
<Long Param3>, <Long Param4>, <String Param5>,
<String Param6>, <String Param7>, <String
Param8>)
CODE
case pEvent
of Guts:User+1 Self.Refresh() End
- In CLW File: In DESTRUCT method add
If not self.Guts &= Null and Self.GutsId
<> 0
self.Guts.Delete(self.GutsId)
End
- In CLW File: Change the INIT method
declaration, adding the Guts parameter
(Guts pGuts)
- In the CLW file: In the INIT method
set the new properties as follows;
self.Guts &= pGuts
self.GutsId = self.Guts.Add('familyName',self.GutsInterface)
The first parameter in the call above is the "familyname" for the
object. The Post (as described below) will usually limit events to
objects in the same family. The family name can be anything you
like, although name clashes with other classes are undesirable.
Post an Event
The PostEvent method of the GUTS Class takes a
number of parameters. The first parameter is the event number. The next
3 allow the scope of the Post to be restricted, and the following 8 are
optional parameters that are passed to the interface. The method returns
the number of interfaces which received the event.
The parameters to this method are as follows;
Parameter |
Description |
Event Long |
The event number that is passed to the interfaces. Any number
can be used, but for future compatibility use numbers equal to
or more than Guts:User |
Family String(20) |
Limit the Post to all objects of this family |
Thread Long (optional) |
Limit the Post to all objects on this thread. (This can be
combined with the previous parameter to limit to objects of a
specific family, on a specific thread) |
ID Long (optional) |
Limit the post only to the object with this ID. If this
parameter is set then the previous two parameters are ignored. |
Param1..Param4 Long (optional) |
Four optional parameters of type LONG which will be passed
unaltered to the Interfaces. |
Param5..Param8 String (optional) |
Four optional parameters of type STRING which will be passed
unaltered to the Interfaces. |
The method can be called from wherever you like in your code. Here are
some possible calls;
ThisGuts.PostEvent(Guts:User+1,'familyname')
ThisGuts.PostEvent(Guts:User+1,'familyname',thread())
ThisGuts.PostEvent(Guts:EventRefresh,'') ThisGuts.PostEvent(Guts:User+1,'',0,x) ThisGuts.PostEvent(Guts:User+1,'familyname',0,0,x,y,z)
Care with Pointers
An object which hooks into GUTS in the above way has
one characteristic which is different from a normal class, and needs to
be allowed for.
By passing a pointer to its interface to GUTS, you are implicitly
implying that the object can be accessed from thread 1. In other words,
your object code can run on thread 1 even though your object is
instantiated on thread 2.
This is not a problem, unless your object in turn contains a pointer to
a global THREADED variable, like a file handle. There may be cases where
this access of "thread 2" memory, from thread 1 may(*) cause a problem.
Fortunately there is a solution.
Ideally your class should store two pointers when dealing with a global,
threaded, variables. The "INSTANCE 0" of that variable, and the "current
thread" INSTANCE for general use. The following example shows this in
action.
FirstRecord Class()
MyFile0 &file
MyFile &file
Init Procedure(File pFile)
Get Procedure()
end
FirstRecord.Init Procedure(File pFile)
Code
self.Myfile0 &= INSTANCE(pFile,0)
FirstRecord.Get Procedure()
Code
self.MyFile &= INSTANCE(self.Myfile0,Thread())
Get(self.MyFile,1)
In the above code the class is making sure the "Instance 0" pointer is
stored by the Init method, and then the "Current Instance" is set before
the pointer is used.
(*) In our experience of using GUTS so far, pointers to memory fields
are not a problem in this context (outside of normal memory
syncronisation). The problem is when you are pointing to data structures
(like Files) which are then used by code (File Drivers) which do not
support this context switch.
Getting a GPF?
The most likely reason for a GPF when POSTing an Event is if an object has
added itself to the Interface Queue, but
not removed itself from the queue when it goes out of scope.
In other words the line of code
self.Guts.Delete(self.GutsId)
has not been added to the DESTRUCT method of the class.
A second common reason for a GPF when POSTing an event is if the object
contains File handling code, and that code does not take sufficient care
over the thread change. See the section
Care with
Pointers for more information on this.
Examples
There are four examples included with the Install. They show how to apply
the template an ABC or Legacy app, as a simple
Exe or in a Multi-DLL setup.
The single EXE, ABC demo app has an additional bit of code showing how the
GUTS Object might be called - in this case by a very small class called
LeftRight. An example of posting events is in the Main procedure, and an
instance of the LeftRight class has been added (manually) to each window.
You can see the LeftRight class in action in this example by using the
items in the Tools menu.
This example, with LeftRight added completely by hand is a good example of
adding any class to an app by hand. there are a few things to take note
of;
- Global Embeds, After Global Includes, is the line
include('leftright.inc'),Once
This makes the class visible to the application.
- Project Defines include;
LeftRightLinkMode=>1
LeftRightDLLMode=>0
This is important so that the application does not GPF when it
encounters the class. these settings will change in a multi-dll
situation, if in doubt set the values the same as the GUTSLinkMode and
the GUTSDllMode.
- In a procedure an instance of the class is created with the line
LR LeftRight
LR is the name of the Object. LeftRight
is the name of the class.
- After the window is opened the LR Object is initialized with
LR.Init(ThisGuts)
A Post is set to all the objects from the MAIN procedure, and looks
something like this;
ThisGuts.PostEvent(guts:user,'lefttoright',0,0,1)
Template Reference
Global Extension
This template adds the Global ThisGuts object to the
application.
General Tab
Disable All GUTS Features
If this is ticked on the object will not be generated.
Multi-DLL Tab
This is part of a Multi-DLL program
Tick this on if this app is part of a multiple-app system. This is
turned on for all the DLL's and the EXE's.
Export Class from this DLL
Tick this on in the app which will export the class. This is almost
always the Data DLL app. In all other apps, DLLs and EXE's, leave this
off.
Classes Tab
Class Version
The last date when the class was parsed from the LibSrc folder. Used
internally. Do not change.
Object Name
The name of the object to create, usually
ThisGuts.
Class Name
The name of the class to use, usually
Guts.
Object Reference
Purpose
The Guts class maintains a Queue of interfaces.
Other objects can add or remove themselves from this queue. A method is
provided so that notifications can be sent to all the interfaces in the
queue.
GUTS Interface
TakeEvent
TakeEvent(Long
pEvent, <Long Param1>, <Long Param2>, <Long
Param3>, <Long Param4>, <String Param5>, <String
Param6>, <String Param7>, <String Param8>)
Description
Receives an event posted by the Guts class. How an object processes
the event is completely up to the class implementing the interface.
Note that this isn't a Clarion or Windows event, but rather just a
notification to the interface.
Parameters
Parameter |
Description |
pEvent |
An event number that has meaning to the receiving object. |
Param1...Param4 |
Four optional LONG parameters containing additional
information for the object. |
Param5...Param8 |
Four optional STRING parameters containing additional
information for the object. |
Return Value
none
Example
Leftright.GutsInterface.TakeEvent Procedure(Long
pEvent, <Long Param1>, <Long Param2>, <Long
Param3>, <Long Param4>, <String Param5>, <String
Param6>, <String Param7>, <String Param8>)
CODE
case pEvent
of Guts:User
Self.SetDirection(Param1)
End
Properties
Property |
Description |
iQueue &GutsQueueType |
A queue of interfaces. |
LastId Long |
As each item is added to the queue it is given a unique id.
This property contains the number of the last id issued. |
cs &ICriticalSection |
A critical section to manage access to the global, unthreaded
queue safely. |
Methods
Add
Add(String
pFamily, GutsInterface pInterface)
Description
Adds an interface to the queue. Generally called by a class which
implements the interface when it wishes to add itself to the global
queue.
Parameters
Parameter |
Description |
pFamily |
A short string containing a unique family name for the class
which is registering itself. Events can be posted by family,
so this allows an object to post events only to objects of the
same family. |
pInterface |
The GutsInterface as implemented in the calling class. |
Return Value
Long. The unique ID of the object in the Guts Queue is returned. The
calling object should store this, and use it as the parameter to the
Delete method when the calling object goes out of scope.
Example
Leftright.Init Procedure(Guts pGuts)
CODE
If not pGuts &= Null
self.Guts &= pGuts
self.GutsId = self.Guts.Add(family:leftright,self.GutsInterface)
End
See Also
Delete
Construct
Construct()
Description
Automatically called when the Guts object comes into scope. Does some
basic initialization of the object.
Parameters
None
Return Value
None
Delete
Delete(long pId)
Description
Allows an object to delete itself from the Interface queue. It's very
important that an object does this when it goes out of scope, as
leaving it in the queue, once the object has gone out of scope, will
lead to a GPF the next time an event is posted.
Parameters
Parameter |
Description |
pID |
The Unique ID of the object to delete. The unique ID was
returned to the object when the Add method was called. |
Return Value
None
Example
Leftright.Destruct Procedure()
CODE
If not self.guts &= Null
self.Guts.Delete(self.GutsId)
End
See Also
Add
Destruct
Destruct()
Description
Automatically called when the Guts object goes out of scope. Does some
basic clean up of the object.
Parameters
None
Return Value
None
PostEvent
PostEvent(long
pEvent, String pFamily, Long pThread=0,Long pId=0, <Long
Param1>, <Long Param2>, <Long Param3>, <Long
Param4>, <String Param5>, <String Param6>, <String
Param7>, <String Param8>)
Description
Used to post a notification to one or more items in the interface
queue.
The items being posted to can be identified by ID, or alternatively by
a combination of Family and Thread. If the Family is set then only
objects of this family will receive the event. Likewise if the Thread
is set then only objects on that thread will receive the event. If
both Family and Thread are set then only objects of that family, on
that one specific thread, will receive the event.
Parameters
Parameter |
Description |
pEvent |
The number of the event to send to the objects. Note this is
not a Clarion EVENT - you can use any number you like. Numbers
>= Guts:User are recommended for future-compatibility. |
pFamily |
If this parameter is set, then only objects in this family
will receive the event. An object sets its family when calling
the Add method. |
pThread |
If this parameter is 0 then the event is sent to all the
threads. If it contains a thread number, then the event will
only be sent to that specific thread. |
pId |
If this parameter is set then only this object will receive
the event, regardless of the Thread or Family parameters. |
Param1...Param8 |
Additional optional parameters that will be passed on to the
receiving object. |
Return Value
The number of objects that received the event is returned.
Example
ThisGuts.PostEvent(guts:user,family:leftright,0,0,1)
See Also
Post An Event
Release
Release(Long
pId)
Description
Releases the CriticalSection. Calls to Release MUST be paired with a
call to the
WAIT method. Each WAIT requires 1,
and only 1, RELEASE. Failure to balance WAIT and RELEASE calls will
cause bugs in your program that are extremely difficult to find.
This method is used inside the class itself, and is not normally
called from outside the class.
Parameters
Parameter |
Description |
pId |
Used for debugging purposes. If each call to Wait and
Release has a unique pId, then it's easier to detect
mismatched Wait/Release pairs. |
Return Value
None
Example
self.Release(3)
See Also
Wait
Trace
Trace(string
pStr)
Description
Sends a string to the Windows Debug Output, where it can be monitored
using
Debugview. Useful when debugging the class.
Parameters
Parameter |
Description |
pStr |
The String to send to Debugview. The string will be
prepended with the identifier [guts]
|
Return Value
None
Example
Self.Trace('Hello World')
Wait
Wait(Long pId)
Description
Waits the CriticalSection. Calls to Wait MUST be paired with a call to
the RELEASE method. Each WAIT requires 1, and only 1, RELEASE. Failure
to balance WAIT and RELEASE calls will cause bugs in your program that
are extremely difficult to find.
This method is used inside the class itself, and is not normally
called from outside the class.
Parameters
Parameter |
Description |
pId |
Used for debugging purposes. If each call to Wait and
Release has a unique pId, then it's easier to detect
mismatched Wait/Release pairs. |
Return Value
None
Example
self.Wait(3)
See Also
Release
Version History
1.10 - 24 May 2021
- Add: Clarion 11.1 to install.
1.09 - 18 September 2018
- Add: Clarion 11 to install.
1.08 - 1 December 2015
- Fix: An endless loop in PostEvent was possible if both Family and
Thread were passed.
- Fix: PostEvent did not apply the Thread parameter if the Family
parameter was blank.
1.07 - 16 April 2015
- Changed name of global template variables %ObjectName and %ClassName
to avoid clashes with other tools.
1.06 - 25 February 2015
1.05 - 30 July 2014
- Updated with Cape templates version 4.07 (no material change, just
doc in cape01.tpw updated.)
1.04 - 21 January 2014
1.03 - 13 January 2014
- Updated template to Cape 4.06 level.
1.02 - 6 January 2014
- Updated template to Cape 4.05 level. Should make some Legacy
generation problems go away.
1.01 - 4 November 2013
- Various tweaks to the LeftRight class as discussed during the
webinar
- Refactor of PostEvent method so that calls
to the TakeEvent method are outside the
CriticalSection (as discussed in the webinar)
- Template Reference added to the
docs.
- Object Reference added to docs.
1.00 - 1 November 2013