The Inbox: Kernel-jar.1412.mcz

Previous Topic Next Topic
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

The Inbox: Kernel-jar.1412.mcz

A new version of Kernel was added to project The Inbox:

==================== Summary ====================

Name: Kernel-jar.1412
Author: jar
Time: 24 May 2021, 11:06:42.045885 pm
UUID: a5554c88-2df6-dc43-ad89-8b128c719dcd
Ancestors: Kernel-jar.1411

Fix an issue when a process terminating another process is terminated in the middle of the unwind.

As a result the original process won't finish unwinding and will leave either a suspended process or a chain of unfinished contexts behind (to be GC'd).

This is an update of Kernel-jar.1411 (not replacement).

Complemented by a test: KernelTests-jar.406

=============== Diff against Kernel-jar.1411 ===============

Item was changed:
  ----- Method: Process>>terminate (in category 'changing process state') -----
  "Stop the process that the receiver represents forever.
  Unwind to execute pending ensure:/ifCurtailed: blocks before terminating.
  If the process is in the middle of a critical: critical section, release it properly."
  | oldList top ctxt outerMost newTop unwindBlock |
  "If terminating the active process, suspend it first and terminate it as a suspended process."
  self isActiveProcess ifTrue: [
  [self terminate] fork.
  ^self suspend].
+ [] ensure: [
  "Always suspend the process first so it doesn't accidentally get woken up.
  N.B. If oldList is a LinkedList then the process is runnable. If it is a Semaphore/Mutex et al
  then the process is blocked, and if it is nil then the process is already suspended."
  oldList := self suspend.
  suspendedContext ifNil: [^self]. "self is already terminated"
  "Release any method marked with the <criticalSection> pragma.
  The argument is whether the process is runnable."
  self releaseCriticalSection: (oldList isNil or: [oldList class == LinkedList]).
  top := suspendedContext.
  suspendedContext := nil. "disable this process while running its stack in active process below"
  "If terminating a process halfways through an unwind, try to complete that unwind block first;
  if there are multiple such nested unwind blocks, try to complete the outer-most one; nested
  unwind blocks will be completed in the process. Halfway-through blocks have already set the
  complete variable (tempAt: 2) in their defining #ensure:/#ifCurtailed contexts from nil to true.
  Note: #findNextUnwindContextUpTo: starts searching from the receiver's sender but the receiver
  itself may be an unwind context."
  ctxt := top.
  ctxt isUnwindContext ifFalse: [ctxt := ctxt findNextUnwindContextUpTo: nil].
  [ctxt isNil] whileFalse: [
  (ctxt tempAt:2) ifNotNil: [
  outerMost := ctxt].
  ctxt := ctxt findNextUnwindContextUpTo: nil].
  outerMost ifNotNil: [newTop := self complete: top to: outerMost].
  "By now no halfway-through unwind blocks are on the stack. Create a new top context for each
  pending unwind block (tempAt: 1) and execute it on the unwind block's stack.
  Note: using #value instead of #complete:to: would lead to incorrect evaluation of non-local returns.
  Note: newTop sender points to the former outerMost sender, i.e. the next unexplored context."
  ctxt := newTop ifNil: [top] ifNotNil: [newTop sender].
  ctxt isUnwindContext ifFalse: [ctxt := ctxt findNextUnwindContextUpTo: nil].
  [ctxt isNil] whileFalse: [
  (ctxt tempAt: 2) ifNil: [
  ctxt tempAt: 2 put: true.
  unwindBlock := ctxt tempAt: 1.
  top := unwindBlock asContextWithSender: ctxt.
  self complete: top to: top].
+ ctxt := ctxt findNextUnwindContextUpTo: nil]
+ ]!
- ctxt := ctxt findNextUnwindContextUpTo: nil]!