Page 2 of 2

Posted: Thu Mar 15, 2007 10:13 pm
by srod
That's what I do as well, and I find it leads to 3 lines of error checking for every 1 line of program logic. Which is sort of boring.
Yes, mine tend to proceed along the following lines so that when an error is flagged, an error report is given automatcally and the main procedure terminates.

Code: Select all

Procedure Main()
  If ReportError(Proc1()) = 0
    If ReportError(Proc2()) = 0
      If ReportError(Proc3()) = 0
        If ReportError(Proc4()) = 0

        ...blah blah

        EndIf
      EndIf
    EndIf
  EndIf
ProcedureReturn

Posted: Mon Mar 19, 2007 8:07 pm
by okasvi
Trond wrote:
the complete processing can be within a thread that will be terminated if an error occurs,
so the level of nesting is no longer a problem and the main-thread isn't touched at all....
I will have to allocate a lot of memory, which will be flushed down the toilet when I use KillThread().
1. OnErrorGosub(@FreeAllocatedMemory())
2. SetErrorNumber($DEADC0DE) or !int3 (?)
3. ExitThread_(0)

:)

Posted: Mon Mar 19, 2007 9:05 pm
by Trond
okasvi wrote:
Trond wrote:
the complete processing can be within a thread that will be terminated if an error occurs,
so the level of nesting is no longer a problem and the main-thread isn't touched at all....
I will have to allocate a lot of memory, which will be flushed down the toilet when I use KillThread().
1. OnErrorGosub(@FreeAllocatedMemory())
2. SetErrorNumber($DEADC0DE) or !int3 (?)
3. ExitThread_(0)

:)
Then I'll have to keep track of all allocated memory.

Posted: Mon Mar 19, 2007 11:28 pm
by okasvi
Well, you can't ask for miracles :P
Code simple wrappers for memory functions to do it, so you don't have to change existing code that much...

Posted: Tue Mar 20, 2007 3:46 am
by Kaeru Gaman
I will have to allocate a lot of memory, which will be flushed down the toilet when I use KillThread().
ermn...
I think the MEM you allocate within the thread will be freed by the OS when you kill the thread.
if you need to keep the MEM, allocate it outside the thread...

besides, I thought you meant "an error" in that way,
that your processing reveals an incoherence or such,
not a real error that should be handled via OnError...
did I mistake you in that point?

Posted: Tue Mar 20, 2007 12:02 pm
by freak
Killing a thread frees NO memory, not even the stack area of the thread! Thats why using KillThread() is highly discouraged.
(should only be used in extreme cases)

Posted: Tue Mar 20, 2007 2:09 pm
by freak
I think what you are looking for is something like setjump() and longjump() in C.

Here is an example:

Code: Select all

; =======================================================================

Structure JumpData
  EAX.l
  ECX.l
  EDX.l
  EBX.l
  ESP.l
  EBP.l
  ESI.l
  EDI.l
  ReturnAddr.l
EndStructure

DisableDebugger

; Stores the current program state in *mark, making it a possible target for a jump
; The procedure returns normally when the jump is set up, and when LongJump() is called,
; the program execution will again return from this procedure and continue there.
;
; Returnvalue:
;   - 0 when called to setup a jump target
;   - 'ReturnValue' from LongJump() after a jump was made back to this location
;
Procedure SetJump(*mark.JumpData)
  !mov eax, [p.p_mark]
  !pop dword [eax+32]
  !add esp, 4
  
  !mov [eax], dword 0
  !mov [eax+4], ecx
  !mov [eax+8], edx
  !mov [eax+12], ebx
  !mov [eax+16], esp
  !mov [eax+20], ebp
  !mov [eax+24], esi
  !mov [eax+28], edi

LongJump_Target:
  !mov edi, [eax+28]
  !mov esi, [eax+24]
  !mov ebp, [eax+20]
  !mov esp, [eax+16]
  !mov ebx, [eax+12]
  !mov edx, [eax+8]
  !mov ecx, [eax+4]
  
  !push dword [eax+32]
  !mov eax, [eax]
  !ret
EndProcedure

; Jumps back to the given *mark. SetJump() will return on the old location with
; the specified 'ReturnValue'.
; The LongJump() procedure never returns to where it was called from.
;
; Note: You can only jump out of procedures, not into them. So when SetJump()
; is called inside a procedure, you can only jump back to it while the execution
; has not yet left that procedure (as its stack frame is then destroyed, making it invalid to jump to)
;
Procedure LongJump(*mark.JumpData, ReturnValue.l)
  *mark\EAX = ReturnValue
  !mov eax, [p.p_mark]
  !jmp l_longjump_target
EndProcedure

EnableDebugger

; =======================================================================
; Example:
; =======================================================================


Global jump.JumpData


Procedure Processing(value)
  Debug "entering procedure: "  + Str(value)
  
  If value = 10
    LongJump(@jump, 1)
  ElseIf Value = 20
    LongJump(@jump, 2)
  Else
    Processing(value+1)
  EndIf    

  Debug "leaving procedure: " + Str(value)
EndProcedure

; Set up the jump position. In this example, the execution will return
; 3 times from this position. First normally, and then each time the LongJump()
; is called
;
Select SetJump(@jump)
  Case 0: Debug "jump mark set"
          Processing(0) ; start our procedure
  
  Case 1: Debug "error 1"
          Debug "trying again from 11"
          Processing(11) ; start the processing again

  Case 2: Debug "error 2"
  ; ...
EndSelect

Debug "program end"
End
Notes:
  • The CallStack display of the Debugger will be wrong when you call this, as the debugger does not notice
    that the procedures returned. (because technically they didn't)
  • This of course does not take care of any allocated memory. (also not local String variables, LinkedLists and Arrays)
    So when using this, you have to handle these things in a different way, or you will get a memory leak.
    (Use global/static variables, or allocate memory blocks for them that you register somewhere so you
    can correctly free them all after a LongJump)
It is not a perfect solution, as it requires extra work to get right, but it works.

Posted: Tue Mar 20, 2007 2:52 pm
by Trond
Thanks, it's what I'm looking for, except for the string problem.