Global and session objects: Difference between revisions

From m204wiki
Jump to navigation Jump to search
No edit summary
m (misc formatting)
 
(13 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Object %variables are useful for holding references to objects in a request.
Object %variables are useful for holding references to objects in a request.
However, these references “go away” at the end of a request.
However, these references "go away" at the end of a request.
But it
But it can be very useful to have objects that can be used in multiple <var class="product">SOUL</var>
can be very useful to have objects that can be used in multiple <var class="product">User Language</var>
requests, that is, in multiple procedure evaluations.
requests, that is, in multiple procedure evaluations.
For this to be possible, references to objects must be able to
For this to be possible, references to objects must be able to
span requests.
span requests.
   
   
In ''SOUL'', there are two kinds of objects that span requests:
In <var class="product">SOUL</var>, there are two kinds of objects that span requests: '''global objects''' and '''session objects'''.
'''global objects''' and '''session objects'''.
Like object variables, global and session objects are not really objects but
Like object variables, global and session objects are not really objects but
references to the underlying objects.
references to the underlying objects.
   
   
A global or session object is referenced by its name (any 1- to 255-byte string),
A global or session object is referenced by its name (any 1- to 255-byte string), which is kept in a table in CCATEMP that is not cleaned up at
which is kept in a table in CCATEMP that is not cleaned up at
the end of request.
the end of request.
Since a global/session name allows an underlying object to be referenced
Since a global/session name allows an underlying object to be referenced
Line 39: Line 36:
The global and session namespaces are completely separate:
The global and session namespaces are completely separate:
it is perfectly valid to have a session object reference called
it is perfectly valid to have a session object reference called
&ldquo;HAL&rdquo; and a global object reference with the same name.
"HAL" and a global object reference with the same name.
These references could be to the same underlying object or, more likely,
These references could be to the same underlying object or, more likely,
could be references to two totally separate objects, even in different classes.
could be references to two totally separate objects, even in different classes.
Line 49: Line 46:
You can access an object through its global/session name by:
You can access an object through its global/session name by:
<ul>
<ul>
<li>A static compile-time binding of the name to a variable
<li>A static compile-time binding of the name to a variable </li>
<li>A dynamic run-time request for a global/session name
<li>A dynamic run-time request for a global/session name </li>
</ul>
</ul>
==Binding global/session names to a variable==
==Binding global/session names to a variable==
The simplest way to use a global name is to bind a variable to it:
The simplest way to use a global name is to bind a variable to it:
Line 62: Line 60:
the variable <code>%larry</code> is to be bound to the session
the variable <code>%larry</code> is to be bound to the session
name <code>LARRY</code>.
name <code>LARRY</code>.
This means that, on the first reference to %moe in the request,
This means that, on the first reference to <code>%moe</code> in the request,
the global name MOE is looked up in the global object
the global name <code>MOE</code> is looked up in the global object
table and, if present, assigned to %moe.
table and, if present, assigned to <code>%moe</code>.
If the name MOE is not found in the global object table,
If the name <code>MOE</code> is not found in the global object table,
%moe is left as null, its initial value.
<code>%moe</code> is left as null, its initial value.
After that first reference, any changes to %moe are reflected
After that first reference, any changes to <code>%moe</code> are reflected
in the global object.
in the global object.
Similar processing occurs for variable %larry with the differences that
 
Similar processing occurs for variable <code>%larry</code>, with the differences that
lookup for the session name is done in the session object table, and if
lookup for the session name is done in the session object table, and if
no session is open, the first reference is a request cancelling error.
no session is open, the first reference is a request cancelling error.
Line 119: Line 118:
The <var>Global</var> and <var>Session</var>
The <var>Global</var> and <var>Session</var>
qualifiers are allowed on object %variable declarations,
qualifiers are allowed on object %variable declarations,
whether for objects, collections, enumerations, <var class="product">User Language</var> classes,
whether for objects, collections, enumerations, user-defined classes,
or system classes.
or system classes.
Any attempt to access the same global/session name in different requests
Any attempt to access the same global/session name in different requests
Line 127: Line 126:
occurs on the first reference to the %variable.
occurs on the first reference to the %variable.
   
   
Two <var class="product">User Language</var> classes in two requests are considered the same if the classes
Two user-defined classes in two requests are considered the same if the classes
have the same name and all
have the same name and all
of the <var>Public</var> and <var>Private</var> non-shared variables have the same types
of the <var>Public</var> and <var>Private</var> non-shared variables have the same types
Line 136: Line 135:
<ul>
<ul>
<li>Anything about methods.
<li>Anything about methods.
This includes their absence or presence, parameters, and code.
This includes their absence or presence, parameters, and code. </li>
<li>Public and private shared variables.
<li>Public and private shared variables. </li>
<li>The names of any variables.
<li>The names of any variables. </li>
</ul>
</ul>
   
   
Line 215: Line 214:
are accessed in all those requests via a global or session name.
are accessed in all those requests via a global or session name.
   
   
A file or group object (see [[File classes|"File classes"]]) associated with a global/session
A file or group object (see [[File classes]]) associated with a global/session name will be implicitly discarded if the file or group is closed between requests.
name will be implicitly discarded if the file or group is closed between
 
requests.
==Using system class methods to access global and session objects==
==Using Object class methods to access global and session objects==
 
===<b id="globObjMeths"></b>Using the Object class===
In addition to binding to a variable, the other way to access global/session names
In addition to binding to a variable, the other way to access global/session names
is to use shared methods in the <var>Object</var> class.
is to use shared methods in the <var>[[Object class|Object]]</var> class.
The <var>Object</var> class can be thought of us the base class for all objects.
The <var>Object</var> class can be thought of us the base class for all objects.
It is possible to define an object in the <var>Object</var> class:
It is possible to define an object in the <var>Object</var> class:
Line 234: Line 234:
   
   
The following shared methods are available for the <var>Object</var> class:
The following shared methods are available for the <var>Object</var> class:
<table>
 
<tr><th><var>DiscardGlobal</var>
<div class="showVisit">
</th><td><p class="code">%(Object):DiscardGlobal(<i>name</i>)</p>
<table class="wikitable">
<tr class="head"><th>Method</th><th>Description</th></tr>
Discards the object referenced by <var class="term">name</var>. If the indicated global name does not reference an object, this method does nothing.
<tr><td valign="top">[[DiscardGlobal (Object subroutine)|DiscardGlobal]]</td><td valign="top">Discard a global object</td></tr>
</td></tr>
 
<tr><th><var>DiscardGlobals</var>
<tr><td valign="top">[[DiscardGlobals (Object subroutine)|DiscardGlobals]]</td><td valign="top">Discard global objects</td></tr>
</th><td><p class="code">%(Object):DiscardGlobals(<i>wildcard</i>)</p>
 
<tr><td valign="top">[[DiscardSession (Object subroutine)|DiscardSession]]</td><td valign="top">Discard a session object</td></tr>
Discards the objects referenced by the name that matches the specified <var class="term">wildcard</var> string. If no global name references an object, this method does nothing.
 
If the <var class="term">wildcard</var> string is <code>*</code>, all objects referenced by global names are discarded.
<tr><td valign="top">[[DiscardSessions (Object subroutine)|DiscardSessions]]</td><td valign="top">Discard session objects</td></tr>
</td></tr>
 
<tr><th><var>DiscardSession</var>
<tr><td valign="top">[[GetGlobal (Object subroutine)|GetGlobal]]</td><td valign="top">Retrieve global object reference</td></tr>
</th><td><p class="code">%(Object):DiscardSession(<i>name</i>)</p>
 
<tr><td valign="top">[[GetSession (Object subroutine)|GetSession]]</td><td valign="top">Retrieve session object reference</td></tr>
Discards the object referenced by <var class="term">name</var>. If the indicated session name does not reference an object, this method does nothing.
 
<tr><td valign="top">[[GlobalList (Object function)|GlobalList]]</td><td valign="top">Get list of global objects</td></tr>
If no session is open, the request is cancelled.
 
</td></tr>
<tr><td valign="top">[[NullifyGlobal (Object subroutine)|NullifyGlobal]]</td><td valign="top">Clear global object reference</td></tr>
<tr><th><var>DiscardSessions</var>
 
</th><td><p class="code">%(Object):DiscardSessions(<i>wildcard</i>)</p>
<tr><td valign="top">[[NullifyGlobals (Object subroutine)|NullifyGlobals]]</td><td valign="top">Clear global object references</td></tr>
 
Discards the objects referenced by the name that matches the specified <var class="term">wildcard</var> string. If no session name references an object, this method does nothing.
<tr><td valign="top">[[NullifySession (Object subroutine)|NullifySession]]</td><td valign="top">Clear session object reference</td></tr>
If the <var class="term">wildcard</var> string is <code>*</code>, all objects referenced by session names are discarded.
 
<tr><td valign="top">[[NullifySessions (Object subroutine)|NullifySessions]]</td><td valign="top">Clear global object references</td></tr>
If no session is open, the request is cancelled.
 
</td></tr>
<tr><td valign="top">[[SessionList (Object function)|SessionList]]</td><td valign="top">Get list of session objects</td></tr>
<tr><th><var>GarbageCollect</var>
 
</th><td><p class="code">%(Object):GarbageCollect</p>
<tr><td valign="top">[[SetGlobal (Object subroutine)|SetGlobal]]</td><td valign="top">Set global object reference</td></tr>
 
Performs garbage collection, that is, cleans up &ldquo;unreachable&rdquo; objects. For more information about garbage collection, see [[#Managing object storage|"Managing object storage"]].
<tr><td valign="top">[[SetSession (Object subroutine)|SetSession]]</td><td valign="top">Set session object reference</td></tr>
</td></tr>
<tr><th><var>GetGlobal</var>
</th><td><p class="code">%(Object):GetGlobal(<i>name</i>, <i>%variable</i>)</p>
Sets the variable indicated by <var class="term">%variable</var> to reference the object referenced by the global name <var class="term">name</var>.
If the indicated global name does not reference an object, this method sets the indicated variable to null. <var class="term">%variable</var> cannot be an object member.
</td></tr>
<tr><th><var>GetSession</var>
</th><td><p class="code">%(Object):GetSession(<i>name</i>, <i>%variable</i>)</p>
Sets the variable indicated by <var class="term">%variable</var> to reference the object referenced by the session name <var class="term">name</var>.
If the indicated session name does not reference an object, this method sets the indicated variable to null. <var class="term">%variable</var> cannot be an object member.
If no session is open, the request is cancelled.
</td></tr>
<tr><th><var>[[GlobalList (PersistentObjectInfo function)|GlobalList]]</var>
</th><td><p class="code"><i>%persObjList</i> = %(Object):GlobalList(<i>matchString</i>)</p>
Returns to an <var>Arraylist of Object PersistentObjectInfo</var> information about all the global objects whose names match a specified pattern string.
</td></tr>
<tr><th><var>NullifyGlobal</var>
</th><td><p class="code">%(Object):NullifyGlobal(<i>name</i>)</p>
Sets the object reference named <var class="term">name</var> to null, that is, referencing nothing. If the specified global name does not reference an object, this method does nothing.
</td></tr>
<tr><th><var>NullifyGlobals</var>
</th><td><p class="code">%(Object):NullifyGlobals(<i>wildcard</i>)</p>
Sets object references with names matching the specified <var class="term">wildcard</var> string to null. If no global name references an object, this method does nothing.
If the wildcard string is <code>*</code>, all global references are set to null.
</td></tr>
<tr><th><var>NullifySession</var>
</th><td><p class="code">%(Object):NullifySession(<i>name</i>)</p>
Sets the object reference named <var class="term">name</var> to null, that is, referencing nothing. If the specified session name does not reference an object, this method does nothing.
If no session is open, the request is cancelled.
</td></tr>
<tr><th><var>NullifySessions</var>
</th><td><p class="code">%(Object):NullifySessions(<i>wildcard</i>)</p>
Sets object references with names matching the specified <var class="term">wildcard</var> string to null. If no session name references an object, this method does nothing.
If the <var class="term">wildcard</var> string is <code>*</code>, all session references are set to null.
If no session is open, the request is cancelled.
</td></tr>
<tr><th><var>SetGlobal</var>
</th><td><p class="code">%(Object):SetGlobal(<i>name</i>, <i>object</i>)</p>
Sets the global reference named <var class="term">name</var> to the object referenced by <var class="term">object</var>. <var class="term">object</var> can be a %variable, a structure member, or a class member inside an object.
</td></tr>
<tr><th><div id="setsession"></div><var>SetSession</var>
</th><td><p class="code">%(Object):SetSession(<i>name</i>, <i>object</i>)</p>
Sets the session reference named <var class="term">name</var> to the object referenced by <var class="term">object</var>. <var class="term">object</var> can be a %variable, a structure member, or a class member inside an object.
</td></tr>
<tr><th><var>[[SessionList (PersistentObjectInfo function)|SessionList]]</var>
</th><td><p class="code"><i>%persObjList</i> = %(Object):SessionList(<i>matchString</i>)</p>
Returns to an <var>Arraylist of Object PersistentObjectInfo</var> information about all the session objects whose names match a specified pattern string.
</td></tr>
</table>
</table>
</div>
   
   
The following code illustrates how two globals are set:
The following code illustrates how globals are set:
<p class="code">%moe    is class stooge
<p class="code">%moe    is class stooge
%larry  is class stooge
%larry  is class stooge
Line 378: Line 318:
%(object):setGlobal('Vino', null)
%(object):setGlobal('Vino', null)
</p>
</p>
===Using the PersistentObjectInfo class===
The <var>[[PersistentObjectInfo class|PersistentObjectInfo]]</var> class contains information about global or session objects in the current thread. <var>PersistentObjectInfo</var>
objects offer the advantage of the sorting, finding, and subsetting facilities of
collections.


==Managing object storage==
==Managing object storage==
Line 398: Line 343:
This typically involves provision of a method to explicitly discard
This typically involves provision of a method to explicitly discard
an object when it is no longer needed.
an object when it is no longer needed.
The [[Janus SOAP User Language Interface|Janus SOAP ULI]] is no exception to this: it provides a <var>Discard</var> method
<var class="product">SOUL</var> is no exception to this: it provides a <var>Discard</var> method
to allow an object to be discarded when it is no longer needed.
to allow an object to be discarded when it is no longer needed.
===Cleanup based on object reference count===
===Cleanup based on object reference count===
In the reference count model, a count of references to an object
In the reference count model, a count of references to an object
is maintained, and when that count goes to zero, the object is discarded.
is maintained, and when that count goes to zero, the object is discarded.
This model works well because objects are generally discarded as
This model works well because objects are generally discarded as
soon as they are &ldquo;unreachable.&rdquo;
soon as they are "unreachable."
That is, if there are no object variables that directly or indirectly
That is, if there are no object variables that directly or indirectly
reference an object, the object is essentially unusable and so can be
reference an object, the object is essentially unusable and so can be
Line 411: Line 357:
By discarding objects as soon as they are unreachable, the reference
By discarding objects as soon as they are unreachable, the reference
count model tends to facilitate applications that run in a small
count model tends to facilitate applications that run in a small
&ldquo;footprint,&rdquo; that is, use minimal storage.
"footprint," that is, use minimal storage.
In addition, this model tends to ensure smooth response and to minimize
In addition, this model tends to ensure smooth response and to minimize
the &ldquo;burps&rdquo; and excessive CPU overhead associated with garbage
the "burps" and excessive CPU overhead associated with garbage
collection.
collection.
For all these reasons, the <var class="product">Janus SOAP ULI</var> implements the reference count model
For all these reasons, <var class="product">SOUL</var> implements the reference count model
of object management.
of object management.
   
   
Line 444: Line 390:
set to reference the new instance.
set to reference the new instance.
   
   
Thus with no effort on the programmer's part, the <var class="product">Janus SOAP ULI</var> ensures
Thus with no effort on the programmer's part, <var class="product">SOUL</var> ensures
that there is never more than one instance of a Stringlist object
that there is never more than one instance of a Stringlist object
in this code.
in this code.
   
   
One drawback of the reference count model is that there might
One drawback of the reference count model is that there might
be a rarely used reference to an object &ldquo;laying around,&rdquo;
be a rarely used reference to an object "laying around,"
and this prevents an object from being discarded when it should.
and this prevents an object from being discarded when it should.
There is not much to be done about this, as there is no way for the
There is not much to be done about this, as there is no way for <var class="product">SOUL</var> to "know" that an application is really done with an object should this occur.
<var class="product">Janus SOAP ULI</var> to &ldquo;know&rdquo; that an application is really done with
an object should this occur.
Fortunately, it is rare that a reference will lay around long without
Fortunately, it is rare that a reference will lay around long without
being assigned to something else, eliminating the last reference to
being assigned to something else, eliminating the last reference to
Line 522: Line 466:
creating a cycle of one.
creating a cycle of one.
   
   
Fortunately, the <var class="product">Janus SOAP ULI</var> cleans up all
Fortunately, <var class="product">SOUL</var> cleans up all
objects, regardless of their reference count at end of request,
objects, regardless of their reference count at end of request,
since presumably all references to the objects have gone away with
since presumably all references to the objects have gone away with
Line 531: Line 475:
object, it increases the reference count by one.
object, it increases the reference count by one.
But, unlike %variable references to objects, a global or session
But, unlike %variable references to objects, a global or session
reference does not &ldquo;go away&rdquo; at end of request.
reference does not "go away" at end of request.
   
   
So, in the previous example, if before setting <code>%a</code> and <code>%b</code> to null,
So, in the previous example, if before setting <code>%a</code> and <code>%b</code> to null,
Line 554: Line 498:
The solution to this problem with the reference count model is
The solution to this problem with the reference count model is
garbage collection.
garbage collection.
===Cleanup by garbage collection===
===Cleanup by garbage collection===
In a pure garbage collection based management approach, references
In a pure garbage collection based management approach, references
Line 564: Line 509:
<li>Going through every static
<li>Going through every static
reference to objects &mdash; variables in most languages; variables and
reference to objects &mdash; variables in most languages; variables and
global and session objects in the <var class="product">Janus SOAP ULI</var>.
global and session objects in <var class="product">SOUL</var>.
<li>Marking as &ldquo;reachable&rdquo; every object
 
<li>Marking as "reachable" every object
accessible via these static references.
accessible via these static references.
<li>Following any references to other objects inside these objects, and
<li>Following any references to other objects inside these objects, and
marking those objects as reachable.
marking those objects as reachable.
<li>Marking as reachable any objects
<li>Marking as reachable any objects
referred to by any object marked reachable,
referred to by any object marked reachable,
until all objects directly or indirectly accessible from
until all objects directly or indirectly accessible from
a static reference are marked reachable.
a static reference are marked reachable.
<li>Scanning all objects, and discarding the unreachable ones.
<li>Scanning all objects, and discarding the unreachable ones.
</ol>
</ol>
Line 580: Line 529:
of objects, so it does not grow any faster than the number of objects.
of objects, so it does not grow any faster than the number of objects.
   
   
Because the <var class="product">Janus SOAP ULI</var> uses the reference count model of object management,
Because <var class="product">SOUL</var> uses the reference count model of object management,
garbage collection is typically unnecessary.
garbage collection is typically unnecessary.
Possible exceptions, however, are requests that create objects with cycles
Possible exceptions, however, are requests that create objects with cycles
Line 589: Line 538:
orphaned cycles.
orphaned cycles.
<li>A thread has one or more global or session references, preventing
<li>A thread has one or more global or session references, preventing
the <var class="product">Janus SOAP ULI</var> from cleaning up all objects at end of request, regardless
<var class="product">SOUL</var> from cleaning up all objects at end of request, regardless
of their reference count.
of their reference count.
</ul>
</ul>
Line 600: Line 549:
<li>Periodically run garbage collection.
<li>Periodically run garbage collection.
</ul>
</ul>
====Explicitly discarding====
====Explicitly discarding====
It is sometimes sufficient to explicitly discard a single
It is sometimes sufficient to explicitly discard a single
object in an about-to-be-orphaned cycle to discard the whole cycle.
object in an about-to-be-orphaned cycle to discard the whole cycle.
If in the [[Cycle example|cycle example]], you specified
If in the [[#Cycle example|cycle example]], you specified
<p class="code">%a:discard
<p class="code">%a:discard
</p>
</p>
Line 616: Line 565:
<p class="code">%a:deepdiscard
<p class="code">%a:deepdiscard
</p>
</p>
====Invoking garbage collection====
====Invoking garbage collection====
If there are no obvious or simple places to clean up
If there are no obvious or simple places to clean up
cycles that are about to be orphaned, the only solution is to do
cycles that are about to be orphaned, the only solution is to do
Line 629: Line 578:
An alternative way to do garbage collection is to implicitly issue the
An alternative way to do garbage collection is to implicitly issue the
the <var>GarbageCollect</var> method.
the <var>GarbageCollect</var> method.
You can set the user parameter <var>[[AUTOGCN parameter|AUTOGCN]]</var> (added in
You can set the user parameter <var>[[AUTOGCN parameter|AUTOGCN]]</var>
<var class="product">Sirius Mods</var> 7.5) to invoke garbage collection automatically based
<!--(added in <var class="product">Sirius Mods</var> 7.5) -->
to invoke garbage collection automatically based
on the count of non-global, non-session objects that have ''not'' been
on the count of non-global, non-session objects that have ''not'' been
discarded by normal object cleanup.
discarded by normal object cleanup.
   
   
Whether explicit or implicit garbage collection,
Whether explicit or implicit garbage collection,
the user parameter <var>[[GCSTATS parameter|GCSTATS]]</var> (also added in <var class="product">Sirius Mods</var> 7.5) can assist you by
the user parameter <var>[[GCSTATS parameter|GCSTATS]]</var>
<!-- (also added in <var class="product">Sirius Mods</var> 7.5) -->
can assist you by
producing a message that contains the results of each garbage collection.
producing a message that contains the results of each garbage collection.
   
   
Line 643: Line 595:
<ul>
<ul>
<li>Garbage collection can be fairly CPU-intensive.
<li>Garbage collection can be fairly CPU-intensive.
<li>Garbage collection is totally unnecessary unless you have
<li>Garbage collection is totally unnecessary unless you have
orphaned cycles.
orphaned cycles.
<li>The benefit of cleaning up orphaned cycles is that resources
<li>The benefit of cleaning up orphaned cycles is that resources
associated with those cycles are cleaned up.
associated with those cycles are cleaned up.
Line 652: Line 606:
How critical it is to quickly release these resources depends,
How critical it is to quickly release these resources depends,
of course, on the resources.
of course, on the resources.
<li>All objects are cleaned up at user logout after any implicit
<li>All objects are cleaned up at user logout after any implicit
session close caused by the logout.
session close caused by the logout.
<li>Garbage collection is performed automatically at session close
<li>Garbage collection is performed automatically at session close
time if there are any session object references.
time if there are any session object references.
Line 660: Line 616:
To better understand this last point, it is worth going into
To better understand this last point, it is worth going into
more detail about session close processing.
more detail about session close processing.
===Session close processing===
===Session close processing===
In most basic terms, when a session is closed, all objects that can
In most basic terms, when a session is closed, all objects that can
Line 718: Line 675:
<li>All objects accessible from session names are marked as
<li>All objects accessible from session names are marked as
'''session-reachable'''.
'''session-reachable'''.
<li>As with garbage collection, references inside session-reachable objects
<li>As with garbage collection, references inside session-reachable objects
are followed, marking any objects referred to inside session-reachable
are followed, marking any objects referred to inside session-reachable
Line 723: Line 681:
This process is continued until all objects that might be reachable
This process is continued until all objects that might be reachable
from a session name have been marked as such.
from a session name have been marked as such.
<li>All objects are scanned, and those that are not session-reachable
<li>All objects are scanned, and those that are not session-reachable
remain in thread-specific structures.
remain in thread-specific structures.
Line 730: Line 689:
   
   
Because this process is relatively expensive and very similar to
Because this process is relatively expensive and very similar to
garbage collection, the <var class="product">Janus SOAP ULI</var> performs garbage collection whenever
garbage collection, <var class="product">SOAP</var> performs garbage collection whenever
it does session close processing, since it is much more efficient to do
it does session close processing, since it is much more efficient to do
both at once rather than to do each individually.
both at once rather than to do each individually.
Line 737: Line 696:
==See also==
==See also==
<ul>
<ul>
<li>[[Sessions]]
<li>[[Sessions]] </li>
<li>[[Classes and Objects]]
<li>[[Classes and Objects]] </li>
<li>[[Object variables]]
<li>[[Object variables]] </li>
<li>[[Copying objects]]
<li>[[Copying objects]] </li>
<li>[[Managing server space for objects]]
<li>[[Managing server space for objects]] </li>
<li>[[Janus SOAP essentials]]
<li>[[Object oriented programming in SOUL]] </li>
<li>The [[#globObjMeths|list of global-related]] methods in the <var>Object</var> class </li>
</ul>
</ul>


[[Category:Janus SOAP ULI topics]]
[[Category:SOUL object-oriented programming topics]]

Latest revision as of 19:32, 11 July 2017

Object %variables are useful for holding references to objects in a request. However, these references "go away" at the end of a request. But it can be very useful to have objects that can be used in multiple SOUL requests, that is, in multiple procedure evaluations. For this to be possible, references to objects must be able to span requests.

In SOUL, there are two kinds of objects that span requests: global objects and session objects. Like object variables, global and session objects are not really objects but references to the underlying objects.

A global or session object is referenced by its name (any 1- to 255-byte string), which is kept in a table in CCATEMP that is not cleaned up at the end of request. Since a global/session name allows an underlying object to be referenced after the end of request, an object referenced by a global/session name is not implicitly discarded at end of request.

Just as multiple object variables can reference an underlying object, multiple global/session names can reference the same underlying object. In fact, an object can be referenced by any number of variables and global/session names at the same time. An object can be referenced by request variables, global references, and session references at the same time.

The distinction between a global object reference and a session object reference is that a global reference is tied to the user's login while a session reference is tied to a session. A session can be opened by a logged in user, closed, and then re-opened, by either the same user or by another user. This opening and closing of a session can continue indefinitely, and it can extend beyond the login of the user that created the session. In fact, this latter possibility is one of the common reasons for the use of sessions — to maintain context over several logins by ephemeral logins such as web server logins.

The global and session namespaces are completely separate: it is perfectly valid to have a session object reference called "HAL" and a global object reference with the same name. These references could be to the same underlying object or, more likely, could be references to two totally separate objects, even in different classes.

While it is possible for a session to span multiple logins, it is also possible for a single login to open and close several sessions, though only one can be open at any given time.

You can access an object through its global/session name by:

  • A static compile-time binding of the name to a variable
  • A dynamic run-time request for a global/session name

Binding global/session names to a variable

The simplest way to use a global name is to bind a variable to it:

%moe is object stooge global('MOE') %larry is object stooge session('LARRY')

These statements indicate that the variable %moe is to be bound to the global name MOE and that the variable %larry is to be bound to the session name LARRY. This means that, on the first reference to %moe in the request, the global name MOE is looked up in the global object table and, if present, assigned to %moe. If the name MOE is not found in the global object table, %moe is left as null, its initial value. After that first reference, any changes to %moe are reflected in the global object.

Similar processing occurs for variable %larry, with the differences that lookup for the session name is done in the session object table, and if no session is open, the first reference is a request cancelling error.

The Global and Session qualifiers can be specified on a variable declaration without an explicit name, in which case the name used is the uppercase form of the %variable name, excluding the percent sign. This means that the declarations in the previous example are equivalent to:

%moe is object stooge global %larry is object stooge session

The global and session specified in a Global or Session clause on a variable declaration are automatically converted to uppercase, so the initial declarations above are equivalent to the following:

%moe is object stooge global('moe') %larry is object stooge session('larry')

When compiling in case-insensitive mode, non-quoted tokens get translated to uppercase before being processed, so the underlying variable names in the preceding example are really %MOE and %LARRY. Note also that the Object class GetGlobal and SetGlobal methods do not automatically convert the specified names to uppercase, so if GetGlobal or SetGlobal are used to access bound global names, the names they use must be in uppercase.

The Global and Session qualifiers are not allowed on variables in structures or in non-shared Private or Public blocks in class definitions, but they are allowed in variables in a Public Shared or Private Shared block.

Since the Global and Session qualifiers imply Common, an object variable declared as Global or Session in a method or complex subroutine can be accessed in another method or complex subroutine or in the main program level, as long as the same implicit or explicit global/session name is used for all declarations. Trying to declare the same variable name with different global names is a compilation error. Trying to declare different variables bound to the same global name is also a compilation error. Obviously, if global and session objects with the same name are to be referenced in a single request, at least one of them has to be bound to a variable name different from the global or session name.

The Global and Session qualifiers are allowed on object %variable declarations, whether for objects, collections, enumerations, user-defined classes, or system classes. Any attempt to access the same global/session name in different requests using variables of different classes will cause request cancellation. In this case, if the variables are statically bound %variables, the request cancellation occurs on the first reference to the %variable.

Two user-defined classes in two requests are considered the same if the classes have the same name and all of the Public and Private non-shared variables have the same types in the same order. All other class elements, including the following, can vary between requests and still be allowed to be accessed as Global by the requests:

  • Anything about methods. This includes their absence or presence, parameters, and code.
  • Public and private shared variables.
  • The names of any variables.

For example, the following class is defined in one request as:

class silly public absurd is float kooky is longstring nutty is fixed dp 2 end public end class ... %test is object silly global

And the class is defined in another request as:

class silly public profound is float solemn is longstring grave is fixed dp 2 end public end class ... %test is object silly global

Since they have the same default global name (TEST, the uppercased variable name) and exactly similar non-shared variables, the classes refer to the same object, so the requests could work with the same underlying object. The objects are no longer the same, however, if the class definition is changed to:

class silly public profound is float solemn is longstring grave is fixed dp 3 end public end class ... %test is object silly global

The third public variables in the classes now differ in datatype (DP 3 instead of DP 2), so they cannot share a global object. The following class is also not allowed to share a global name with the Silly class because the public variables are in a different order:

class silly public grave is fixed dp 2 solemn is longstring profound is float end public end class ... %test is object silly global

If a global or session object's class contains object variables for other classes, those classes, too, must match from request to request for the global or session object to be accessible from all the requests.

In general, it is recommended that you keep as similar as possible any classes that are referred to among multiple requests using global/session names. It makes most sense to vary between requests only the methods that are actually defined for the class — and to keep program size down, define only the methods that will be used in a request.

Public and Private Shared variables are not saved in the object that underlies a global name, so they are not preserved from request to request. In fact, as noted before, shared variables are allowed to be different from request to request even if individual object instances of the class are accessed in all those requests via a global or session name.

A file or group object (see File classes) associated with a global/session name will be implicitly discarded if the file or group is closed between requests.

Using system class methods to access global and session objects

Using the Object class

In addition to binding to a variable, the other way to access global/session names is to use shared methods in the Object class. The Object class can be thought of us the base class for all objects. It is possible to define an object in the Object class:

%thingy is object object

But there is not much one can do with an Object class object other than to use it as a handle to access shared methods. As such, if using dynamic globals, it might make sense to have a standard Common object variable called %object:

%object is object object common

The following shared methods are available for the Object class:

MethodDescription
DiscardGlobalDiscard a global object
DiscardGlobalsDiscard global objects
DiscardSessionDiscard a session object
DiscardSessionsDiscard session objects
GetGlobalRetrieve global object reference
GetSessionRetrieve session object reference
GlobalListGet list of global objects
NullifyGlobalClear global object reference
NullifyGlobalsClear global object references
NullifySessionClear session object reference
NullifySessionsClear global object references
SessionListGet list of session objects
SetGlobalSet global object reference
SetSessionSet session object reference

The following code illustrates how globals are set:

%moe is class stooge %larry is class stooge %curly is class stooge ... %(object):setGlobal('StoogeHoward', %moe) %(object):setGlobal('StoogeFine', %larry) %(object):setGlobal('StoogeHoward', %curly)

These globals can then be retrieved in a subsequent request:

%zeppo is class stooge %groucho is class stooge %chico is class stooge ... %(object):getGlobal('StoogeHoward', %zeppo) %(object):getGlobal('StoogeFine', %groucho) %(object):getGlobal('StoogeHoward', %chico)

The following would discard any session objects referenced by a name that begins with "Stooge":

%(object):discardSessions('Stooge*')

There is no distinction between a global/session name that references a Null object and a global/session name that simply is not set. When a global or session name is not set (or null), there is no validation of the class on a GetGlobal or GetSession (or retrieval via a bound reference).

For example, the following code is valid:

%moe is class stooge %list is class stringList %(object):setGlobal('LIST', %moe) %(object):getGlobal('LIST', %list)

Even though the global name LIST was set to reference a Stooge class object, the input object reference was null (%moe was not instantiated), so the global name was unset. Since the global name was not set prior to the GetGlobal call, the GetGlobal call simply set %list to null.

The NullifyGlobal and NullifySession methods are equivalent to SetGlobal and SetSession calls with Null for the object input. That is, the following two lines of code are equivalent:

%(object):nullifyGlobal('Vino') %(object):setGlobal('Vino', null)

Using the PersistentObjectInfo class

The PersistentObjectInfo class contains information about global or session objects in the current thread. PersistentObjectInfo objects offer the advantage of the sorting, finding, and subsetting facilities of collections.

Managing object storage

There are three basic models for object storage management in object-oriented programming environments:

  • Application managed
  • Reference count
  • Garbage collection

The application cleans up

Application storage management means that it is an application's responsibility to clean up objects that are no longer needed. This model is not used in many object-oriented languages, with the notable exception of C++ and its derivatives, because it places an unnecessary burden on application programs.

Still, many languages allow an application to do object storage management if it wants. This typically involves provision of a method to explicitly discard an object when it is no longer needed. SOUL is no exception to this: it provides a Discard method to allow an object to be discarded when it is no longer needed.

Cleanup based on object reference count

In the reference count model, a count of references to an object is maintained, and when that count goes to zero, the object is discarded. This model works well because objects are generally discarded as soon as they are "unreachable." That is, if there are no object variables that directly or indirectly reference an object, the object is essentially unusable and so can be discarded.

By discarding objects as soon as they are unreachable, the reference count model tends to facilitate applications that run in a small "footprint," that is, use minimal storage. In addition, this model tends to ensure smooth response and to minimize the "burps" and excessive CPU overhead associated with garbage collection. For all these reasons, SOUL implements the reference count model of object management.

It is quite common for there to be only a single reference to an object. With the reference count model, when that single reference is lost, the object can immediately be discarded. For example, consider the following:

%sl is object stringList ... for %i from 1 to %someLargeNumber %sl = new %sl:add('First item in iteration ' with %i) %sl:add('Second item in iteration ' with %i) %sl:print end for

While not very interesting, this illustrates a key point. On the first iteration of the loop, a Stringlist object instance is created (instantiated). On the second iteration, a new Stringlist object is instantiated. But the only way to reference the object created in the first iteration was via %sl, and %sl now references the object instantiated in the second iteration. The reference count for the first object goes to zero, so the object is automatically discarded. This process is repeated at each iteration: the object instantiated in the previous iteration is discarded when %sl is set to reference the new instance.

Thus with no effort on the programmer's part, SOUL ensures that there is never more than one instance of a Stringlist object in this code.

One drawback of the reference count model is that there might be a rarely used reference to an object "laying around," and this prevents an object from being discarded when it should. There is not much to be done about this, as there is no way for SOUL to "know" that an application is really done with an object should this occur. Fortunately, it is rare that a reference will lay around long without being assigned to something else, eliminating the last reference to an object, and so causing it to be cleaned up (discarded). If, however, this is a concern, an application should explicitly discard an object when it is no longer needed.

A larger problem with the reference count model is that it is largely incapable of dealing with cycles. A cycle is a chain of circular references where, for example, object A contains a reference to object B which contains a reference back to object A. In such a case, each of these circular references count as a reference, so they keep the objects from being discarded, even if there are no other references to the objects, because the objects are unreachable.

Cycle example

Consider, for example, the following:

class circular public variable next is object circular variable number is float end public end class %a is object circular %b is object circular %a = new %a:number = 1 %b = new %b:number = 2 %a:next = %b %b:next = %a

The object pointed to by %a (call it ObjectA) has a reference count of two — one for the reference via %a and another via %b. And the object pointed to by %b (call it ObjectB) has a reference count of two — one for the reference via %b and another via %a. Now suppose this statement is executed:

%b = null

ObjectB is no longer reachable from %b, but it can still be accessed via %a, so its reference count is one and it is not discarded. ObjectA is still referenced by %a and it also has a reference from ObjectB, so its reference count is still two. Now suppose this statement is executed:

%a = null

Since %a no longer references ObjectA, ObjectA's reference count goes to one (the reference in ObjectB). And ObjectB's reference count is also one (the reference in ObjectA). But clearly neither ObjectA nor ObjectB can be accessed any more in the request. Such an unreachable cycle is sometimes called an orphaned cycle.

Obviously, the scenarios involving cycles can get arbitrarily complex, where objects contain many references to other objects in the same and different classes. There is even a simpler scenario where an object can refer to itself, creating a cycle of one.

Fortunately, SOUL cleans up all objects, regardless of their reference count at end of request, since presumably all references to the objects have gone away with the end of request. Unfortunately, for all their benefits, global and session objects muddy this picture. Because a global or session name is simply a reference to an object, it increases the reference count by one. But, unlike %variable references to objects, a global or session reference does not "go away" at end of request.

So, in the previous example, if before setting %a and %b to null, this statement executed:

%(object):setGlobal('ELLIPTIC', %a)

ObjectA's reference count would be incremented by one. Furthermore, even if %a and %b were set to null, you could access ObjectA (and, by extension, ObjectB) in the same or a different request using GetGlobal (or a statically bound variable).

So, if there are any global or session objects at the end of request, it is possible that any object with a non-zero reference count might be accessible directly or indirectly from a global or session reference. As such, the presence of any global or session objects extends the reference count model of object management across request boundaries: that is, objects are not discarded at end of request unless their reference count goes to zero.

The solution to this problem with the reference count model is garbage collection.

Cleanup by garbage collection

In a pure garbage collection based management approach, references to objects are not tracked; instead, unreachable objects are periodically cleaned up. This cleanup process is known as garbage collection.

The garbage collection process involves these steps:

  1. Going through every static reference to objects — variables in most languages; variables and global and session objects in SOUL.
  2. Marking as "reachable" every object accessible via these static references.
  3. Following any references to other objects inside these objects, and marking those objects as reachable.
  4. Marking as reachable any objects referred to by any object marked reachable, until all objects directly or indirectly accessible from a static reference are marked reachable.
  5. Scanning all objects, and discarding the unreachable ones.

As one might imagine, the garbage collection process can be quite expensive, though the cost is proportional to the number of objects, so it does not grow any faster than the number of objects.

Because SOUL uses the reference count model of object management, garbage collection is typically unnecessary. Possible exceptions, however, are requests that create objects with cycles (or potential cycles, anyway). For these, garbage collection might be necessary if:

  • A request runs for a long time, possibly accumulating many orphaned cycles.
  • A thread has one or more global or session references, preventing SOUL from cleaning up all objects at end of request, regardless of their reference count.

If either of these conditions is present, a request that is concerned about the storage used by objects should do either of the following:

  • Explicitly discard objects that are part of cycles that are about to be orphaned.
  • Periodically run garbage collection.

Explicitly discarding

It is sometimes sufficient to explicitly discard a single object in an about-to-be-orphaned cycle to discard the whole cycle. If in the cycle example, you specified

%a:discard

instead of just setting %a to null, not only would ObjectA have been discarded, but, because ObjectA's reference to ObjectB would have been eliminated, ObjectB would also have been discarded.

Or, even more directly, you could initially deep discard an object to more reliably discard the entire cycle:

%a:deepdiscard

Invoking garbage collection

If there are no obvious or simple places to clean up cycles that are about to be orphaned, the only solution is to do garbage collection.

One way an application does garbage collection is to explicitly specify as needed the GarbageCollect method of the Object class:

%(object):GarbageCollect

An alternative way to do garbage collection is to implicitly issue the the GarbageCollect method. You can set the user parameter AUTOGCN to invoke garbage collection automatically based on the count of non-global, non-session objects that have not been discarded by normal object cleanup.

Whether explicit or implicit garbage collection, the user parameter GCSTATS can assist you by producing a message that contains the results of each garbage collection.

How often and when you need to invoke garbage collection is an application issue, but some things to keep in mind when making the decision as to when to run garbage collection are:

  • Garbage collection can be fairly CPU-intensive.
  • Garbage collection is totally unnecessary unless you have orphaned cycles.
  • The benefit of cleaning up orphaned cycles is that resources associated with those cycles are cleaned up. This could be CCATEMP space for the objects themselves, CCATEMP space for object contents, record locking table space, record locks, Janus Sockets threads, etc. How critical it is to quickly release these resources depends, of course, on the resources.
  • All objects are cleaned up at user logout after any implicit session close caused by the logout.
  • Garbage collection is performed automatically at session close time if there are any session object references.

To better understand this last point, it is worth going into more detail about session close processing.

Session close processing

In most basic terms, when a session is closed, all objects that can be accessed either directly or indirectly from the session names are made inaccessible to the closing thread, so those objects can be accessed by whatever thread might open the session. Returning to the cycle example, with the difference that %a is a session object (assume the session is already open):

class circular public variable next is object circular variable number is float end public end class %a is object circular session %b is object circular %c is object circular %a = new %a:number = 1 %c = %a %b = new %b:number = 2 %a:next = %b %b:next = %a

Then issue:

$session_close

ObjectA ""follows the session," so it is no longer accessible in the request. Therefore, after a $session_close, the following results in a null pointer error, since %c references ObjectA which is no longer available in the request.

print %c:number

Perhaps somewhat more surprisingly, the following also results in a null pointer error:

print %b:number

This error occurs because ObjectB was accessible via ObjectA (which was accessible via a session name), so ObjectB is considered to be "owned" by the session: when the session is closed, ObjectB "follows the session," so it becomes inaccessible in the request.

The processing that occurs at session close time is very similar to garbage collection:

  1. All objects accessible from session names are marked as session-reachable.
  2. As with garbage collection, references inside session-reachable objects are followed, marking any objects referred to inside session-reachable objects as also session reachable. This process is continued until all objects that might be reachable from a session name have been marked as such.
  3. All objects are scanned, and those that are not session-reachable remain in thread-specific structures. Those that are session-reachable are moved to a session-specific structure (in CCATEMP).

Because this process is relatively expensive and very similar to garbage collection, SOAP performs garbage collection whenever it does session close processing, since it is much more efficient to do both at once rather than to do each individually. Therefore, and simply, garbage collection is unnecessary after a $Session_Close.

See also