I'm observing behaviour that I'm almost certain is a bug in Oz, but
every time I've thought that in the past I've been wrong. 8)
I know this is long, but you needn't read the code the first time through; just
read the text. I think you'll be intrigued enough to go back and read the
code.
Here's a code fragment:
- --------------------
local GCObj1 GCObj1Cap in
{StorageObj
{StorageObj StorageGetCapabilities getCapabilities($)}.createObject
createObject( 'T0004a' GCObj1 {NewName} GCObj1Cap )
}
{System.showInfo "Test9."}
{System.show {GCObj1 GCObj1Cap getCapabilities($)}}
{System.showInfo "Test9.1"}
{StorageObj StorageCapabilities.info info( Storage Storage) }
{System.showInfo "Test9.2"}
end
{System.gcDo}
{Delay 5000}
{System.showInfo "Test10."}
- ---------------
All object in this system are wrapped in a procedure wrapper that takes 2
arguments: the capability for the method in question, and a normal method
record. When createObject is called, Storage, among other things, stores
GCObj1 in a weak dictionary with the {NewName} show above as a key, and stores
GCObj1Cap in a non-weak dictionary named @ozNameToCapabilities with the same
key.
This is the finalization thread for the weak dictionary:
- ----------------------------
thread
Name
in
{ForAll @ozNameObjFinalize
proc {$ FinalizeKey#FinalizeObj}
{@system.showInfo "Finalize stream."}
{@system.show FinalizeKey}
{@system.showInfo "Finalize stream.1"}
{@system.show FinalizeObj}
{@system.showInfo "Finalize stream.2"}
{ForAll
{Dictionary.entries @ozNameToCapabilities}
proc {$ Name1#Name2}
{@system.show Name1==FinalizeKey}
{@system.show
{Dictionary.condGet
@ozNameFile
Name1
nil
$
}
}
{@system.show
{Dictionary.condGet
@ozNameFile
FinalizeKey
nil
$
}
}
{@system.show Name2==FinalizeKey}
{@system.show
{Dictionary.condGet
@ozNameFile
Name2
nil
$
}
}
end
}
{@system.showInfo "Finalize stream.3"}
{@system.show
{Dictionary.get
@ozNameToCapabilities
FinalizeKey
$
}
}
{self saveObject( FinalizeObj FinalizeKey
{Dictionary.get
@ozNameToCapabilities
FinalizeKey
}
)
}
{@system.showInfo "Finalize stream.n"}
end
}
end
- -----------------------------
Note that most of that is debugging output.
As the code stands, that thread is never used; gcDo has no perceptible effect,
at least on the weak dictionary in question.
If
{StorageObj StorageCapabilities.info info( Storage Storage) }
is commented out, the finalization is reached. Note that info() currently
contains only 'skip'!!
It doesn't actually work, though, and the output of the finalization thread is:
- ---------------------------------
Finalize stream.
<N: Name>
Finalize stream.1
<P/2 StatP>
Finalize stream.2
false
2
nil
false
nil
Finalize stream.3
%************************ Error: Dictionary *********************
%**
%** Key not found
%**
%** Dictionary: <Dictionary>
%** Key found: <N: Name>
%** Legal keys: [<N>]
%** In statement: {<P/3 Dictionary.get> <Dictionary> <N: Name> _}
%**
%** Call Stack:
%** procedure in line 101, column 2, PC = 136481280
%**--------------------------------------------------------------
- ---------------------------------
As you can see, the FinalizeKey is not a key in @ozNameToCapabilities, which,
if FinalizeKey is actually the name that it was set to, is impossible.
But wait, it gets better!
If
{System.show {GCObj1 GCObj1Cap getCapabilities($)}}
is commented out, regardless of whether
{StorageObj StorageCapabilities.info info( Storage Storage) }
is commented out or not, it works!
With that GCObj1 call commented out, FinalizeKey is, in fact, a key in
@ozNameToCapablities.
Here's the code that handles the port that GCObj1 uses, stolen straight from
the tutorials, with some mods:
- ---------------------------------
% Client side:
proc {StatP Name M}
R in
% Note that this send causes R to be bound to
% whatever the server thread puts after the # in its
% processing. This will either be N or an error.
{Send P M#Name#R}
if R==N then
skip
else
raise R end
end
end
% Server side:
thread
{ForAll S
proc {$ Method#Name#R}
NewObj OrigRecord
in
% First we make sure the caller has the right
% capability.
if Name \=
{Dictionary.get CapabilityDict {Label Method} }
then
R = mozError( permission_denied(
'That capability does not exist!' ) )
end
% Here we handle those 'methods' that require
% intervention at the wrapper level.
case {Label Method}
% toRecord: takes a variable as its sole
% argument, which it assigns to the persistent
% record associated with the object.
of 'toRecord' then
[*********SNIP************]
% getCapabilities: takes a single variable as
% its sole argument, which it binds to a record
% containing bindings of method names to
% capability Oz names.
[] 'getCapabilities' then
% Here we return the set of capabilities as
% a record.
Method.1 =
{List.toRecord 'capabilities'
% Here we get a tuple list from the
% dictionary in name#method order.
{Record.toListInd
{Dictionary.toRecord
'capabilities'
CapabilityDict
}
$}
$ }
R=N
[*********SNIP************]
- ---------------------------------
As you can see, no writing of any dictionaries is done here. In fact, the
Storage object, and hence @ozNameToCapablities, is never gone anywhere _near_
by this code. But if this code gets run, the finalization stream no longer works.
Clearly, this behaviour makes no sense whatsoever.
Help, please!!
-Robin
-- http://www.digitalkingdom.org/~rlpowell/ BTW, I'm male, honest. le datni cu djica le nu zifre .iku'i .oi le so'e datni cu to'e te pilno je xlali -- RLP http://www.lojban.org/ - Please send submissions to users@mozart-oz.org and administriva mail to users-request@mozart-oz.org. The Mozart Oz web site is at http://www.mozart-oz.org/.