C'est SetGadgetText qui abime le buffer text

Archive.
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

C'est SetGadgetText qui abime le buffer text

Message par ZapMan »

J'avais soupçonné injustement ReceiveNetworkData. Finalement, c'est SetGadgetText qui était responsable de mes ennuis. En voici la démonstration :

Code : Tout sélectionner

; Démonstration d'un curieux bug de SetGadgetText
;
; Avec Zapman dans le rôle d'Emile Zola (celui qui accuse)
;
; Le bug démontré est alléatoire et peut ne se produire qu'au bout d'un long moment.
;
; Quand il survient, un fenêtre portant le titre "Ouch !" apparait.
;
; Si le bug ne se produit pas chez vous, faites passez une autre application
; devant cette là et refaites passer celle là au premier plan, ou alors laissez tourner le programme en arrière plan et patientez

Global temoin$
Global Mode$

Mode$ = "Unsafe" ; Mode$ = "Safe"

Enumeration
  #Defiltext1
  #Defiltext2
  #CheckMode
EndEnumeration
;
Procedure SetGadgetTextSafe(NoGadget,Txt$)
; Sous cette forme, ça ne marche pas, et cette procédure safe porte mal son nom
; ne la recopiez pas dans vos programmes
  SendMessage_(GadgetID(NoGadget), #WM_SETTEXT, 0, @Txt$)
EndProcedure
;
Procedure DefilThread1(txdefil1$)
  Repeat
    txdefil1$ = Right(txdefil1$,Len(txdefil1$)-1)+Left(txdefil1$,1)
    If Mode$ = "Safe"
      Txt$ = Left(txdefil1$,36)
      SendMessage_(GadgetID(#Defiltext1), #WM_SETTEXT, 0, @Txt$) ; comme ça, ça marche
    Else
      SetGadgetText(#Defiltext1,Left(txdefil1$,36)) ; !!!!!!!! create a thread bug with text buffer because PB is not thread safe ?
    EndIf
    Delay(150)
  ForEver
EndProcedure
;
Procedure.s RotateLeft(text$)
  Delay(50) ; sans ce petit délai, le bug ne se produit quasimment jamais
  text$ = Right(text$,1)+Left(text$,Len(text$)-1)
  temoin$ = text$
  ProcedureReturn temoin$
EndProcedure
;
Procedure.s VerifieRotateLeft(text$)
  text$ = RotateLeft(text$)
  If text$<>temoin$
    MessageRequester("Ouch !!!",text$+Chr(13)+temoin$,0)
  EndIf
  ProcedureReturn temoin$
EndProcedure
;
Procedure DefilThread2(txdefil2$)
  Repeat
    txdefil2$ = VerifieRotateLeft(txdefil2$)
    If Mode$ = "Safe"
      Txt$ = Left(txdefil2$,36)
      SendMessage_(GadgetID(#Defiltext2), #WM_SETTEXT, 0, @Txt$) ; comme ça, ça marche
    Else
      SetGadgetText(#Defiltext2,Left(txdefil2$,36)) ; !!!!!!!! create a thread bug with text buffer because PB is not thread safe ?
    EndIf
    Delay(100)
  ForEver
EndProcedure

LargeurFenetre = 220
hWnd=OpenWindow(#Pb_Any,100,200,LargeurFenetre,80,#PB_Window_TitleBar|#PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget,"Démo de bug") 
If hWnd=0 Or CreateGadgetList(WindowID(hWnd))=0:End:EndIf
posH = 15
TextGadget(#Defiltext1,10,posH,LargeurFenetre-20,14,"")
posH + 20
TextGadget(#Defiltext2,10,posH,LargeurFenetre-20,14,"")
posH + 20
CheckBoxGadget(#CheckMode, 10,posH, LargeurFenetre-20, 14, "Safe mode")
;
CreateThread(@DefilThread2(),UCase("Démonstration du bug créé par SetGadgetText - "))
CreateThread(@DefilThread1(),"SetGadgetText abime les données du buffer text - ")
Quit = 0
Repeat
  UseWindow(hWnd)
  UseGadgetList(WindowID())
  EventID = WindowEvent()
  If EventID=#PB_EventCloseWindow
    Quit = 1
  ElseIf EventID=#PB_EventGadget
    If EventGadgetID()=#CheckMode
      If GetGadgetState(#CheckMode)
        Mode$ = "Safe"
      Else
        Mode$ = "UnSafe"
      EndIf
      Debug Mode$ 
    EndIf
  EndIf
  Delay(50)
Until Quit
Je peux dire que ça m'en a fait baver.

Merci de tester ça chez vous pour me dire ce que ça donne.

Et en bonus, un autre bug détecté chez GetGadgetText : quand le texte à récupérer dans le gadget a une longueur supérieure à 32 000, il est tronqué au 32 000 premiers caractères.

Voici une version corrigée de GetGadgetText :

Code : Tout sélectionner

#MaxSize=1000000
;
Procedure SetStringManipulationBufferSize(Bytes)
 ; Ca, c'est une procédure proposée par Fred
  PBStringBase.l = 0
  PBMemoryBase.l = 0
  !MOV eax, dword [PB_StringBase]
  !MOV [esp+4],eax
  !MOV eax, dword [PB_MemoryBase]
  !MOV [esp+8],eax
  HeapReAlloc_(PBMemoryBase, #GMEM_ZEROINIT, PBStringBase, Bytes)
  !MOV dword [_PB_StringBase],eax
EndProcedure

; Set the buffer size for all strings to #MaxSize.
SetStringManipulationBufferSize(#MaxSize)



Procedure.s XGetGadgetText(Gadget) ; pour les textes de plus 32000 caractères
  ; By Zapman
  *ADR = AllocateMemory(#MaxSize)
  SendMessage_(GadgetID(Gadget), #WM_GETTEXT, #MaxSize, *ADR) ;Get the Content
  tx2$ = PeekS(*ADR)
  FreeMemory(*ADR)
  ProcedureReturn tx2$
EndProcedure
Dernière modification par ZapMan le sam. 06/août/2005 16:39, modifié 3 fois.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

heu c'est sensé faire quoi le bug ?? 8O

chez moi une petit fenetre avec du text qui defile !!
si je passe japbe devant , je vois plus la fenetre (normal non ?)
si je rapelle la fenetre , le text defile toujours dedans !! :?
bref ! rien d'anormal !
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

C'est un bug alléatoire. Ca n'arrive que de temps à autre. Laisse le soft tourner en arrière plan. Au bout d'un moment (que je ne saurais déterminer...), tu devrais avoir une fenêtre avec le titre "Ouch !" qui apparaît au centre de l'écran. C'est le signal que le bug s'est produit.

Il suffit ensuite de regarder le code qui ouvre cette fenêtre (procédure VerifieRotateLeft()) pour comprendre la nature du bug.

J'ai reproduit, avec ce code simplifié, un bug dont DropUpLoad était victime. Et je t'assure que je n'étais pas le seul à m'en plaindre !!

Tu peux en faire une version compilée et la laisser tourner jusqu'à ce que ça se produise.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Je viens d'augmenter le delay() qui est en première ligne de la procédure RotateLeft(). Et là, chez moi, ça bug en permanence.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

ok !! j'ai eu le problemme !! :?
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

essaye cette version !! :D
chez moi ça a l'air de tenir mem avec un delay() long !!

regarde la variable dobro !! c'est elle qui conditione l'execution des treads !

Code : Tout sélectionner

; Démonstration d'un curieux bug de SetGadgetText
;
; Avec Zapman dans le rôle d'Emile Zola (celui qui accuse)
;
; Le bug démontré est alléatoire et peut ne se produire qu'au bout d'un long moment.
;
; Quand il survient, un fenêtre portant le titre "Ouch !" apparait.
;
; Si le bug ne se produit pas chez vous, faites passez une autre application
; devant cette là et refaites passer celle là au premier plan, ou alors laissez tourner le programme en arrière plan et patientez

Global temoin$
Global Mode$ , dobro

Mode$ = "Unsafe" ; Mode$ = "Safe"

Enumeration
        #Defiltext1
        #Defiltext2
        #CheckMode
EndEnumeration
;
Procedure SetGadgetTextSafe(NoGadget,Txt$)
        ; Sous cette forme, ça ne marche pas, et cette procédure safe porte mal son nom
        ; ne la recopiez pas dans vos programmes
        SendMessage_(GadgetID(NoGadget), #WM_SETTEXT, 0, @Txt$)
EndProcedure
;
Procedure DefilThread1(txdefil1$)
        Repeat
                If dobro=0
                txdefil1$ = Right(txdefil1$,Len(txdefil1$)-1)+Left(txdefil1$,1)
                If Mode$ = "Safe"
                        Txt$ = Left(txdefil1$,36)
                        SendMessage_(GadgetID(#Defiltext1), #WM_SETTEXT, 0, @Txt$) ; comme ça, ça marche
                Else
                        SetGadgetText(#Defiltext1,Left(txdefil1$,36)) ; !!!!!!!! create a thread bug with text buffer because PB is not thread safe ?
                EndIf
                Delay(1)
                dobro=1
                EndIf
        ForEver
EndProcedure
;
Procedure.s RotateLeft(text$)
        Delay(100) ; sans ce petit délai, le bug ne se produit quasimment jamais
        text$ = Right(text$,1)+Left(text$,Len(text$)-1)
        temoin$ = text$
          dobro=1
        ProcedureReturn temoin$
EndProcedure
;
Procedure.s VerifieRotateLeft(text$) 
        text$ = RotateLeft(text$)
        If text$<>temoin$
                MessageRequester("Ouch !!!",text$+Chr(13)+temoin$,0)
        EndIf
      
        ProcedureReturn temoin$
EndProcedure
;
Procedure DefilThread2(txdefil2$)
        Repeat
                If dobro=1
                txdefil2$ = VerifieRotateLeft(txdefil2$)
                If Mode$ = "Safe"
                        Txt$ = Left(txdefil2$,36)
                        SendMessage_(GadgetID(#Defiltext2), #WM_SETTEXT, 0, @Txt$) ; comme ça, ça marche
                Else
                        SetGadgetText(#Defiltext2,Left(txdefil2$,36)) ; !!!!!!!! create a thread bug with text buffer because PB is not thread safe ?
                EndIf
                Delay(100)
                dobro=0
        EndIf
        
        ForEver
EndProcedure

LargeurFenetre = 220
hwnd=OpenWindow(#PB_Any,100,200,LargeurFenetre,80,#PB_Window_TitleBar|#PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget,"Démo de bug")
If hwnd=0 Or CreateGadgetList(WindowID(hwnd))=0:End:EndIf
posH = 15
TextGadget(#Defiltext1,10,posH,LargeurFenetre-20,14,"")
posH + 20
TextGadget(#Defiltext2,10,posH,LargeurFenetre-20,14,"")
posH + 20
CheckBoxGadget(#CheckMode, 10,posH, LargeurFenetre-20, 14, "Safe mode")
;
CreateThread(@DefilThread2(),UCase("Démonstration du bug créé par SetGadgetText - "))
CreateThread(@DefilThread1(),"SetGadgetText abime les données du buffer text - ")
Quit = 0
Repeat
        UseWindow(hwnd)
        UseGadgetList(WindowID())
        EventID = WindowEvent()
        If EventID=#PB_EventCloseWindow
                Quit = 1
        ElseIf EventID=#PB_EventGadget
                If EventGadgetID()=#CheckMode
                        If GetGadgetState(#CheckMode)
                                Mode$ = "Safe"
                        Else
                                Mode$ = "UnSafe"
                        EndIf
                        Debug Mode$
                EndIf
        EndIf
        Delay(50)
Until Quit 

Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Oui, évidemment ! Avec ta variable, on est plus vraiment en multi-thread. Or c'est bien en multi-thread que le problème survient.
Ta démonstration ne change rien à la nature du bug. Quant à trouver une solution pour que l'ensemble fonctionne, je l'avais déjà fais avec mon mode safe :D

Si d'autres on fait l'essai, j'aimerais bien avoir la confirmation que ça bug chez tout le monde.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

confirmé

Dri :-?
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Oui ça bug très rapidement chez moi

XP édition familiale, SP2 et toutes les mises à jour, PB 3.94 (la dernière beta)
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

ah bah pour préciser, toujours win98 se et avec la beta 4 de la 3.94

Dri
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Je pense que l'erreur correspond à ce que dit la doc sur les Threads
Note: Les threads doivent être utilisés avec précaution, car il est possible que des ressources partagées (mémoire, fichiers, variables etc..) soient accédées au même moment ce qui causera des resultats aléatoires. Par exemple, il n'est pas possible de manipuler des chaines de caracteres dans differents threads au même moment, car un seul buffer est disponible pour leur gestion dans PureBasic. Il faut donc prendre des mesures (a l'aide de mutexs) pour eviter ce genre de situations. Par contre, la lecture des chaines de caractere ne pose pas de probleme.
Oliv
Messages : 2117
Inscription : mer. 21/janv./2004 18:39

Message par Oliv »

C'est quasi-instantané chez moi
gansta93
Messages : 1448
Inscription : jeu. 26/févr./2004 11:17
Localisation : Le Village
Contact :

Message par gansta93 »

En parlant de 3.94, toujours pas de nouvelles ?
Je testerais ça...
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Denis a écrit :Je pense que l'erreur correspond à ce que dit la doc sur les Threads
Cette partie de la doc aurait pu être rédigée par Microsoft #aieaieaie !
Si tu la prends au pied de la lettre, autant dire qu'il ne faut PAS utiliser les threads ou alors seulement pour faire des animations de style horloge à quartz ou poupée qui danse. Il me semble que PureBasic n'en est plus la !!
Le consensus qui semblait se dégager sur les forums était que tout allait bien tant qu'on ne travaillait pas à plusieurs threads sur des variables GLOBALES, et je comprends bien la logique du sémaphore (ou mutex) dans ce cas (comme la variable de Dobro, ci-dessus). Cela dit, si le sémaphore est la solution, ne serait-il pas plus simple de l'implanter dans le language lui-même ?

En tout cas, cet exemple montre que le fait de ne pas travailler sur des globales ne suffit pas tout à fait à sécuriser le programme. S'agit-il du seul autre cas de problème ? La question peut faire peur car ce type de bug est horriblement long et compliqué à identifier. Ca n'a l'air de rien, comme ça, une fois que la démo est faite, mais c'est carrément pas simple de cerner le problème quand il survient de façon alléatoire dans un multithread de 2000 lignes. Personnellement, je considère que c'est un des pires trucs qui puisse arriver :mad:

De façon extrèmement alléatoire, ce bug amène parfois la machine à planter purement et simplement, sans messages (pire ! ça ferme le fenêtre du debugger). C'est le genre de truc qui me rends fou. A ce sujet, je regrette beaucoup que le plantage sévère qui survient quand on travaille sur des chaines de plus de 32000 caractères (sans avoir pris les précautions nécessaires) ne soit pas géré de façon plus propre : ça fait plus d'un an qu'on en parle !! Va-t-il falloir attendre qu'il y ait des morts ?

Voilà, pardon si j'ai été un peu sec dans cet exposé, mais je dois avouer que cette affaire m'a un peu mis les nerfs en pelote. J'y ai passé 2 jours et 2 nuits et je n'avais pas besoin de ça.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Fred
Site Admin
Messages : 2808
Inscription : mer. 21/janv./2004 11:03

Message par Fred »

C'est vraiment que lorsqu'on utilise des strings et des threads, ca ne fonctionne pas correctement. Désolé pour la prise de tête, on a prévu de remettre tout ca a plat pour la v4..
Répondre