The way I broke the kernel I think is in a #doValidate
I did a self
error:
'foo'. I thought this was a the correct way to signal that there's
something
wrong with the user input. It obviously wasn't because I didn't get a
SW2 or
MA error but a Seaside error. So what is the correct way?
Today I am in a paper writing mode, so let me try to explain this a
little bit more than usual:
The lock in the SmallWiki kernel makes sure that only one command is
executed at once. It makes sure that the model doesn't get into an
invalid state due to concurrent write accesses. However, having locks
especially during the development phase, can be pretty boring: say if
you are using the debugger to step through a critical section, restart,
jump out of it, ... you might run into troubles because its semaphores
are not properly reset.
This is neither an implementation error of SmallWiki nor one of the the
lock-implementation in Smalltalk, but a common and well known issue in
powerful languages such as Smalltalk or Scheme, where the exception
handling is done at the language and not at the VM level. Java, C#,
C++, PHP, ... don't show this issue since their execution stack is not
a first class object and the exception handling is out of the scope of
the programmer, after an unhanded exception your application needs to
be restarted anyway.
A greatly simplified implementation of a lock might look the following
piece of code. The critical piece is the ensure block, that ensures
that the lock is opened after doing the critical thing in aBlock. So
whatever happens inside aBlock (finishing the evaluation of the block,
doing a non-local return from within the block, raising an exception,
etc.) first the ensure-block is evaluated before continuing with the
default executing. So if you have a self halt in there the ensure-block
will be evaluated because the debugger is opened in a exception handler
at the very top of the stack. However with the debugger we are able to
jump back into aBlock and continue the execution, but now within the
unlocked context. Of course when exiting again from aBlock the
ensure-block will be evaluated again and the lock might get into a bad
state.
lock: aBlock
self waitUntilUnlocked; lock.
aBlock ensure: [ self unlock ].
Especially when using Seaside you have to be aware and take care about
the use of ensure-blocks: say if you are using continuations to jump
into aBlock or out of aBlock the semantics of the #ensure: are
basically undefined! Should it be evaluated multiple times? Only the
first time? Only the last time? How do we know when we leave aBlock for
the last time? Actually I suggest, not to mix continuations and
ensure-blocks.
Are there better/saver ways to fix such isses?
No, it is a known problem, without good solutions. Search Google for
"continuation unwind" and you will find a lot of material about this.
Is there some way of resting the kernel lock if a a
command goes wild?
SW2Kernel kernels do: [ :each |
each instVarAt: 'lock' put: each defaultLock ].
Cheers,
Lukas
--
Lukas Renggli
http://www.lukas-renggli.ch