A design limitation in VisualAge causes Semaphore>>wait to fail with the error:
'Invalid operation during callback'
if the operation occurs during a UI callback, and the semaphore must wait.
GBS relies on semaphores for normal operation. If a UI callback must unstub objects, or invoke GemStone code, and another thread is currently accessing that GbsSession, this error may occur.
The simplest way to work around this error is to fork execution at some point so that by the time GBS has to go through a semaphore, it is no longer running in the UI process. Examine the stack traces of occurrences of this error and determine an appropriate place to fork. This can often be achieved by wrapping the entire contents of such a method with:
[
<contents of method>
] fork
Often, this can be isolated to a small number of places in the application.
There are a couple restriction on this workaround - the senders of this method cannot be relying on the return value from this method. In many cases, the sender is the VisualAge method CwCallbackRec>>callWith:callData:, which doesn't use the return value of the application's callback method, so it works (assuming there aren't other senders in the application that do rely on the return value).
Another restriction is there can't be any returns (^) at all from inside the forked block. So if the method contains a control flow construct such as:
x == y ifTrue: [
<do one thing>
^self ].
<do another thing>
... it needs to be recoded to avoid the return statement, like:
x == y
ifTrue: [
<do one thing> ]
ifFalse: [
<do another thing> ]
One potential problem with this workaround of simply forking, though, is the possibility of introducing multiple threads into the application where you might not want it. When the block is forked off, the UI will immediately become usable again, even though the block may still be executing. Ask yourself the following questions: Is it possible that another UI request will be issued before that block finishes executing? If so, will that be OK?
If that's not OK, then the next part of the workaround is to introduce a work queue. Rather than simply saying "[ <work> ] fork", instead the workaround will be of the form "someQueue submit: [ <work> ]", and the queue will make sure that blocks submitted to it are executed one at a time.
Last updated: 3/31/05