"double free or corruption (fasttop)" using a lib in thread

Linux specific forum
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

"double free or corruption (fasttop)" using a lib in thread

Post by Kukulkan »

Hi,

While the PulseAudio module I posted yesterday (http://www.purebasic.fr/english/viewtop ... 90#p491890) seems to work fine even for an hour, it does not work if I use it in a thread. I run it on Kubuntu 14.04 (64 bit) and compile it using PB 5.24 in 64 bit.

Here is a small example of a program I wrote. I call the module functions all in the thread VUThread() but after a while (between 2 and 40 seconds), the program stops at a random position in the source with Program aborted. (by external library).

The console window in the background is saying
*** Error in `/tmp/purebasic_compilation0.out': double free or corruption (fasttop): 0x00007f1ad00008c0 ***

This is my current code (you need the above linked include for PulseAudio as "PulseAudio_module.pbi" in the same folder):

Code: Select all

; AudioCounter

EnableExplicit

CompilerIf #PB_Compiler_Unicode
  #XmlEncoding = #PB_UTF8
CompilerElse 
  #XmlEncoding = #PB_Ascii
CompilerEndIf

#queue_size = 512
#LineColor = $FFAAAAAA; AABBGGRR

; Include PulseAudio module
; http://www.purebasic.fr/english/viewtopic.php?f=27&t=66240&p=491890#p491890
XIncludeFile "PulseAudio_module.pbi"

Structure vu_thread
  stop.b
  deviceName.s
  mutex.i
EndStructure

Structure vu_queue
  pL.f
  pR.f
  rL.f
  rR.f
  dL.f
  dR.f
EndStructure

Global Dim queue.vu_queue(#queue_size)

Procedure fillDevices(gad.i)
  Protected x.i, line.s, entry.s, lst.s
  ClearGadgetItems(gad.i)
  ;
  Protected proc.i = RunProgram("pactl", "list short sources", "", #PB_Program_Open | #PB_Program_Read)
  If proc.i
    While ProgramRunning(proc.i)
      If AvailableProgramOutput(proc.i)
        lst.s + ReadProgramString(proc.i) + Chr(13)
      EndIf
    Wend
    CloseProgram(proc.i)
  EndIf
  For x.i = 1 To CountString(lst.s, Chr(13))
    line.s = StringField(lst.s, x.i, Chr(13))
    entry.s = Trim(StringField(line.s, 2, Chr(9)))
    AddGadgetItem(gad.i, -1, entry.s)
  Next
EndProcedure

Procedure incQueue()
  Protected x.i
  For x.i = #queue_size - 1 To 0 Step -1
    queue(x+1) = queue(x)
  Next
EndProcedure

Procedure drawQueue(gad.i)
  Protected width.i = GadgetWidth(gad.i)
  Protected height.i = GadgetHeight(gad.i)
  Protected areaHeight.i = height.i / 3
  Protected x.i
  Protected y.f
  Protected pos.f, st.f
  Protected scale.f = 32768 / areaHeight
  
  StartDrawing(CanvasOutput(gad.i))
  DrawingMode(#PB_2DDrawing_AlphaBlend)
  Box(0, 0, width, height, RGBA(255,255,255,255))
  
  st.f = width / #queue_size
  pos.f = 0
  For x.i = 0 To #queue_size - 1
    ; left
    y = queue(x)\rL / scale.f
    LineXY(width - pos.f, areaHeight - y, width - pos.f, areaHeight, RGBA(0,0,255,255))
    ; right
    y = queue(x)\rR / scale.f
    LineXY(width - pos.f, 2 * areaHeight - y, width - pos.f, 2 * areaHeight, RGBA(0,0,255,255))
    ; dir / side
    y = (queue(x)\rR - queue(x)\rL)  / scale.f
    LineXY(width - pos.f, 2.5 * areaHeight - y, width - pos.f, 2.5 * areaHeight, RGBA(255,0,0,255))
    
    pos.f + st.f
  Next
  
  LineXY(0, areaHeight, width, areaHeight, #LineColor)
  LineXY(0, 2 * areaHeight, width, 2 * areaHeight, #LineColor)
  
  StopDrawing()
EndProcedure

Procedure.i VUThread(*values.vu_thread)
  Protected valu.pulse::vu_result ; define return value
  
  *values\stop = #False ; init
  
  If pulse::initLibrary()
    Protected err.i = pulse::initStream("AudioCounter", "record", *values\deviceName)
    If err.i <> 0
      Debug "initStream() error: " + pulse::errToStr(err.i)
      *values\stop = #True ; stop!
    Else
      ; get signals
      Repeat
        pulse::getVU(valu)

        ; move queue and add new value
        LockMutex(*values\mutex)
        incQueue()
        queue(0)\pL = valu\peakLeft
        queue(0)\pR = valu\peakRight
        queue(0)\rL = valu\rmsLeft
        queue(0)\rR = valu\rmsRight
        queue(0)\dL = valu\dbLeft
        queue(0)\dR = valu\dbRight
        UnlockMutex(*values\mutex)
        Delay(5)
      Until *values\stop = #True
      
      pulse::closeStream()
    EndIf
    
  EndIf
  pulse::shutdown()
EndProcedure

Procedure main()
  Protected XML.s, Event.i, evType.i, evGadget.i
  Protected vuThreadId.i
  
  XML  = "<window id='#PB_Any' name='mainWin' text='AudioCounter' flags='#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget'>" +
         "  <vbox expand='item:1'>" +
         "    <hbox expand='item:1'>" +
         "      <canvas name='draw' width='300' height='120'/>" +
         "      <vbox expand='item:2'>" +
         "        <button name='clear' text='clear' />" +
         "        <empty />" +
         "      </vbox>" +
         "    </hbox>" +
         "    <gridbox columns='2' colexpand='item:2'>" +
         "      <text text='Device:' name='lblDevice' flags='#PB_Text_Right'/>" +
         "      <combobox name='device' />" +       
         "    </gridbox>" +
         "  </vbox>" +
         "</window>"
  
  Protected xm.i = CatchXML(#PB_Any, @XML, StringByteLength(XML), 0, #XmlEncoding)
  If XMLStatus(xm.i) <> #PB_XML_Success
    Debug "XML error: " + XMLError(xm) + " (Line: " + XMLErrorLine(xm) + ")"
    ProcedureReturn 0
  EndIf
  Protected dialog.i = CreateDialog(#PB_Any)
  If OpenXMLDialog(dialog.i, xm.i, "mainWin") = 0
    Debug "Dialog error: " + DialogError(dialog.i)
    ProcedureReturn 0
  EndIf
  
  ; fill device list
  fillDevices(DialogGadget(dialog.i, "device"))
  SetGadgetState(DialogGadget(dialog.i, "device"), 1) ; TODO: Use ini file to set and get the ID
  
  ; start VU thread
  Static p.vu_thread
  p\deviceName = GetGadgetText(DialogGadget(dialog.i, "device"))
  p\mutex = CreateMutex()
  vuThreadId.i = CreateThread(@VUThread(), p)
  
  ; main GUI loop
  Repeat
    Event = WaitWindowEvent(10)
    If Event = #PB_Event_Gadget
      evType.i = EventType()
      evGadget.i = EventGadget()
      
      If evGadget = DialogGadget(dialog.i, "device") And evType.i = #PB_EventType_Change
        ; Stop running thread
        p\stop = #True
        WaitThread(vuThreadId.i, 2000)
        If IsThread(vuThreadId.i): KillThread(vuThreadId.i): EndIf ; terminate if not auto-closing in 2 sec
        ; Start thread with new chosen device
        p\deviceName = GetGadgetText(DialogGadget(dialog.i, "device"))
        vuThreadId.i = CreateThread(@VUThread(), p)
      EndIf
      
      If evGadget = DialogGadget(dialog.i, "clear") And evType.i = #PB_EventType_LeftClick
        Debug "Clear"
      EndIf
      
    EndIf
    If Event = #PB_Event_SizeWindow
      
    EndIf
    
    LockMutex(p\mutex)
    drawQueue(DialogGadget(dialog.i, "draw"))
    UnlockMutex(p\mutex)
    
  Until Event = #PB_Event_CloseWindow
  
  ; Shutdown and cleanup
  p\stop = #True
  WaitThread(vuThreadId.i, 1000)

EndProcedure

main()
Any idea what I'm doing wrong?