<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
.MsoChpDefault
        {mso-style-type:export-only;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="blue" vlink="#954F72" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal">Hi again,</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">apologies, I mixed things up a bit: suspending a process waiting on a Semaphore is a **no-op** in VA; they don't recognize blocked (i.e. conditionally suspended) as a distinct process state. It means VA can’t suspend e.g. the following
 process while in the wait:<br>
<br>
</p>
<p class="MsoNormal" style="text-indent:.5in">p := [10 seconds wait] forkAt: Processor activePriority + 1</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Sorry for the confusion. VA’s #suspend semantics is indeed different from Squeak’s.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">However, I’ve found an absurd behavior of the following example (series of waits):</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal" style="margin-left:.5in">p := [1 to: 12 do: [: each |</p>
<p class="MsoNormal" style="margin-left:.5in">                                Transcript cr; show: 'waiting ', each.</p>
<p class="MsoNormal" style="margin-left:.5in">                                (Delay forSeconds: 2) wait]</p>
<p class="MsoNormal" style="margin-left:.5in">                ] forkAt: Processor activePriority + 1</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">If you start the process p (do-it), it’ll start printing every 2 seconds, then execute `p resume` let’s say twice and then in order to suspend the process p you have to execute `p suspend` multiple times (at least 3 times) – as if resume
 "charged" the process against suspend :D And on top of that a few BCR errors may appear after some time, depending on how many times you executed #suspend.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I don’t think it’s #suspend’s problem; all three primitives behave the same. I think there’s a bug in #resume (prim 87). Its comment says:
</p>
<p class="MsoNormal" style="text-indent:.5in">[…] Fail if the receiver is already waiting in a queue (in a Semaphore or ProcessScheduler) […]</p>
<p class="MsoNormal">But apparently in Squeak this is not happening; try these two examples:</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Resume bug example 1:</p>
<p class="MsoNormal" style="margin-left:.5in">p := [Semaphore new wait] forkAt: Processor activePriority + 1.</p>
<p class="MsoNormal" style="margin-left:.5in">p resume.</p>
<p class="MsoNormal" style="margin-left:.5in">p isTerminated</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">example 2:</p>
<p class="MsoNormal" style="margin-left:.5in">p := [10 seconds wait] forkAt: Processor activePriority + 1.</p>
<p class="MsoNormal" style="margin-left:.5in">p resume.</p>
<p class="MsoNormal" style="margin-left:.5in">p isTerminated "answers true => a bug"</p>
<p class="MsoNormal" style="margin-left:.5in">"in 10 seconds a BCR error appears :("</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This second example hints why the above example behaves so weird… There’s a Semaphore involved in Delay and it recharges every time after #resume is executed…
</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I guess a possible fix is to make #resume do nothing (or fail) when sending resume to a blocked process.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">However, I’m a bit afraid it may be the same story as with #suspend – some apps may have used this “semantics” and fixing the may break them… But indeed I don’t know.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">What do you think?</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Tie the two changes together?</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Notes:</p>
<p class="MsoNormal">1.  For comparison: VW behaves as expected – an attempt to resume a blocked process results in resume’s failure. (for VA no problem exists because they don’t recognize a blocked process state as distinct from suspended).</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">2.  At last I understand now what Andreas meant by the "changed semantics" of suspend – in earlier versions (prior 2006?) #suspend didn’t work on blocked or waiting processes… just on active ones. Later #suspend worked even on blocked,
 however with the current semantics (remove from the conditional variable).</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Thanks for your patience if you’ve read this far :))</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Best,</p>
<p class="MsoNormal">Jaromir</p>
<p class="MsoNormal"><o:p> </o:p></p>
<div style="mso-element:para-border-div;border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="border:none;padding:0in"><b>From: </b><a href="mailto:mail@jaromir.net">Jaromir Matas</a><br>
<b>Sent: </b>Sunday, February 13, 2022 16:49<br>
<b>To: </b><a href="mailto:vm-dev@lists.squeakfoundation.org">vm-dev@lists.squeakfoundation.org</a>;
<a href="mailto:eliot.miranda@gmail.com">Eliot Miranda</a><br>
<b>Subject: </b>RE: [Vm-dev] VM Maker: VMMaker.oscog-eem.3151.mcz</p>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Hi Eliot,<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I've noticed your recent changes in primitiveSuspend (primitives 88, 568 and 578).<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">> #88 removes a process from a condition variable, allowing subsequently resumed processes to get past their condition variable.  This is a bug, but there are images (noably Qwaq/Teleplace/Virtend) which depend on this behaviour.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">From your explanation I understand Virtend etc. depend not only on the values returned by primitiveSuspend but primarily on the whole "non-backing", semaphore releasing suspend behavior; that means none of the new suspend primitives (568,
 578) will work with Virtend and other images taking advantage of the old semantics - at least without potentially extensive rewrite.
<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">If that is true I wonder why two new suspend versions differing by their return value.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">> Primitive suspend: revert the semantics of #88 to Andreas' revision in the early 2000's. <o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">There's something else I don't understand:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This is Andreas's comment in Process >> offList (ar 12/7/2007):<o:p></o:p></p>
<p class="MsoNormal">```<o:p></o:p></p>
<p class="MsoNormal">                "OBSOLETE. Process>>suspend will atomically reset myList if the process is suspended.
<o:p></o:p></p>
<p class="MsoNormal">                There should never be a need to send #offList but some older users may not be aware
<o:p></o:p></p>
<p class="MsoNormal">                of the changed semantics to suspend and may try the old hickadidoo seen here:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">                                (suspendingList := process suspendingList) == nil<o:p></o:p></p>
<p class="MsoNormal">                                                ifTrue: [process == Processor activeProcess ifTrue: [process suspend]]<o:p></o:p></p>
<p class="MsoNormal">                                                ifFalse: [suspendingList remove: process ifAbsent:[].<o:p></o:p></p>
<p class="MsoNormal">                                                                process offList].<o:p></o:p></p>
<p class="MsoNormal">                <o:p></o:p></p>
<p class="MsoNormal">                Usages like the above should be replaced by a simple 'process suspend' "<o:p></o:p></p>
<p class="MsoNormal">```<o:p></o:p></p>
<p class="MsoNormal">What "changed semantics" (on the 3rd line)?? Was there yet another suspend semantics before the current one?
<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">In any case it seems to me Andreas consciously used the changed (i.e. our current, non-backing) semantics to remove processes from semaphores/mutexes and encouraged using #suspend to achieve that.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">So I’ve started to suspect removing a process from a conditional variable may not have been a bug but an intentional design. Is there some generally accepted standard for #suspend? I personally prefer the "backing up" way but I've noticed
 Visual Age openly use this non-backing, semaphore releasing #suspend semantics while Visual Works use the backing up semantics implemented by primitives 568/578.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I have no way to verify my suspicion but you might be in a position to shed some light :)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I'm looking forward to hearing from you.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Best regards,<o:p></o:p></p>
<p class="MsoNormal">Jaromir<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From: </b><a href="mailto:commits@source.squeak.org">commits@source.squeak.org</a><br>
<b>Sent: </b>Friday, February 11, 2022 1:36<br>
<b>To: </b><a href="mailto:vm-dev@lists.squeakfoundation.org">vm-dev@lists.squeakfoundation.org</a><br>
<b>Subject: </b>[Vm-dev] VM Maker: VMMaker.oscog-eem.3151.mcz<o:p></o:p></p>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><br>
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:<br>
<a href="http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3151.mcz">http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3151.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: VMMaker.oscog-eem.3151<br>
Author: eem<br>
Time: 10 February 2022, 4:36:05.908056 pm<br>
UUID: ee3d31a3-ee4b-4205-87cc-be78d1879c79<br>
Ancestors: VMMaker.oscog-eem.3150<br>
<br>
Primitive suspend: revert the semantics of #88 to Andreas' revision in the early 2000's.  #88 removes a process from a condition variable, allowing subsequently resumed processes to get past their condition variable.  This is a bug, but there are images (noably
 Qwaq/Teleplace/Virtend) which depend on this behaviour.  Provide #568 (primitiveSuspendBackingUpV1) which backs up a process waiting on a condition variable to the send that invoked the wait primitive, and which answers that list. Provide #578 (primitiveSuspendBackingUpV2)
 which backs up a process waiting on a condition variable to the send that invoked the wait primitive, but in which case answers nil. The presence of the three primitives is indicated by bit 5 of the cogVMFeatureFlags.<br>
<br>
DeflatePlugin: the update primitives can run on the Smalltalk stack.<br>
<br>
Add the FileDialogPlugin from Qwaq/Teleplace/Virtend.<br>
<br>
=============== Diff against VMMaker.oscog-eem.3150 ===============<br>
<br>
Item was changed:<br>
  ----- Method: CoInterpreterPrimitives>>primitiveSuspend (in category 'process primitives') -----<br>
  primitiveSuspend<br>
+        "Primitive #88. Suspend the receiver, aProcess, such that it can be executed again<br>
-        "Primitive. Suspend the receiver, aProcess, such that it can be executed again<br>
          by sending #resume. If the given process is not the active process, take it off<br>
+         its corresponding list.  The primitive returns the list the receiver was previously on.<br>
+         c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578"<br>
-         its corresponding list. If the list was not its run queue assume it was on some<br>
-         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
-         invoked the wait state the process entered.  Hence when the process resumes<br>
-         it will reenter the wait state. Answer the list the receiver was previously on,<br>
-         unless it was the activ eProcess, in which case answer nil."<br>
         | process myList myContext ok |<br>
         process := self stackTop.<br>
         process = self activeProcess ifTrue:<br>
                 [| inInterpreter |<br>
                 "We're going to switch process, either to an interpreted frame or a machine<br>
                  code frame. To know whether to return or enter machine code we have to<br>
                  know from whence we came.  We could have come from the interpreter,<br>
                  either directly or via a machine code primitive.  We could have come from<br>
                  machine code.  The instructionPointer tells us where from:"<br>
+                self stackTopPut: objectMemory nilObject.<br>
-                self pop: 1 thenPush: objectMemory nilObject.<br>
                 inInterpreter := instructionPointer >= objectMemory startOfMemory.<br>
                 self transferTo: self wakeHighestPriority from: CSSuspend.<br>
                 ^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].<br>
         myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
         myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
-        ((objectMemory isPointers: myList)<br>
-         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
-         and: [(objectMemory isContext: myContext)<br>
-         and: [self isResumableContext: myContext]]]) ifFalse:<br>
-                [^self primitiveFailFor: PrimErrBadReceiver].<br>
         ok := self removeProcess: process fromList: myList.<br>
         ok ifFalse:<br>
                 [^self primitiveFailFor: PrimErrOperationFailed].<br>
         objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
+        self stackTopPut: myList!<br>
-        self assert: RevisedSuspend.<br>
-        (RevisedSuspend<br>
-         and: [(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag]) ifTrue:<br>
-                [self backupContext: myContext toBlockingSendTo: myList].<br>
-        self pop: 1 thenPush: myList!<br>
<br>
Item was added:<br>
+ ----- Method: CoInterpreterPrimitives>>primitiveSuspendBackingUpV1 (in category 'process primitives') -----<br>
+ primitiveSuspendBackingUpV1<br>
+        "Primitive #568. Suspend the receiver, aProcess, such that it can be executed again<br>
+         by sending #resume. If the given process is not the active process, take it off<br>
+         its corresponding list. If the list was not its run queue assume it was on some<br>
+         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
+         invoked the wait state the process entered.  Hence when the process resumes<br>
+         it will reenter the wait state. Answer the list the receiver was previously on,<br>
+         unless it was the activeProcess, in which case answer nil.<br>
+         c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578"<br>
+        | process myList myContext ok |<br>
+        process := self stackTop.<br>
+        process = self activeProcess ifTrue:<br>
+                [| inInterpreter |<br>
+                "We're going to switch process, either to an interpreted frame or a machine<br>
+                 code frame. To know whether to return or enter machine code we have to<br>
+                 know from whence we came.  We could have come from the interpreter,<br>
+                 either directly or via a machine code primitive.  We could have come from<br>
+                 machine code.  The instructionPointer tells us where from:"<br>
+                self stackTopPut: objectMemory nilObject.<br>
+                inInterpreter := instructionPointer >= objectMemory startOfMemory.<br>
+                self transferTo: self wakeHighestPriority from: CSSuspend.<br>
+                ^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].<br>
+        myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
+        myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
+        ((objectMemory isPointers: myList)<br>
+         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
+         and: [(objectMemory isContext: myContext)<br>
+         and: [self isResumableContext: myContext]]]) ifFalse:<br>
+                [^self primitiveFailFor: PrimErrBadReceiver].<br>
+        ok := self removeProcess: process fromList: myList.<br>
+        ok ifFalse:<br>
+                [^self primitiveFailFor: PrimErrOperationFailed].<br>
+        objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
+        (objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag ifTrue:<br>
+                [self backupContext: myContext toBlockingSendTo: myList].<br>
+        self stackTopPut: myList!<br>
<br>
Item was added:<br>
+ ----- Method: CoInterpreterPrimitives>>primitiveSuspendBackingUpV2 (in category 'process primitives') -----<br>
+ primitiveSuspendBackingUpV2<br>
+        "Primitive #578. Suspend the receiver, aProcess, such that it can be executed again<br>
+         by sending #resume. If the given process is not the active process, take it off<br>
+         its corresponding list. If the list was not its run queue assume it was on some<br>
+         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
+         invoked the wait state the process entered.  Hence when the process resumes<br>
+         it will reenter the wait state. Answer the list the receiver was previously on iff<br>
+         it was not active and not blocked, otherwise answer nil.<br>
+         c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568,<br>
+         which always answer the list the process was on, even if blocked."<br>
+        <export: true><br>
+        | process myList myContext ok |<br>
+        process := self stackTop.<br>
+        process = self activeProcess ifTrue:<br>
+                [| inInterpreter |<br>
+                "We're going to switch process, either to an interpreted frame or a machine<br>
+                 code frame. To know whether to return or enter machine code we have to<br>
+                 know from whence we came.  We could have come from the interpreter,<br>
+                 either directly or via a machine code primitive.  We could have come from<br>
+                 machine code.  The instructionPointer tells us where from:"<br>
+                self stackTopPut: objectMemory nilObject.<br>
+                inInterpreter := instructionPointer >= objectMemory startOfMemory.<br>
+                self transferTo: self wakeHighestPriority from: CSSuspend.<br>
+                ^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].<br>
+        myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
+        myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
+        ((objectMemory isPointers: myList)<br>
+         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
+         and: [(objectMemory isContext: myContext)<br>
+         and: [self isResumableContext: myContext]]]) ifFalse:<br>
+                [^self primitiveFailFor: PrimErrBadReceiver].<br>
+        ok := self removeProcess: process fromList: myList.<br>
+        ok ifFalse:<br>
+                [^self primitiveFailFor: PrimErrOperationFailed].<br>
+        objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
+        (objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag<br>
+                ifTrue:<br>
+                        [self backupContext: myContext toBlockingSendTo: myList.<br>
+                         self stackTopPut: objectMemory nilObject]<br>
+                ifFalse:<br>
+                        [self stackTopPut: myList]!<br>
<br>
Item was removed:<br>
- ----- Method: CoInterpreterPrimitives>>primitiveSuspendV2 (in category 'process primitives') -----<br>
- primitiveSuspendV2<br>
-        "Primitive. Suspend the receiver, aProcess, such that it can be executed again<br>
-         by sending #resume. If the given process is not the active process, take it off<br>
-         its corresponding list. If the list was not its run queue assume it was on some<br>
-         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
-         invoked the wait state the process entered.  Hence when the process resumes<br>
-         it will reenter the wait state. Answer the list the receiver was previously on iff<br>
-         it was not active and not blocked, otherwise answer nil.<br>
-         c.f. primitiveSuspend, which always answers the list the process was on, if blocked."<br>
-        <export: true><br>
-        | process myList myContext ok |<br>
-        process := self stackTop.<br>
-        process = self activeProcess ifTrue:<br>
-                [| inInterpreter |<br>
-                "We're going to switch process, either to an interpreted frame or a machine<br>
-                 code frame. To know whether to return or enter machine code we have to<br>
-                 know from whence we came.  We could have come from the interpreter,<br>
-                 either directly or via a machine code primitive.  We could have come from<br>
-                 machine code.  The instructionPointer tells us where from:"<br>
-                self pop: 1 thenPush: objectMemory nilObject.<br>
-                inInterpreter := instructionPointer >= objectMemory startOfMemory.<br>
-                self transferTo: self wakeHighestPriority from: CSSuspend.<br>
-                ^self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter].<br>
-        myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
-        myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
-        ((objectMemory isPointers: myList)<br>
-         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
-         and: [(objectMemory isContext: myContext)<br>
-         and: [self isResumableContext: myContext]]]) ifFalse:<br>
-                [^self primitiveFailFor: PrimErrBadReceiver].<br>
-        ok := self removeProcess: process fromList: myList.<br>
-        ok ifFalse:<br>
-                [^self primitiveFailFor: PrimErrOperationFailed].<br>
-        objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
-        self assert: RevisedSuspend.<br>
-        (RevisedSuspend<br>
-         and: [(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag])<br>
-                ifTrue:<br>
-                        [self backupContext: myContext toBlockingSendTo: myList.<br>
-                         self pop: 1 thenPush: objectMemory nilObject]<br>
-                ifFalse:<br>
-                        [self pop: 1 thenPush: myList]!<br>
<br>
Item was changed:<br>
  ----- Method: CogVMSimulator class>>initialize (in category 'class initialization') -----<br>
  initialize<br>
         "These are primitives that alter the state of the stack.  They are here simply for assert checking.<br>
          After invocation the Cogit should not check for the expected stack delta when these primitives<br>
          succeed, because the stack will usually have been modified."<br>
         StackAlteringPrimitives := #(   primitiveClosureValue primitiveClosureValueWithArgs primitiveClosureValueNoContextSwitch<br>
                                                                         primitiveClone primitiveInstVarAt primitiveSlotAt "because these can cause code compactions..."<br>
                                                                         primitiveEnterCriticalSection primitiveExitCriticalSection<br>
                                                                         primitiveFullClosureValue primitiveFullClosureValueWithArgs primitiveFullClosureValueNoContextSwitch<br>
+                                                                        primitiveSignal primitiveWait primitiveResume primitiveYield<br>
+                                                                         primitiveSuspend primitiveSuspendBackingUpV1 primitiveSuspendBackingUpV2<br>
-                                                                        primitiveSignal primitiveWait primitiveResume primitiveSuspend primitiveSuspendV2 primitiveYield<br>
                                                                         primitiveExecuteMethodArgsArray primitiveExecuteMethod<br>
                                                                         primitivePerform primitivePerformWithArgs primitivePerformInSuperclass<br>
                                                                         primitiveTerminateTo primitiveStoreStackp primitiveDoPrimitiveWithArgs) asIdentitySet!<br>
<br>
Item was changed:<br>
  ----- Method: DeflatePlugin>>primitiveDeflateUpdateHashTable (in category 'primitives') -----<br>
  primitiveDeflateUpdateHashTable<br>
         "Primitive. Update the hash tables after data has been moved by delta."<br>
-        | delta table tableSize tablePtr entry |<br>
         <export: true><br>
+        <primitiveMetadata: #(FastCPrimitive FastCPrimitiveAlignForFloatsFlag)> "Using AlignForFloats since the arithmetic is potentially vectorizable..."<br>
+        | delta table tableSize tablePtr entry |<br>
         <var: #tablePtr type:'int *'><br>
-        interpreterProxy methodArgumentCount = 2<br>
-                ifFalse:[^interpreterProxy primitiveFail].<br>
         delta := interpreterProxy stackIntegerValue: 0.<br>
+        table := interpreterProxy stackValue: 1.<br>
-        table := interpreterProxy stackObjectValue: 1.<br>
         interpreterProxy failed ifTrue:[^nil].<br>
+        (interpreterProxy isWords: table) ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
-        (interpreterProxy isWords: table)<br>
-                ifFalse:[^interpreterProxy primitiveFail].<br>
         tableSize := interpreterProxy slotSizeOf: table.<br>
         tablePtr := interpreterProxy firstIndexableField: table.<br>
         0 to: tableSize-1 do:[:i|<br>
                 entry := tablePtr at: i.<br>
                 entry >= delta<br>
                         ifTrue:[tablePtr at: i put: entry - delta]<br>
                         ifFalse:[tablePtr at: i put: 0]].<br>
+        interpreterProxy pop: 2 "Leave rcvr on stack"!<br>
-        interpreterProxy pop: 2. "Leave rcvr on stack"!<br>
<br>
Item was changed:<br>
  ----- Method: DeflatePlugin>>primitiveUpdateAdler32 (in category 'primitives') -----<br>
  primitiveUpdateAdler32<br>
         "Primitive. Update a 32bit CRC value."<br>
-        | collection stopIndex startIndex length bytePtr s1 adler32 s2 b |<br>
         <export: true><br>
+        <primitiveMetadata: #(FastCPrimitive FastCPrimitiveAlignForFloatsFlag)> "Using AlignForFloats since the arithmetic is potentially vectorizable..."<br>
+        | collection stopIndex startIndex length bytePtr s1 adler32 s2 b |<br>
         <var: #adler32 type:'unsigned int '><br>
         <var: #bytePtr type:'unsigned char *'><br>
+        collection := interpreterProxy stackValue: 0.<br>
-        interpreterProxy methodArgumentCount = 4<br>
-                ifFalse:[^interpreterProxy primitiveFail].<br>
-        collection := interpreterProxy stackObjectValue: 0.<br>
         stopIndex := interpreterProxy stackIntegerValue: 1.<br>
         startIndex := interpreterProxy stackIntegerValue: 2.<br>
         adler32 := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 3).<br>
+        interpreterProxy failed ifTrue: [^nil].<br>
-        interpreterProxy failed ifTrue:[^0].<br>
         ((interpreterProxy isBytes: collection) and:[stopIndex >= startIndex and:[startIndex > 0]])<br>
                 ifFalse:[^interpreterProxy primitiveFail].<br>
         length := interpreterProxy byteSizeOf: collection.<br>
         (stopIndex <= length) ifFalse:[^interpreterProxy primitiveFail].<br>
         bytePtr := interpreterProxy firstIndexableField: collection.<br>
         startIndex := startIndex - 1.<br>
         stopIndex := stopIndex - 1.<br>
         s1 := adler32 bitAnd: 16rFFFF.<br>
+        s2 := adler32 >> 16 bitAnd: 16rFFFF.<br>
-        s2 := (adler32 >> 16) bitAnd: 16rFFFF.<br>
         startIndex to: stopIndex do:[:i|<br>
                 b := bytePtr at: i.<br>
                 s1 := (s1 + b) \\ 65521.<br>
                 s2 := (s2 + s1) \\ 65521.<br>
         ].<br>
         adler32 := (s2 bitShift: 16) + s1.<br>
+        interpreterProxy methodReturnValue: (interpreterProxy positive32BitIntegerFor: adler32)!<br>
-        interpreterProxy<br>
-                pop: 5 "args + rcvr"<br>
-                thenPush: (interpreterProxy positive32BitIntegerFor: adler32)!<br>
<br>
Item was changed:<br>
  ----- Method: DeflatePlugin>>primitiveUpdateGZipCrc32 (in category 'primitives') -----<br>
  primitiveUpdateGZipCrc32<br>
         "Primitive. Update a 32bit CRC value."<br>
-        | collection stopIndex startIndex crc length bytePtr |<br>
         <export: true><br>
+        <primitiveMetadata: #(FastCPrimitive FastCPrimitiveAlignForFloatsFlag)> "Using AlignForFloats since the arithmetic is potentially vectorizable..."<br>
+        | collection stopIndex startIndex crc length bytePtr |<br>
         <var: #bytePtr type: #'unsigned char *'><br>
+        collection := interpreterProxy stackValue: 0.<br>
-        interpreterProxy methodArgumentCount = 4<br>
-                ifFalse:[^interpreterProxy primitiveFail].<br>
-        collection := interpreterProxy stackObjectValue: 0.<br>
         stopIndex := interpreterProxy stackIntegerValue: 1.<br>
         startIndex := interpreterProxy stackIntegerValue: 2.<br>
         crc := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 3).<br>
         interpreterProxy failed ifTrue: [^self].<br>
         ((interpreterProxy isBytes: collection) and:[stopIndex >= startIndex and:[startIndex > 0]])<br>
                 ifFalse:[^interpreterProxy primitiveFail].<br>
         length := interpreterProxy byteSizeOf: collection.<br>
         (stopIndex <= length) ifFalse:[^interpreterProxy primitiveFail].<br>
         bytePtr := interpreterProxy firstIndexableField: collection.<br>
         self cCode:'' inSmalltalk:[zipCrcTable := CArrayAccessor on: GZipWriteStream crcTable].<br>
         startIndex := startIndex - 1.<br>
         stopIndex := stopIndex - 1.<br>
         startIndex to: stopIndex do:<br>
                 [:i|<br>
+                crc := (zipCrcTable at: ((crc bitXor: (bytePtr at: i)) bitAnd: 255)) bitXor: crc >> 8].<br>
+        interpreterProxy methodReturnValue: (interpreterProxy positive32BitIntegerFor: crc)!<br>
-                crc := (zipCrcTable at: ((crc bitXor: (bytePtr at: i)) bitAnd: 255)) bitXor: (crc >> 8)].<br>
-        interpreterProxy<br>
-                pop: 5 "args + rcvr"<br>
-                thenPush: (interpreterProxy positive32BitIntegerFor: crc)!<br>
<br>
Item was added:<br>
+ InterpreterPlugin subclass: #FileDialogPlugin<br>
+        instanceVariableNames: ''<br>
+        classVariableNames: ''<br>
+        poolDictionaries: ''<br>
+        category: 'VMMaker-Plugins'!<br>
+ <br>
+ !FileDialogPlugin commentStamp: '<historical>' prior: 0!<br>
+ A plugin supporting various bits and pieces for native file dialogs.!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin class>>hasHeaderFile (in category 'compiling') -----<br>
+ hasHeaderFile<br>
+        "If there is a single intrinsic header file to be associated with the plugin, here is where you want to flag"<br>
+        ^true!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin class>>requiresPlatformFiles (in category 'compiling') -----<br>
+ requiresPlatformFiles<br>
+        "default is ok for most, any plugin needing platform specific files must say so"<br>
+        ^true!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin class>>simulatorClass (in category 'simulation') -----<br>
+ simulatorClass<br>
+        ^FileDialogPluginSimulator!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>initialiseModule (in category 'initialize') -----<br>
+ initialiseModule<br>
+        <export: true><br>
+        ^self fileDialogInitialize  "inSmalltalk: true"!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogAddFilter (in category 'file dialogs') -----<br>
+ primitiveFileDialogAddFilter<br>
+        "Primitive. Add a filter to an existing file dialog.<br>
+        Arguments:<br>
+                dlgHandle: Handle for the file dialog.<br>
+                filterDesc: Description for the filter ('Text Files (*.txt)')<br>
+                filterPattern: Filter pattern (*.txt)<br>
+        Returns: Nothing."<br>
+        | dlgHandle filterDesc filterPattern |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 3 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        filterPattern := self stackEphemeralStringValue: 0.<br>
+        filterDesc := self stackEphemeralStringValue: 1.<br>
+        dlgHandle := self stackDialogHandle: 2.<br>
+        interpreterProxy failed ifTrue:<br>
+                [^self primitiveFailFor: PrimErrBadArgument].<br>
+        self fileDialogAddFilter: dlgHandle _: filterDesc _: filterPattern  "inSmalltalk: filterPattern".<br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogCallbackReturn (in category 'file dialogs') -----<br>
+ primitiveFileDialogCallbackReturn<br>
+        "Primitive. Reap the return value from the dialog callback.<br>
+         This is unimplemented (stubbed out) on all current platforms and so it is obsolete."<br>
+        <export: true><br>
+        <legacy><br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogCreate (in category 'file dialogs') -----<br>
+ primitiveFileDialogCreate<br>
+        "Primitive. Create a new file dialog handle and answer the result.<br>
+        Arguments: None.<br>
+        Return value: File dialog handle."<br>
+        | dlgHandle |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 0 ifFalse:<br>
+                [^interpreterProxy primitiveFailFor: PrimErrBadNumArgs].<br>
+        dlgHandle := self fileDialogCreate.<br>
+        dlgHandle < 0 ifTrue:<br>
+                [^interpreterProxy primitiveFailFor: PrimErrOperationFailed].<br>
+        interpreterProxy methodReturnInteger: dlgHandle!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogDestroy (in category 'file dialogs') -----<br>
+ primitiveFileDialogDestroy<br>
+        "Primitive. Hide/destroy the file dialog after it is done.<br>
+        Arguments:<br>
+                dlgHandle:       Handle of the file dialog.<br>
+        Return value: Nothing.<br>
+        Notes: This primitive may fail if the dialog wasn't completed and platform<br>
+        doesn't support destroying existing dialogs. Generally it is assumed that<br>
+        the dialog has been closed by the user before calling this method."<br>
+        | dlgHandle ok |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        dlgHandle := self stackDialogHandle: 0.<br>
+        interpreterProxy failed ifTrue:[^nil].<br>
+        ok := self fileDialogDestroy: dlgHandle  "inSmalltalk: false".<br>
+        ok ifFalse:[^interpreterProxy primitiveFail].<br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogDone (in category 'file dialogs') -----<br>
+ primitiveFileDialogDone<br>
+        "Primitive. Answer whether the file dialog completed or not.<br>
+        Arguments:<br>
+                dlgHandle:       Handle of the file dialog.<br>
+        Return value: Boolean indicating whether the dialog completed."<br>
+        | dlgHandle |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        dlgHandle := self stackDialogHandle: 0.<br>
+        interpreterProxy failed ifFalse:<br>
+                [interpreterProxy methodReturnBool: (self fileDialogDone: dlgHandle)]!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogDoneSemaphore (in category 'file dialogs') -----<br>
+ primitiveFileDialogDoneSemaphore<br>
+        "Primitive. Set the semaphore to be signaled when the file dialog completes.<br>
+        Arguments:<br>
+                dlgHandle:      Handle of the file dialog.<br>
+                semaIndex:      External semaphore index.<br>
+        Return value: Nothing."<br>
+        | dlgHandle semaIndex |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 2 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        semaIndex := interpreterProxy stackIntegerValue: 0.<br>
+        dlgHandle := self stackDialogHandle: 1.<br>
+        interpreterProxy failed ifTrue:[^nil].<br>
+        self fileDialogDoneSemaphore: dlgHandle _: semaIndex  "inSmalltalk: semaIndex".<br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogGetFilterIndex (in category 'file dialogs') -----<br>
+ primitiveFileDialogGetFilterIndex<br>
+        "Primitive. Get the current filter index from one of the previously chosen filters.<br>
+        Arguments:<br>
+                dlgHandle: Handle for the file dialog.<br>
+        Return value: Filter index."<br>
+        | dlgHandle result |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        dlgHandle := self stackDialogHandle: 0.<br>
+        interpreterProxy failed ifTrue:[^nil].<br>
+        result := self fileDialogGetFilterIndex: dlgHandle  "inSmalltalk: 0".<br>
+        result = 0 ifTrue:[^interpreterProxy primitiveFail].<br>
+        interpreterProxy methodReturnInteger: result!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogGetResult (in category 'file dialogs') -----<br>
+ primitiveFileDialogGetResult<br>
+        "Primitive. Retrieve the result of the file dialog.<br>
+        Arguments:<br>
+                dlgHandle:      Handle of the file dialog.<br>
+        Return value: String for choosen file, or nil if canceled"<br>
+        | dlgHandle cString |<br>
+        <export: true><br>
+        <var: 'cString' type: #'char *'><br>
+        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        dlgHandle := self stackDialogHandle: 0.<br>
+        interpreterProxy failed ifFalse:<br>
+                [cString := self fileDialogGetResult: dlgHandle.<br>
+                 interpreterProxy failed ifFalse:<br>
+                        [self methodReturnStringOrNil: cString]]!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogSetCallbackSemaphore (in category 'file dialogs') -----<br>
+ primitiveFileDialogSetCallbackSemaphore<br>
+        "Primitive. Set the callback semaphore to be used for running modal dialogs.<br>
+         This is unimplemented (stubbed out) on all current platforms and so it is obsolete."<br>
+        <export: true><br>
+        <legacy><br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogSetFile (in category 'file dialogs') -----<br>
+ primitiveFileDialogSetFile<br>
+        "Primitive. Set the initial file name/path for the dialog.<br>
+        Arguments:<br>
+                dlgHandle: Handle for the file dialog.<br>
+                filePath: Initial path for open dialog<br>
+        Returns: Nothing."<br>
+        | dlgHandle filePath |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 2 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        filePath := self stackEphemeralStringValue: 0.<br>
+        dlgHandle := self stackDialogHandle: 1.<br>
+        interpreterProxy failed ifTrue:<br>
+                [^self primitiveFailFor: PrimErrBadArgument].<br>
+        self fileDialogSetFile: dlgHandle _: filePath  "inSmalltalk: false".<br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogSetFilterIndex (in category 'file dialogs') -----<br>
+ primitiveFileDialogSetFilterIndex<br>
+        "Primitive. Set the current filter index from one of the previously chosen filters.<br>
+        Arguments:<br>
+                dlgHandle: Handle for the file dialog.<br>
+                index: Current filter index.<br>
+        Return value: Nothing."<br>
+        | dlgHandle index |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 2 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        index := interpreterProxy stackIntegerValue: 0.<br>
+        dlgHandle := self stackDialogHandle: 1.<br>
+        interpreterProxy failed ifFalse:<br>
+                [self fileDialogSetFilterIndex: dlgHandle _: index  "inSmalltalk: index".<br>
+                 interpreterProxy methodReturnReceiver]!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogSetLabel (in category 'file dialogs') -----<br>
+ primitiveFileDialogSetLabel<br>
+        "Primitive. Set the label for the dialog.<br>
+        Arguments:<br>
+                dlgHandle: Handle for the file dialog.<br>
+                dlgLabel: Dialog label.<br>
+        Returns: Nothing."<br>
+        | dlgHandle dlgLabel |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 2 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        dlgLabel := self stackEphemeralStringValue: 0.<br>
+        dlgHandle := self stackDialogHandle: 1.<br>
+        interpreterProxy failed ifTrue:<br>
+                [^self primitiveFailFor: PrimErrBadArgument].<br>
+        self fileDialogSetLabel: dlgHandle _: dlgLabel  "inSmalltalk: dlgLabel".<br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogSetProperty (in category 'file dialogs') -----<br>
+ primitiveFileDialogSetProperty<br>
+        "Primitive. Set additional properties for a file dialog.<br>
+        Arguments:<br>
+                dlgHandle:      Handle of the file dialog.<br>
+                propName:       Property name.<br>
+                propValue:       Boolean indication whether to turn it on or off.<br>
+        Return value: Boolean, indicating whether the property is supported."<br>
+        | dlgHandle propName propValue |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 3 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        dlgHandle := self stackDialogHandle: 2.<br>
+        propName := self stackEphemeralStringValue: 1.<br>
+        propValue := self stackBooleanValue: 0.<br>
+        interpreterProxy failed ifTrue:<br>
+                [^self primitiveFailFor: PrimErrBadArgument].<br>
+        interpreterProxy methodReturnBool: (self fileDialogSetProperty: dlgHandle _: propName _: propValue)!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveFileDialogShow (in category 'file dialogs') -----<br>
+ primitiveFileDialogShow<br>
+        "Primitive. Show the file dialog.<br>
+        Arguments:<br>
+                dlgHandle:      Handle of the file dialog.<br>
+                fSaveAs:        Whether to show an 'open' or a 'save' style dialog.<br>
+        Return value: Nothing."<br>
+        | dlgHandle fSaveAs |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 2 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        fSaveAs := self stackBooleanValue: 0.<br>
+        dlgHandle := self stackDialogHandle: 1.<br>
+        interpreterProxy failed ifTrue:<br>
+                [^nil].<br>
+        (self fileDialogShow: dlgHandle _: fSaveAs) ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        interpreterProxy methodReturnReceiver!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>primitiveGetFileLocation (in category 'file dialogs') -----<br>
+ primitiveGetFileLocation<br>
+        "Primitive. Query for a common file location.<br>
+        Arguments:<br>
+                location: String describing the common file location.<br>
+        Return value: The path to the designated location.<br>
+        Known locations:<br>
+                'home' - the user's home directory<br>
+                'desktop' - the user's desktop directory<br>
+ <br>
+                'temp' - the temp directory to use<br>
+                'preferences' - the place to store (per user) app preferences<br>
+                'applications' - the directory for installing applications<br>
+                'fonts' - the directory to install fonts in the system<br>
+ <br>
+                'documents' - the users documents folder<br>
+                'music' - the users default location for music<br>
+                'pictures' - the users default location for pictures<br>
+                'videos' - the users default location for videos<br>
+        "<br>
+        | location |<br>
+        <export: true><br>
+        interpreterProxy methodArgumentCount = 1 ifFalse:<br>
+                [^interpreterProxy primitiveFail].<br>
+        location := self stackEphemeralStringValue: 0.<br>
+        interpreterProxy failed ifTrue:<br>
+                [^self primitiveFailFor: PrimErrBadArgument].<br>
+        self methodReturnStringOrNil: (self fileDialogGetLocation: location)!<br>
<br>
Item was added:<br>
+ ----- Method: FileDialogPlugin>>stackDialogHandle: (in category 'support') -----<br>
+ stackDialogHandle: index<br>
+        <returnTypeC: #int><br>
+        <inline: #always><br>
+        ^self cCoerce: (interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: index))<br>
+                to: #int!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter class>>initializeMiscConstants (in category 'initialization') -----<br>
  initializeMiscConstants<br>
  <br>
         super initializeMiscConstants.<br>
         STACKVM := true.<br>
  <br>
+        RevisedSuspend := true. "primitiveSuspendBackingUpV1/2 no longer allow a process waiting on a condition variable to go past the condition variable"<br>
-        RevisedSuspend := true. "primitiveSuspend no longer allows a process waiting on a condition variable to go past the condition variable"<br>
  <br>
         "These flags identify a GC operation (& hence a reason to leak check),<br>
          or just operations the leak checker can be run for."<br>
         GCModeFull := 1.                                "stop-the-world global GC"<br>
         GCModeNewSpace := 2.            "Spur's scavenge, or V3's incremental"<br>
         GCModeIncremental := 4.         "incremental global gc (Dijkstra tri-colour marking); as yet unimplemented"<br>
         GCModeBecome := 8.                      "v3 post-become sweeping/Spur forwarding"<br>
         GCCheckImageSegment := 16.      "just a flag for leak checking image segments"<br>
         GCCheckFreeSpace := 32.         "just a flag for leak checking free space; Spur only"<br>
         GCCheckShorten := 64.           "just a flag for leak checking object shortening operations; Spur only"<br>
         GCCheckPrimCall := 128.         "just a flag for leak checking external primitive calls"<br>
  <br>
         StackPageTraceInvalid := -1.<br>
         StackPageUnreached := 0.<br>
         StackPageReachedButUntraced := 1.<br>
         StackPageTraced := 2.<br>
  <br>
         MillisecondClockMask := 16r1FFFFFFF.<br>
         "Note: The external primitive table should actually be dynamically sized but for the sake of inferior platforms (e.g., Mac :-) who cannot allocate memory in any reasonable way, we keep it static (and cross our fingers...)"<br>
         MaxExternalPrimitiveTableSize := 4096. "entries"<br>
  <br>
         FailImbalancedPrimitives := InitializationOptions at: #FailImbalancedPrimitives ifAbsentPut: [true].<br>
         EnforceAccessControl := InitializationOptions at: #EnforceAccessControl ifAbsent: [true].<br>
  <br>
         ReturnToInterpreter := 1. "setjmp/longjmp code."<br>
  <br>
         "Because of a hack with callbacks in the non-threaded VM they must not conflct with the VM's tag bits."<br>
         DisownVMForFFICall := 16.<br>
         DisownVMForThreading := 32<br>
  !<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter class>>initializePrimitiveTable (in category 'initialization') -----<br>
(excessive size, no diff calculated)<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter>>getCogVMFeatureFlags (in category 'internal interpreter access') -----<br>
  getCogVMFeatureFlags<br>
         "Answer an array of flags indicating various optional features of the Cog VM.<br>
          If the bit is set then...<br>
          Bit 0: supports two bytecode sets (MULTIPLEBYTECODESETS)<br>
          Bit 1: supports immutablity (IMMUTABILITY)<br>
          Bit 2: suffers from a UNIX setitimer signal-based heartbeat<br>
          Bit 3: the VM provides cross-platform bit-identical floating point<br>
          Bit 4: the VM can catch exceptions in FFI calls and answer them as primitive failures<br>
+         Bit 5: the VM has suspend primitives 568 & 578 which back up a process to before the wait if it was waiting on a condition variable"<br>
-         Bit 5: the suspend primitive backs up a process to before the wait if it was waiting on a condition variable"<br>
         ^objectMemory integerObjectOf: (MULTIPLEBYTECODESETS ifTrue: [1] ifFalse: [0])<br>
                                                                         + (IMMUTABILITY ifTrue: [2] ifFalse: [0])<br>
                                                                         + (self cppIf: #'ITIMER_HEARTBEAT' ifTrue: [4] ifFalse: [0])<br>
                                                                         + (self cppIf: #'BIT_IDENTICAL_FLOATING_POINT' ifTrue: [8] ifFalse: [0])<br>
                                                                         + (self ioCanCatchFFIExceptions ifTrue: [16] ifFalse: [0])<br>
                                                                         + (RevisedSuspend ifTrue: [32] ifFalse: [0])!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreterPrimitives>>primitiveSuspend (in category 'process primitives') -----<br>
  primitiveSuspend<br>
+        "Primitive #88. Suspend the receiver, aProcess, such that it can be executed again<br>
-        "Primitive. Suspend the receiver, aProcess, such that it can be executed again<br>
          by sending #resume. If the given process is not the active process, take it off<br>
+         its corresponding list.  The primitive returns the list the receiver was previously on.<br>
+         c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578"<br>
-         its corresponding list. If the list was not its run queue assume it was on some<br>
-         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
-         invoked the wait state the process entered.  Hence when the process resumes<br>
-         it will reenter the wait state. Answer the list the receiver was previously on,<br>
-         unless it was the activ eProcess, in which case answer nil."<br>
         | process myList myContext ok |<br>
         process := self stackTop.<br>
         process = self activeProcess ifTrue:<br>
+                [self stackTopPut: objectMemory nilObject.<br>
-                [self pop: 1 thenPush: objectMemory nilObject.<br>
                  ^self transferTo: self wakeHighestPriority].<br>
         myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
         myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
         ((objectMemory isPointers: myList)<br>
          and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
          and: [(objectMemory isContext: myContext)<br>
          and: [self isResumableContext: myContext]]]) ifFalse:<br>
                 [^self primitiveFailFor: PrimErrBadReceiver].<br>
         ok := self removeProcess: process fromList: myList.<br>
         ok ifFalse:<br>
                 [^self primitiveFailFor: PrimErrOperationFailed].<br>
         objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
+        self stackTopPut: myList!<br>
-        self assert: RevisedSuspend.<br>
-        (RevisedSuspend<br>
-         and: [(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag]) ifTrue:<br>
-                [self backupContext: myContext toBlockingSendTo: myList].<br>
-        self pop: 1 thenPush: myList!<br>
<br>
Item was added:<br>
+ ----- Method: StackInterpreterPrimitives>>primitiveSuspendBackingUpV1 (in category 'process primitives') -----<br>
+ primitiveSuspendBackingUpV1<br>
+        "Primitive #568. Suspend the receiver, aProcess, such that it can be executed again<br>
+         by sending #resume. If the given process is not the active process, take it off<br>
+         its corresponding list. If the list was not its run queue assume it was on some<br>
+         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
+         invoked the wait state the process entered.  Hence when the process resumes<br>
+         it will reenter the wait state. Answer the list the receiver was previously on,<br>
+         unless it was the activeProcess, in which case answer nil.<br>
+         c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578"<br>
+        | process myList myContext ok |<br>
+        process := self stackTop.<br>
+        process = self activeProcess ifTrue:<br>
+                [self stackTopPut: objectMemory nilObject.<br>
+                 ^self transferTo: self wakeHighestPriority].<br>
+        myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
+        myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
+        ((objectMemory isPointers: myList)<br>
+         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
+         and: [(objectMemory isContext: myContext)<br>
+         and: [self isResumableContext: myContext]]]) ifFalse:<br>
+                [^self primitiveFailFor: PrimErrBadReceiver].<br>
+        ok := self removeProcess: process fromList: myList.<br>
+        ok ifFalse:<br>
+                [^self primitiveFailFor: PrimErrOperationFailed].<br>
+        objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
+        (objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag ifTrue:<br>
+                [self backupContext: myContext toBlockingSendTo: myList].<br>
+        self stackTopPut: myList!<br>
<br>
Item was added:<br>
+ ----- Method: StackInterpreterPrimitives>>primitiveSuspendBackingUpV2 (in category 'process primitives') -----<br>
+ primitiveSuspendBackingUpV2<br>
+        "Primitive #578. Suspend the receiver, aProcess, such that it can be executed again<br>
+         by sending #resume. If the given process is not the active process, take it off<br>
+         its corresponding list. If the list was not its run queue assume it was on some<br>
+         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
+         invoked the wait state the process entered.  Hence when the process resumes<br>
+         it will reenter the wait state. Answer the list the receiver was previously on iff<br>
+         it was not active and not blocked, otherwise answer nil.<br>
+         c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568,<br>
+         which always answer the list the process was on, even if blocked."<br>
+        <export: true><br>
+        | process myList myContext ok |<br>
+        process := self stackTop.<br>
+        process = self activeProcess ifTrue:<br>
+                [self stackTopPut: objectMemory nilObject.<br>
+                 ^self transferTo: self wakeHighestPriority].<br>
+        myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
+        myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
+        ((objectMemory isPointers: myList)<br>
+         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
+         and: [(objectMemory isContext: myContext)<br>
+         and: [self isResumableContext: myContext]]]) ifFalse:<br>
+                [^self primitiveFailFor: PrimErrBadReceiver].<br>
+        ok := self removeProcess: process fromList: myList.<br>
+        ok ifFalse:<br>
+                [^self primitiveFailFor: PrimErrOperationFailed].<br>
+        objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
+        (objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag<br>
+                ifTrue:<br>
+                        [self backupContext: myContext toBlockingSendTo: myList.<br>
+                         self stackTopPut: objectMemory nilObject]<br>
+                ifFalse:<br>
+                        [self stackTopPut: myList]!<br>
<br>
Item was removed:<br>
- ----- Method: StackInterpreterPrimitives>>primitiveSuspendV2 (in category 'process primitives') -----<br>
- primitiveSuspendV2<br>
-        "Primitive. Suspend the receiver, aProcess, such that it can be executed again<br>
-         by sending #resume. If the given process is not the active process, take it off<br>
-         its corresponding list. If the list was not its run queue assume it was on some<br>
-         condition variable (Semaphore, Mutex) and back up its pc to the send that<br>
-         invoked the wait state the process entered.  Hence when the process resumes<br>
-         it will reenter the wait state. Answer the list the receiver was previously on iff<br>
-         it was not active and not blocked, otherwise answer nil.<br>
-         c.f. primitiveSuspend, which always answers the list the process was on, if blocked."<br>
-        <export: true><br>
-        | process myList myContext ok |<br>
-        process := self stackTop.<br>
-        process = self activeProcess ifTrue:<br>
-                [self pop: 1 thenPush: objectMemory nilObject.<br>
-                 ^self transferTo: self wakeHighestPriority].<br>
-        myList := objectMemory fetchPointer: MyListIndex ofObject: process.<br>
-        myContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: process.<br>
-        ((objectMemory isPointers: myList)<br>
-         and: [(objectMemory numSlotsOf: myList) > LastLinkIndex<br>
-         and: [(objectMemory isContext: myContext)<br>
-         and: [self isResumableContext: myContext]]]) ifFalse:<br>
-                [^self primitiveFailFor: PrimErrBadReceiver].<br>
-        ok := self removeProcess: process fromList: myList.<br>
-        ok ifFalse:<br>
-                [^self primitiveFailFor: PrimErrOperationFailed].<br>
-        objectMemory storePointerUnchecked: MyListIndex ofObject: process withValue: objectMemory nilObject.<br>
-        self assert: RevisedSuspend.<br>
-        (RevisedSuspend<br>
-         and: [(objectMemory fetchClassTagOfNonImm: myList) ~= classLinkedListClassTag])<br>
-                ifTrue:<br>
-                        [self backupContext: myContext toBlockingSendTo: myList.<br>
-                         self pop: 1 thenPush: objectMemory nilObject]<br>
-                ifFalse:<br>
-                        [self pop: 1 thenPush: myList]!<br>
<br>
Item was removed:<br>
- ----- Method: StackInterpreterSimulator>>primitiveSuspend (in category 'debugging traps') -----<br>
- primitiveSuspend<br>
-        "Catch errors before we start the whole morphic error process"<br>
- <br>
-        "byteCount > 1000000 ifTrue: [self halt]."  "Ignore early process activity"<br>
-        "self stackTop = (objectMemory fetchPointer: FirstLinkIndex ofObject: (objectMemory splObj: TheFinalizationSemaphore)) ifTrue:<br>
-                [self halt]."<br>
-        ^ super primitiveSuspend!<br>
<br>
Item was changed:<br>
  ----- Method: VMMaker class>>generateVMPlugins (in category 'configurations') -----<br>
  generateVMPlugins<br>
         ^VMMaker<br>
                 generatePluginsTo: self sourceTree, '/src'<br>
                 options: #()<br>
                 platformDir: self sourceTree, '/platforms'<br>
                 including:#(ADPCMCodecPlugin AsynchFilePlugin<br>
                                         BalloonEnginePlugin B3DAcceleratorPlugin B3DEnginePlugin BMPReadWriterPlugin BitBltSimulation<br>
                                         BochsIA32Plugin BochsX64Plugin GdbARMv6Plugin GdbARMv8Plugin<br>
                                         CameraPlugin ClipboardExtendedPlugin CroquetPlugin DeflatePlugin DropPlugin<br>
                                         "Cryptography Plugins:" DESPlugin DSAPlugin MD5Plugin SHA2Plugin<br>
+                                        FileDialogPlugin "FT2Plugin" FFTPlugin FileCopyPlugin FilePlugin FileAttributesPlugin Float64ArrayPlugin FloatArrayPlugin FloatMathPlugin<br>
-                                        "FT2Plugin" FFTPlugin FileCopyPlugin FilePlugin FileAttributesPlugin Float64ArrayPlugin FloatArrayPlugin FloatMathPlugin<br>
                                         GeniePlugin HostWindowPlugin IA32ABIPlugin ImmX11Plugin InternetConfigPlugin<br>
                                         JPEGReadWriter2Plugin JPEGReaderPlugin JoystickTabletPlugin KlattSynthesizerPlugin<br>
                                         LargeIntegersPlugin LocalePlugin MIDIPlugin MacMenubarPlugin Matrix2x3Plugin<br>
                                         MiscPrimitivePlugin Mpeg3Plugin QuicktimePlugin RePlugin<br>
                                         ScratchPlugin SecurityPlugin SerialPlugin SocketPlugin<br>
                                         SoundCodecPlugin SoundGenerationPlugin SoundPlugin SqueakSSLPlugin StarSqueakPlugin<br>
                                         ThreadedFFIPlugin ThreadedARM32FFIPlugin ThreadedARM64FFIPlugin ThreadedIA32FFIPlugin<br>
                                         ThreadedX64SysVFFIPlugin ThreadedX64Win64FFIPlugin<br>
                                         UnicodePlugin UnixAioPlugin UUIDPlugin UnixOSProcessPlugin<br>
                                         Win32OSProcessPlugin VMProfileLinuxSupportPlugin VMProfileMacSupportPlugin WeDoPlugin<br>
                                         XDisplayControlPlugin)!<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
</body>
</html>