How to solve random invalid memory access in thread?
Posted: Mon Apr 29, 2013 5:30 am
Hi,
I got random invalid memory access when I try to use a thread in my program that communicate through serial port. When there is no thread the program seems to work ok but the program interface is not responsive when I stress it with a lot of communication. That is why I want to implement a thread. Here is part of the code for you guys to help.
I hope the code shown above could give clues on the problem. I have no clue why this happen. Is it I cannot update gadgets in a thread.
Thanks for giving any idea in advance.
I got random invalid memory access when I try to use a thread in my program that communicate through serial port. When there is no thread the program seems to work ok but the program interface is not responsive when I stress it with a lot of communication. That is why I want to implement a thread. Here is part of the code for you guys to help.
Code: Select all
Structure SerialSetting
SerCommPort.i
BaudRate.i ;
Parity.i
DataBit.i
StopBit.i
HandshakeMode.i
InputBufferSize.i
OutputBufferSize.i
Port.s ;Com port as string
EndStructure
Structure BCRaw
TimeStamp.i
Flow.d
Array BC.d(0)
Array ATN.d(0)
EndStructure
Procedure HandleSerialData(*Value) ; A thread to handle the serial port data coming in
Shared StopFlag.i,DataReceived.i
Shared PortCfg
StopFlag =#False ; Turn on the monitor loop
Define ByteReceived.i
;Max character input rate is 14400 CPS @115200 bauds 7 databit And 1 stopbit, no parity check And no handshaking
;If the set delay is 30 millisec, each cycle will have at most 480CPS, 512 byte will be sufficient
Shared *Buffer
Define TempStr.s
If IsSerialPort(PortCfg\SerCommPort) ; Ensure comm port is open properly
Repeat
ByteReceived =AvailableSerialPortInput(PortCfg\SerCommPort)
;
While ByteReceived
DataReceived =#True
If ByteReceived > 512
ByteReceived = 512
EndIf
If ReadSerialPortData(PortCfg\SerCommPort, *Buffer, ByteReceived) ; Read at most 512 characters
ProcessData(*Buffer, ByteReceived) ; Send to the data parasic routine
Else
;Error needs to be addressed during the port reading operation
TempStr = HandleSerialPortError()
If TempStr =""
UpdateStatusLable("Unknown Error occurred")
Else
UpdateStatusLable(TempStr)
EndIf
StopFlag = #True
Break
EndIf
ByteReceived =AvailableSerialPortInput(PortCfg\SerCommPort)
Wend
DataReceived=#False ; Turn off the DataReceived light so to give better visual representation to user - flashing
Delay(30) ; Wait to see whether there is further input from the communication port
ByteReceived =AvailableSerialPortInput(PortCfg\SerCommPort)
Until StopFlag Or ByteReceived =0
DataReceived=#False
EndIf
; Thread running completed means no more data need to process for the time being
EndProcedure
Procedure ProcessData(*Buffer, BufferLength)
Static CarryOverStr.s
Define EndPosition.i, NumOfField.i
Define TempStr.s, LocalStr.s,ReportStr.s
Shared CaptureFile, CaptureState, UpdateFlag.i,DataIndex,UBData
CarryOverStr= CarryOverStr+ PeekS(*Buffer,BufferLength)
If ErrorFlag =#False ; This flag control to raise the error only once
If CarryOverStr="" And BufferLength >0
ErrorFlag = #True
UpdateStatusLable("Wrong communication setting with device such as baud rate, stop bit...Please check.")
EndIf
EndIf
EndPosition = FindString(CarryOverStr,#LF$ )
While EndPosition <> 0;if endoposition <>0
TempStr = Left(CarryOverStr, EndPosition-1) ; Remove the LineFeed
TempStr = RTrim(TempStr,#CR$) ;Remove the carriage return at the end if it is present
EndPosition = EndPosition +1
If EndPosition <Len(CarryOverStr) ; The carryoverStr has characters after the linefeed
CarryOverStr =Mid(CarryOverStr,EndPosition) ; Cut the process string and leave the remains
Else
CarryOverStr="" ; otherwise clear the string
EndIf
; Write Data to disk
If CaptureState =#True
WriteToDiskN(CaptureFile, TempStr)
EndIf
;updatedisplay
AddGadgetItem(#ScrollText,-1, Tempstr ) ; Add the line at the end
;Parase the Data
NumOfField =CountString(TempStr,",")
Select InstrumentType
Case #AE1X
If NumOfField =9
With BCData(DataIndex)
\TimeStamp = BCTimeStampToDate(StringField(TempStr,1,","),StringField(TempStr,2,","))
ReportStr =FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss", BCData(DataIndex)\TimeStamp)
LocalStr = StringField(TempStr,3,",")
ReportStr = ReportStr +Chr(10) + LocalStr
\BC(0) =Val(LocalStr)
UpdateBCPanel(LocalStr) ; Update the panel with BC value
LocalStr = StringField(TempStr,4,",")
ReportStr = ReportStr + Chr(10) + LocalStr
\Flow = Val(LocalStr)
LocalStr = StringField(TempStr,10,",")
ReportStr = ReportStr + Chr(10) + LocalStr
\ATN(0) = Val(LocalStr)
EndWith
EndIf
Case #AE2X
;If ArraySize(BCData(DataIndex)\BC()) =1
With BCData(DataIndex)
\TimeStamp = BCTimeStampToDate(StringField(TempStr,1,","),StringField(TempStr,2,","))
ReportStr =FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss", BCData(DataIndex)\TimeStamp)
LocalStr = StringField(TempStr,3,",")
ReportStr = ReportStr +Chr(10) + LocalStr
\BC(0) =Val(LocalStr)
UpdateBCPanel(LocalStr) ; Update the panel with BC value and it is store in \BC(0)
LocalStr = StringField(TempStr,4,",")
ReportStr = ReportStr +Chr(10)+ LocalStr
\BC(1) = Val(LocalStr)
LocalStr = StringField(TempStr,5,",")
ReportStr = ReportStr +Chr(10) + LocalStr
\Flow = Val(LocalStr)
LocalStr = StringField(TempStr,11,",")
ReportStr = ReportStr +Chr(10) + LocalStr
\ATN(0) = Val(LocalStr)
LocalStr = StringField(TempStr,17,",")
ReportStr = ReportStr +Chr(10) + LocalStr
\ATN(1) = Val(LocalStr)
EndWith
;Else
If ErrorFlag=#False
UpdateStatusLable("Data structure is not set up for AE2X. Please reset the configuration")
EndIf
;EndIf
Case #AE3X
Define ICounter.i
With BCData(DataIndex)
;If ArraySize(\BC()) <5
\TimeStamp = BCTimeStampToDate(StringField(TempStr,1,","),StringField(TempStr,2,","))
ReportStr =FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss", BCData(DataIndex)\TimeStamp)
For ICounter= 0 To 6
LocalStr = StringField(TempStr,3+ICounter,",")
ReportStr = ReportStr + Chr(10) + LocalStr
\BC(ICounter) =Val(LocalStr)
Next
LocalStr = StringField(TempStr,10,",")
ReportStr = ReportStr + Chr(10) + LocalStr
\Flow = Val(LocalStr)
For ICounter= 0 To 6
LocalStr = StringField(TempStr,16+ICounter*6,",")
ReportStr = ReportStr +Chr(10)+ LocalStr
\ATN(ICounter) =Val(LocalStr)
Next
UpdateBCPanel(StrSciD(\BC(5),9,8)) ; The sixth record stores the bc value
;Else
If ErrorFlag=#False
UpdateStatusLable("Data structure is not set up for AE3X. Please reset the configuration")
ErrorFlag =#True
EndIf
;EndIf
EndWith
EndSelect
UpdateListData(ReportStr)
UpdateFlag.i =#True
;update database
DataIndex =(DataIndex +1)%(UBData+1) ;Rolling buffer
EndPosition = FindString(CarryOverStr,#LF$)
Wend
EndProcedure
Procedure UpdateStatusLable(UpdatedMsg.s)
#UBIndex = 4
Static Dim SystemMsg.s(#UBIndex-1)
Static Index.i
Define Counter.i
SystemMsg(Index)= GetCurrentTimeStamp(Date(),#TimeOnly) + Space(3) + UpdatedMsg
UpdatedMsg=""
For Counter =1 To #UBIndex
UpdatedMsg=UpdatedMsg + SystemMsg((index + Counter)%(#UBIndex)) + #CRLF$
Next
Index =((index+1 )%#UBIndex) ;
SetGadgetText(#LblStatus, UpdatedMsg)
EndProcedure
Procedure ChangeInstrumentType(InstrumentType.i)
Define ICounter.i
Shared UBData
If InstrumentType = #AE3X
For ICounter = 0 To UBData
ReDim BCData(ICounter)\BC(6) ; Total seven parameters
ReDim BCData(ICounter)\ATN(6)
Next
ElseIf InstrumentType = #AE2X
For ICounter = 0 To UBData
ReDim BCData(ICounter)\BC(1) ; Total Two parameters
ReDim BCData(ICounter)\ATN(1)
Next
Else
;InstrumentType =#OptionSingle
For ICounter = 0 To UBData
ReDim BCData(ICounter)\BC(0) ; only one parameters
ReDim BCData(ICounter)\ATN(0)
Next
EndIf
AethalometerType = InstrumentType
ReconfigListData()
EndProcedure
Procedure WindowMain_Events(EventIs.i)
Shared WindowTop, WindowLeft,DCDStatus, WindowCloseFlag.i, FilePattern,PatternSelected,iniFile,AppDirectory.s
Define EventTimerIs.i
Static CheckCount.i ; This must be static for checking the status
Select EventIs
Case #WaitEventTimeout ; this is equal to 0 as timeout in waitevent return zero
If IsThread(HandleSerialThreadID)=#NotRunning ; When there is no more serial data is being process
;Check the serial port for any incoming data
CheckSerialData()
Else
; There is still have serial data keep coming in, refresh the screen content to reflect the change
UpdateScreen()
EndIf
Case #PB_Event_MoveWindow
WindowLeft = WindowX(#MainWindow)
WindowTop = WindowY(#MainWindow)
Case #PB_Event_Menu
Select EventMenu() ; To see which menu has been selected
Case #MenuOpen
iniFile =AppDirectory + "Instrument.ini" ; set initial file+path to display
FilePattern = "Configuration File (*.ini)|*.ini|All files (*.*)|*.*"
PatternSelected = 0 ; use the first of the possible patterns as standard
iniFile = OpenFileRequester("Please choose file to open", iniFile, FilePattern, PatternSelected)
If iniFile ; file has been selected
If Loadini(iniFile)
UpdateStatusLable("Configuration file " + GetFilePart(iniFile) +" is loaded")
SysUpdateFlag=#False
Else
UpdateStatusLable("Failed to load "+GetFilePart(iniFile)+ " configuration. Default setting will be used")
EndIf
Else
UpdateStatusLable("Cancel loading new configuration file")
EndIf
Case #MenuSave
If Saveini()
UpdateStatusLable("Configuration file saved")
Else
UpdateStatusLable("Failed to save configuration file")
EndIf
Case #MenuSaveAs
iniFile =AppDirectory + "Instrument.ini" ; set initial file+path to display
FilePattern = "Configuration File (*.ini)|*.ini|All files (*.*)|*.*"
PatternSelected = 0 ; use the first of the possible patterns as standard
iniFile = SaveFileRequester("Please choose file to save", iniFile, FilePattern, PatternSelected)
If iniFile
If SaveINI(iniFile)
UpdateStatusLable("Configuration file saved as " + GetFilePart(iniFile))
Else
UpdateStatusLable("Failed To save configuration file as " + GetFilePart(iniFile))
EndIf
Else
UpdateStatusLable("Cancel save operation for the configuration file")
EndIf
Case #MenuExit
WindowCloseFlag=#True
Case #MenuGeneralSetting
OpenSettingWindow()
Case #MenuGraphics
Case #MenuConnect
Connect(@PortCfg)
UpdateOperationLable()
Case #MenuDisConnect
CloseSerialConnection(PortCfg\SerCommPort)
UpdateOperationLable()
Case #MenuRecord
SetGadgetState(#ButtonRecord, #Pressed)
DisableMenuItem(#MenuMainWindow,#MenuRecord,#Disable)
DisableMenuItem(#MenuMainWindow,#MenuStop,#Enable)
CaptureState=#True
UpdateOperationLable()
Case #MenuStop
SetGadgetState(#ButtonRecord,#Unpressed)
DisableMenuItem(#MenuMainWindow,#MenuRecord,#Enable)
DisableMenuItem(#MenuMainWindow,#MenuStop,#Disable)
CaptureState=#False
UpdateOperationLable()
Case #MenuAbout ; About
MessageRequester("About", "Under Construction", 0)
Default
EndSelect
Case #PB_Event_Gadget
HandleEventGadget()
Case #PB_Event_Timer
EventTimerIs = EventTimer()
If EventTimerIs = #ClockTimer
CheckTime()
CheckCount= Mod(CheckCount +1, 5)
If CheckCount = 0 ; Means check ~ every 2.5 secs.
If IsSerialPort(PortCfg\SerCommPort)
DCDStatus = GetSerialPortStatus(PortCfg\SerCommPort,#PB_SerialPort_DCD)
EndIf
EndIf
UpdateScreen()
EndIf
Case #PB_Event_CloseWindow
WindowCloseFlag =#True
EndSelect
EndProcedure
Thanks for giving any idea in advance.