<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 Eliot,</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).</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.
</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.</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:</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This is Andreas's comment in Process >> offList (ar 12/7/2007):</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"> "OBSOLETE. Process>>suspend will atomically reset myList if the process is suspended.
</p>
<p class="MsoNormal"> There should never be a need to send #offList but some older users may not be aware
</p>
<p class="MsoNormal"> of the changed semantics to suspend and may try the old hickadidoo seen here:</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"> (suspendingList := process suspendingList) == nil</p>
<p class="MsoNormal"> ifTrue: [process == Processor activeProcess ifTrue: [process suspend]]</p>
<p class="MsoNormal"> ifFalse: [suspendingList remove: process ifAbsent:[].</p>
<p class="MsoNormal"> process offList].</p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"> Usages like the above should be replaced by a simple 'process suspend' "</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal">What "changed semantics" (on the 3rd line)?? Was there yet another suspend semantics before the current one?
</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.</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.</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 :)</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I'm looking forward to hearing from you.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Best regards,</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: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</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)!<br>
<br>
<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
</body>
</html>