[GemStone-Smalltalk] objectForOop: question about some errors we get + how to implement a weak reference?

Otto Behrens otto at finworks.biz
Fri Jul 22 09:44:01 PDT 2022

> We only see this happening after an MFC and only occasionally and also
> only from within gems that have been running before the MFC.

Ok. We never run a full MFC with seaside sessions up. MFC works better if
you can stop as many processes as possible. This is because it does a
complete sweep of the repository. We rely on the Epoch GC to mark objects
as possibly dead. There may be a subtle difference. The reclaim gem will
come later and claim the space. This is my layman's understanding. I see
you use oopIsDead when the space it occupies has not been reclaimed yet. I
remember there may be some more complexity around the "deadness" of an

> Since the oop lookup is part of rendering the log entries in our Seaside
> application, connected users can trigger this error. The stack trace I
> pasted was triggered multiple times from the same gem, while Seaside
> requests handled by other gems were processing the same data (i.e. log
> entries) without errors at the same time (probably getting a nil result for
> the objectForOop:, as expected).
> So, the error probably occurs at the first access to the instance variable
> of the object we retrieved via _objectForOop:. Since the place in the stack
> trace where the error is thrown is indeed the first time we access the
> instVar with the object that is reported missing, that is probably what is
> happening. Indeed.

I don't understand what you mean by "the instance variable of the object we
retrieved via _objectForOop:". Are you saying that the object returned by
_objectForOop: (which is now marked dead by the MFC) refers to another
object, which may now also be possibly dead? Is it perhaps possible that
your access modifies the object? I don't see the correlation: why accessing
the instVar should have anything to do with it, unless there is a side
effect on the accessing.

> Unfortunately, I would expect _oopIsDead: to report the object as being
> dead in that case and thus not return us the object.

I agree, unless it is somehow re-attached. I think this is possible.
Getting hold of an object that was marked as dead can somehow resurrect it,
even if the gem that accessed it has the reference in transient memory.
Perhaps this has something to do with it: there is a reference now (after
looking at it) or the dead object was moved to a location meant for objects
that are not dead.

The block passed to ifNil:ifNotNil: in your code below could be a complex
block, which means that there could be a copy of the invocation in memory
that would now refer to "object".

I'm not sure if #ifNil:ifNotNil: is optimised by the compiler in the
version you are using. I know that there are some more optimisations in
3.6. There is an issue with calling

 (SystemRepository _oopIsDead: anInteger)
ifTrue: aBlock
because I think it cannot be optimised by the compiler (aBlock is sent to
#ifTrue:ifFalse:), but if you do
 (SystemRepository _oopIsDead: anInteger)
ifTrue: [ aBlock value ]
the ifTrue:ifFalse: call is optimised.

This probably has nothing to do with your issue.

Perhaps consider something like this. I know the multiple exit points are
not as nice. But there is some logic to what the compiler can do.

privateObjectAtUniqueIdentifier: anInteger ifAbsent: aBlock
| object |
(object := Object _objectForOop: anInteger)
ifNil: [ ^ aBlock value ].
^ (SystemRepository _oopIsDead: anInteger)
ifTrue: [ aBlock value ]
ifFalse: [ object ]

I wonder what will happen if you call _oopIsDead: first. Perhaps this will
guard it from failing.

Just an old hacker trying things.


> The implementation where we invoke _objectForOop: is shown below. First
> the version of the method as it was when the error occurred then the new
> version where I tried to the ‘fix’ it by wrapping the error handler around
> the entire code.
> privateObjectAtUniqueIdentifier: anInteger ifAbsent: aBlock
> ^ (Object _objectForOop: anInteger)
> ifNil: aBlock
> ifNotNil:[ :object | [ (SystemRepository _oopIsDead: anInteger)
> ifTrue: aBlock
> ifFalse: [ object ] ] on: Error do: [:e | aBlock value ] ]
> privateObjectAtUniqueIdentifier: anInteger ifAbsent: aBlock
> ^ [ (Object _objectForOop: anInteger)
> ifNil: aBlock
> ifNotNil:[ :object | (SystemRepository _oopIsDead: anInteger)
> ifTrue: aBlock
> ifFalse: [ object ] ]
> ] on: Error do: [:e |
> "oop fishing in GemStone may return internal objects that do not implement
> ifNil:ifNotNil:.
> _oopIsDead can also error (see method comment).
> capture these objects here and return nil"
> aBlock value ]
> _______________________________________________
> GemStone-Smalltalk mailing list
> GemStone-Smalltalk at lists.gemtalksystems.com
> https://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.gemtalksystems.com/mailman/archives/gemstone-smalltalk/attachments/20220722/9af5d50e/attachment-0001.htm>

More information about the GemStone-Smalltalk mailing list