View Issue Details

IDProjectCategoryView StatusLast Update
0038306FPCFCLpublic2021-01-27 16:20
ReporterThomas Bulla Assigned ToMichael Van Canneyt  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.2.0 
Fixed in Version3.3.1 
Summary0038306: gqueue unit memory access error
DescriptionHello,

the gqueue unit make problems. If I take a big queue it takes memory access error. I think it is in the IncreaseCapacity routine. I have build a little program to demonstrate the problem.
Additional Informationprogram gqueue_test;

uses
  gqueue;

type
  TIntQueue = specialize TQueue<Integer>;

var
  IntQueue: TIntQueue;
  PushCnt: Integer;

procedure Push2Pop1;
var
  i: Integer;
begin
  for i:= 0 to 1000000 do begin
    IntQueue.Push(PushCnt);
    inc(PushCnt);
    IntQueue.Push(PushCnt);
    inc(PushCnt);
    IntQueue.Pop();
  end;
end;

var
  i: Integer;
begin
  try
    IntQueue:= TIntQueue.Create;
    Push2Pop1;
    WriteLn('Ready');
    ReadLn;
  finally
    IntQueue.Free;
  end;
end.
TagsNo tags attached.
Fixed in Revision48405
FPCOldBugId
FPCTarget-
Attached Files

Relationships

child of 0034420 closedMichael Van Canneyt More memory efficient TDeque.IncreaseCapacity 

Activities

Thomas Bulla

2021-01-04 14:15

reporter  

gqueue_test.lpr (528 bytes)   
program gqueue_test;

uses
  gqueue;

type
  TIntQueue = specialize TQueue<Integer>;

var
  IntQueue: TIntQueue;
  PushCnt: Integer;

procedure Push2Pop1;
var
  i: Integer;
begin
  for i:= 0 to 1000000 do begin
    IntQueue.Push(PushCnt);
    inc(PushCnt);
    IntQueue.Push(PushCnt);
    inc(PushCnt);
    IntQueue.Pop();
  end;
end;

var
  i: Integer;
begin
  try
    IntQueue:= TIntQueue.Create;
    Push2Pop1;
    WriteLn('Ready');
    ReadLn;
  finally
    IntQueue.Free;
  end;
end.

gqueue_test.lpr (528 bytes)   

Bart Broersma

2021-01-04 16:11

reporter   ~0128072

Last edited: 2021-01-04 16:27

View 4 revisions

When i = 262143 a call to TDeque.IncreaseCapacity is made and it tries to increase capacity from $40000 to $48000.
In this loop:
for i:=0 to FStart-1 do
      FData[OldEnd+i]:=FData[i];
a range check error occurs.

It tries to do: FData[$48000]:=FData[$8000], but High(FData)=$47FFF.

Range check is off in this unit (by default) and then the next Push() operation triggers an Invalid pointer operation.

Notice that up to that time, TDeque.IncreaseCapacity was called 17 times already without it triggering any fault.

Bart Broersma

2021-01-04 17:00

reporter   ~0128076

Last edited: 2021-01-04 17:01

View 2 revisions

This is the sequence of increasing capacity:
TDeque.IncreaseCapacity: 00004 to 00008, FStart=00003, OldEnd=00004, OldEnd+FStart-1=00006, High(FData)=00007
TDeque.IncreaseCapacity: 00008 to 00010, FStart=00007, OldEnd=00008, OldEnd+FStart-1=0000E, High(FData)=0000F
TDeque.IncreaseCapacity: 00010 to 00020, FStart=0000F, OldEnd=00010, OldEnd+FStart-1=0001E, High(FData)=0001F
TDeque.IncreaseCapacity: 00020 to 00040, FStart=0001F, OldEnd=00020, OldEnd+FStart-1=0003E, High(FData)=0003F
TDeque.IncreaseCapacity: 00040 to 00080, FStart=0003F, OldEnd=00040, OldEnd+FStart-1=0007E, High(FData)=0007F
TDeque.IncreaseCapacity: 00080 to 00100, FStart=0007F, OldEnd=00080, OldEnd+FStart-1=000FE, High(FData)=000FF
TDeque.IncreaseCapacity: 00100 to 00200, FStart=000FF, OldEnd=00100, OldEnd+FStart-1=001FE, High(FData)=001FF
TDeque.IncreaseCapacity: 00200 to 00400, FStart=001FF, OldEnd=00200, OldEnd+FStart-1=003FE, High(FData)=003FF
TDeque.IncreaseCapacity: 00400 to 00800, FStart=003FF, OldEnd=00400, OldEnd+FStart-1=007FE, High(FData)=007FF
TDeque.IncreaseCapacity: 00800 to 01000, FStart=007FF, OldEnd=00800, OldEnd+FStart-1=00FFE, High(FData)=00FFF
TDeque.IncreaseCapacity: 01000 to 02000, FStart=00FFF, OldEnd=01000, OldEnd+FStart-1=01FFE, High(FData)=01FFF
TDeque.IncreaseCapacity: 02000 to 04000, FStart=01FFF, OldEnd=02000, OldEnd+FStart-1=03FFE, High(FData)=03FFF
TDeque.IncreaseCapacity: 04000 to 08000, FStart=03FFF, OldEnd=04000, OldEnd+FStart-1=07FFE, High(FData)=07FFF
TDeque.IncreaseCapacity: 08000 to 10000, FStart=07FFF, OldEnd=08000, OldEnd+FStart-1=0FFFE, High(FData)=0FFFF
TDeque.IncreaseCapacity: 10000 to 20000, FStart=0FFFF, OldEnd=10000, OldEnd+FStart-1=1FFFE, High(FData)=1FFFF
TDeque.IncreaseCapacity: 20000 to 40000, FStart=1FFFF, OldEnd=20000, OldEnd+FStart-1=3FFFE, High(FData)=3FFFF
TDeque.IncreaseCapacity: 40000 to 48000, FStart=3FFFF, OldEnd=40000, OldEnd+FStart-1=7FFFE, High(FData)=47FFF: OldEnd+i > High(FData): 48000 > 47FFF

The loop tries to copy the "old size" number of items to the end of the queue (?), but that fails of course if the newly added chunk is not the same size as the old size of the data.
It seems that also the index calculation in TDeque.PushBack also depends on the size being doubled??

Thomas Bulla

2021-01-05 13:10

reporter   ~0128090

Hello Bart,
thanks for your support. What is the next step?

Bart Broersma

2021-01-05 13:30

reporter   ~0128091

> thanks for your support. What is the next step?
I'm not an fpc devel, I just tried to narrow things down.
Maybe I can come upt with a fix, otherwise it's up to another fpc devel.

Thomas Bulla

2021-01-05 14:12

reporter   ~0128092

Ok, I must wait.

I have noticed that, the error occurs when the memory is not doubled for the first time.

(FCapacity - OldEnd) < (FStart) and FStart is the Front of the Queue.

Bart Broersma

2021-01-05 23:22

reporter   ~0128110

> I have noticed that, the error occurs when the memory is not doubled for the first time.
Yes, I already mentioned that: "but that fails of course if the newly added chunk is not the same size as the old size of the data."

Bart Broersma

2021-01-05 23:32

reporter   ~0128112

Last edited: 2021-01-05 23:40

View 2 revisions

Let me see if I understand the algorithm.
When we resize, we add a chunk of memory tot the array.
We then try to move all elements that come before "Front" (in the array) to a location after "Back" (in the array).
If you don't move all elements that come before "Front" you break the circular buffer (that the array represents).
This moving then can only succeed if the number of elements to move is smaller than the newly added element-space.

I guess that NOT moving ther elements (is there is no room for it) does not break the circular buffer mechanism, but I am not really sure.

The condition for moving elements would then become:
if (FStart>0) and (FCapacity-OldEnd>=FStart) then
  begin

Bart Broersma

2021-01-05 23:57

reporter   ~0128114

Do we have any sort of integrity test for TDeque?

Bart Broersma

2021-01-06 12:52

reporter   ~0128120

> if (FStart>0) and (FCapacity-OldEnd>=FStart) then
That won't work.
The array isn't circular anymore after that.
The calculation of where to put the next item goes wrong.
It depends on moving (when resizing) all data before the position of Front to after the position of Back.

Other than reverting r40214 I see no easy solution for this.

Bart Broersma

2021-01-06 12:53

reporter   ~0128121

This issue is related to 0034420.

CudaText man

2021-01-06 16:18

reporter   ~0128126

Let's revert it then?

Thomas Bulla

2021-01-06 20:26

reporter   ~0128131

Hello Bart,

today my internet was down.

I'm an experienced german Pascaler and my english is not so good, sorry!
Maybe I have a solution.

>>>>>>>>>>>
procedure TDeque.IncreaseCapacity;inline;
const
  // if size is small, multiply by 2;
  // if size bigger but <256M, inc by 1/8*size;
  // otherwise inc by 1/16*size
  cSizeSmall = 1*1024*1024;
  cSizeBig = 256*1024*1024;
var
  i,OldEnd,
  DataSize:SizeUInt;
begin
  OldEnd:=FCapacity;
  DataSize:=FCapacity*SizeOf(T);
  if FCapacity=0 then
    FCapacity:=4
  else
  if DataSize<cSizeSmall then
    FCapacity:=FCapacity*2
  else
  if DataSize<cSizeBig then
    FCapacity:=FCapacity+FCapacity div 8
  else
    FCapacity:=FCapacity+FCapacity div 16;

//============== NEW LINES =====================================================
  if (FDataSize + FStart) > FCapacity then begin
    FCapacity:= FDataSize + FStart;
  end;
//==============================================================================

  SetLength(FData, FCapacity);
  if (FStart>0) then
    for i:=0 to FStart-1 do begin
      FData[OldEnd+i]:=FData[i];
    end;
end;
<<<<<<<<<<<<<<<<<<

I noticed that the memory is not being released again.
An example:
10000000 Push, 10000000 Pop, the memory for FData is used, but the size of the
queue is 0. I think that's an other problem.
gqueue_test.zip (2,791 bytes)

Bart Broersma

2021-01-06 23:51

reporter   ~0128134

The current implementation of Push/Pop only works (it designed this way) if all the items are continuous, that is if you walt throug the data array (FData in TDeque) when you start at "Front" and stop at the "Back" you maybe have to go from FData[High(FData)] to FData[0] (this is the circular aspect of the implementation), there can never be any "empty" (unassigned) value in between them.
This then mens that when you increase the size of the array, you must make sure that this is the case again.
Consider the following situation, where a TDeque for Chars has at some point 8 elements.
^ denotes the position of Front, * denotes the position of Back, a - denotes an unused value in the FData array.
IJKLMNOH
      *^

As you can see, the Front item is 'H', the last is 'O', this works because after FData[7] your next Item is FData[0].

Now we increase the capacity when we add the next item:
-------HIJKLMNOP
       ^       *

You can see that Front still is 'H', Back is now the newly added item 'P'.
The items are re-arragned in the array so that they are continuous again.
Simply adding empy "slots" at the end will not do.

Now imaging that you do not add 8 "slots" to the array, but just (for the sake of argument) 2.
If you have to make the Items continuous again, this is possible, you get
HIJKLMNOP--
^       *


But lets say that you start with
HIJKLMNOP
   ^*

Now it becomes rather difficult to rearrange (in a simple way) so that they are continuous again (after adding 2 empty "slots").

You could copy all the items to a new array in the right order and then copy tat array back to the original (and adjust FStart), but that would mean that the memory requirements of the TDeque would double.

Thomas Bulla

2021-01-07 16:02

reporter   ~0128141

Hello Bart,

the last zip file not correctly, sorry.

I cannot imitate the behavior you described.
On the contrary, the queue is worse than expected.
The error does not occur in normal operation because the memory is always doubled.

I have build a little example hows IncreaseCapacity added FData + 4. The mistake
occurs earlier.

With the Property Workarround you can enbable it.
I have implementiert, that the memory relased if FDataSize=0, but it is uncommented.
You can search it with '{$HINT !!!NEW!!!}'
gqueue_test_2.zip (4,139 bytes)

Bart Broersma

2021-01-08 19:45

reporter   ~0128175

Last edited: 2021-01-08 19:47

View 2 revisions

This algorithm for moving data in IncreaseCapacity seems to work.
It will slow things down, but the memory doesn't grow with a factor 2 when dat gets huge.

  if (FStart>0) then
  begin
    if (FCapacity-OldEnd>=FStart) then //we have room to move all items in one go
    begin
      for i:=0 to FStart-1 do
      begin
        FData[OldEnd+i]:=FData[i];
        //FData[i] := Default(T);
      end;
    end
    else
    begin  //we have to move things around in chunks: we have more data in front of FStart then we have newly created unused elements
      CurLast := OldEnd-1;
      EmptyElems := High(FData)-CurLast;
      while (FStart>0) do
      begin
        Elems := Min(EmptyElems, FStart);
        //writeln(format('moving %d elements from 0 to %d',[elems,CurLast+1]));
        Move(FData[0], FData[CurLast+1], Elems*SizeOf(T));
        FillChar(FData[0], Elems*SizeOf(T), 0);
        Inc(CurLast, Elems);
        //writeln(format('moving %d elements from %d to %d',[FCapacity-{FStart+}Elems, elems, 0]));
        Move(FData[Elems], FData[0], (FCapacity-Elems)*SizeOf(T));
        Dec(FStart, Elems);
        Dec(CurLast, Elems);
        FillChar(FData[CurLast+1], Elems*SizeOf(T), 0);
      end;
    end;
  end;    


The original test case doesn't crash anymore with the above code.

Question for the FPC devels: is the use of Move() safe here?

Bart Broersma

2021-01-08 23:55

reporter   ~0128178

If Move() is safe here, then the loop:
  for i:=0 to FStart-1 do
can be replaced with
  Move(FData[0], FData[OldEnd], FStart*SizeOf(T));

Colin Haywood

2021-01-09 02:22

reporter   ~0128183

When implementing generic queues myself some years ago, I ran into issues where if the generic type is reference-counted (string/dynamic array), doing a Move or FillChar like that will mess up the reference counts. Safer is to do repeated assignments or use Finalize().

Bart Broersma

2021-01-09 11:54

reporter   ~0128194

I can see how FillChar messes up refcounts (it is only in the code now for debugging, so it's easier to see which parts of the FData are "occupied", it'l be either removed or ifdef-ed in the final version).
Move in effect simply moves the entire memory to another location, so I don't really se how this messes up refcounts.
My main concern about move is wether or not it takes alignment into account, or does it only work correctly if the array is packed.

ravi dion

2021-01-09 16:26

reporter   ~0128201

@Bart
maybe read https://en.delphipraxis.net/topic/3348-generic-circular-buffer-library-released/ as it is about this issue:
"TRingbuffer<T> unfortunately has several defects, mostly around simply using Move regardless the type of T (managed, containing weak reference)"
seems your using the same data array so it could be fine but of FPC gets weak support some day your code is broken. Maybe better use IsManagedType and don't use move then?

Bart Broersma

2021-01-09 18:37

reporter   ~0128209

Last edited: 2021-01-09 18:37

View 2 revisions

OK, so back to "lazy-copy" using a for loop.
The link also suggests to set the source element to Default(T) after copying it to dest.

One might introduce a protected virtual method:
 
procedure TDeque.MoveData(StartIndex: SizeUInt; Offset: SizeInt; Count: SizeUInt);
var
  i: SizeUInt;
begin
  for i := 0 to Count-1 do
  begin
    FData[StartIndex+i+Offset] := FData[StartIndex+i];
    FData[StartIndex+i] := Default(T);
  end;
end;


If you specialize TDeque wit a non-managed tye and you expect a large queue, then you could override MoveData to use system.move.
Even for the original test it is noticeable.

Bart Broersma

2021-01-11 23:06

reporter   ~0128275

Possible patch attached.
gdeque.increasecapacity.diff (2,252 bytes)   
Index: packages/fcl-stl/src/gdeque.pp
===================================================================
--- packages/fcl-stl/src/gdeque.pp	(revision 48092)
+++ packages/fcl-stl/src/gdeque.pp	(working copy)
@@ -30,7 +30,7 @@
     procedure SetValue(position:SizeUInt; value:T);inline;
     function GetValue(position:SizeUInt):T;inline;
     function GetMutable(position:SizeUInt):PT;inline;
-    procedure IncreaseCapacity();inline;
+    procedure IncreaseCapacity();
   public
     function Size():SizeUInt;inline;
     constructor Create();
@@ -142,7 +142,14 @@
   GetMutable:=@FData[(FStart+position) mod FCapacity];
 end;
 
-procedure TDeque.IncreaseCapacity;inline;
+procedure TDeque.IncreaseCapacity;
+  function Min(const A,B: SizeUInt): SizeUInt; inline; //no need to drag in the entire Math unit ;-)
+  begin
+    if (A<B) then
+      Result:=A
+    else
+      Result:=B;
+  end;
 const
   // if size is small, multiply by 2;
   // if size bigger but <256M, inc by 1/8*size;
@@ -151,7 +158,7 @@
   cSizeBig = 256*1024*1024;
 var
   i,OldEnd,
-  DataSize:SizeUInt;
+  DataSize,CurLast,EmptyElems,Elems:SizeUInt;
 begin
   OldEnd:=FCapacity;
   DataSize:=FCapacity*SizeOf(T);
@@ -165,11 +172,29 @@
     FCapacity:=FCapacity+FCapacity div 8
   else
     FCapacity:=FCapacity+FCapacity div 16;
-
   SetLength(FData, FCapacity);
   if (FStart>0) then
-    for i:=0 to FStart-1 do
-      FData[OldEnd+i]:=FData[i];
+  begin
+    if (FCapacity-OldEnd>=FStart) then //we have room to move all items in one go
+    begin  //don't use system.move: T might be a managed type that would need finilization
+      for i:=0 to FStart-1 do
+        FData[OldEnd+i]:=FData[i];
+    end
+    else
+    begin  //we have to move things around in chunks: we have more data in front of FStart than we have newly created unused elements
+      CurLast := OldEnd-1;
+      EmptyElems:=FCapacity-1-CurLast;
+      while (FStart>0) do
+      begin
+        Elems:=Min(EmptyElems, FStart);
+        for i:=0 to Elems-1 do
+          FData[CurLast+1+i]:=FData[i];
+        for i := 0 to FCapacity-Elems-1 do
+          FData[i]:=FData[Elems+i];
+        Dec(FStart, Elems);
+      end;
+    end;
+  end;
 end;
 
 procedure TDeque.Reserve(cap:SizeUInt);inline;
gdeque.increasecapacity.diff (2,252 bytes)   

Florian

2021-01-13 22:25

administrator   ~0128304

Thanks applied. I modified it to take advantage of IsManagedType for using move for copying.

Bart Broersma

2021-01-13 23:31

reporter   ~0128305

Last edited: 2021-01-13 23:38

View 2 revisions

If T is a managed type, then in the 2 for loops, it should finalize the data before overwriting it.
Also in the "we have to move things around in chunks" part, Move() could be used if T is NOT managed.

I would propose to introduce 2 new methods:
MoveManageData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt)
and
MoveData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);

These can be called in both the loops depending on IsManagedType(T).

If we make MoveData virtual, it would be easy to override it, so that it works with Objects (classes) too.
That way it would be rather easy to derive a TObjDeque.

Should I open a new bugreport for my proposed changes?

Florian

2021-01-14 18:50

administrator   ~0128321

Just add it here and re-open, saves probably time :)

Bart Broersma

2021-01-14 20:50

reporter   ~0128322

Since I'm not the original reporter of this bug, I cannot re-open this issue ;-)

Florian

2021-01-14 21:28

administrator   ~0128325

Yes, I realized this after pressing Add Note, this is why I set the report to "Feedback".

Bart Broersma

2021-01-15 19:20

reporter   ~0128362

I would propose somthing like this.
It's mostly refactoring, but it should make it easier to derive a TObjectDeque.
The FillChar's in MoveXXXData also make that you can move objects (classes actually) around, and still can free them without problems.
It's not extensively tested yet, so don't commit.
Please give feedback on the patch.
gdeque.movedata.diff (3,297 bytes)   
Index: packages/fcl-stl/src/gdeque.pp
===================================================================
--- packages/fcl-stl/src/gdeque.pp	(revision 48161)
+++ packages/fcl-stl/src/gdeque.pp	(working copy)
@@ -31,6 +31,10 @@
     function GetValue(position:SizeUInt):T;inline;
     function GetMutable(position:SizeUInt):PT;inline;
     procedure IncreaseCapacity();
+  protected
+    procedure MoveSimpleData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
+    procedure MoveManagedData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
+    procedure MoveData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
   public
     function Size():SizeUInt;inline;
     constructor Create();
@@ -127,6 +131,8 @@
 procedure TDeque.SetValue(position:SizeUInt; value:T);inline;
 begin
   Assert(position < size, 'Deque access out of range');
+  if IsManagedType(T) then
+    Finalize(FData[(FStart+position)mod FCapacity]);
   FData[(FStart+position)mod FCapacity]:=value;
 end;
 
@@ -142,6 +148,40 @@
   GetMutable:=@FData[(FStart+position) mod FCapacity];
 end;
 
+
+
+procedure TDeque.MoveSimpleData(StartIndex: SizeUInt; Offset: SizeInt;  NrElems: SizeUInt);
+begin
+  Move(FData[StartIndex], FData[StartIndex+Offset], NrElems*SizeOf(T));
+  if Offset>0 then
+    FillChar(FData[StartIndex], NrElems*SizeOf(T), 0)
+  else
+    FillChar(FData[StartIndex+NrElems+Offset], -Offset*SizeOf(T), 0);
+end;
+
+procedure TDeque.MoveManagedData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
+var
+  i: SizeUInt;
+begin
+  //since we always move blocks where Abs(Offset)>=NrElems, there is no need for
+  //2 seperate loops (1 for ngeative and 1 for positive Offsett)
+  for i := 0 to NrElems-1 do
+  begin
+    Finalize(FData[StartIndex+i+Offset]);
+    FData[StartIndex+i+Offset] := FData[StartIndex+i];
+    Finalize(FData[StartIndex+i]);
+    FillChar(FData[StartIndex+i], SizeOf(T), 0);
+  end;
+end;
+
+procedure TDeque.MoveData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
+begin
+  if IsManagedType(T) then
+    MoveManagedData(StartIndex, Offset, NrElems)
+  else
+    MoveSimpleData(StartIndex, Offset, NrElems);
+end;
+
 procedure TDeque.IncreaseCapacity;
   function Min(const A,B: SizeUInt): SizeUInt; inline; //no need to drag in the entire Math unit ;-)
   begin
@@ -177,11 +217,7 @@
   begin
     if (FCapacity-OldEnd>=FStart) then //we have room to move all items in one go
     begin
-      if IsManagedType(T) then
-        for i:=0 to FStart-1 do
-          FData[OldEnd+i]:=FData[i]
-      else
-        Move(FData[0], FData[OldEnd], FStart*SizeOf(T));
+      MoveData(0, OldEnd ,FStart)
     end
     else
     begin  //we have to move things around in chunks: we have more data in front of FStart than we have newly created unused elements
@@ -189,11 +225,9 @@
       EmptyElems:=FCapacity-1-CurLast;
       while (FStart>0) do
       begin
-        Elems:=Min(EmptyElems, FStart);
-        for i:=0 to Elems-1 do
-          FData[CurLast+1+i]:=FData[i];
-        for i := 0 to FCapacity-Elems-1 do
-          FData[i]:=FData[Elems+i];
+        Elems := Min(EmptyElems, FStart);
+        MoveData(0, CurLast+1, Elems);
+        MoveData(Elems, -Elems, FCapacity-Elems);
         Dec(FStart, Elems);
       end;
     end;
gdeque.movedata.diff (3,297 bytes)   

Bart Broersma

2021-01-15 21:23

reporter   ~0128365

I think clear needs to finalize data as wel (if managed).
And it must be made easy to override for a TObjectDeque (which possibly needs to free all data in this case).

Michael Van Canneyt

2021-01-24 18:41

administrator   ~0128566

Checked and applied patch from Bart, thank you !

Issue History

Date Modified Username Field Change
2021-01-04 14:15 Thomas Bulla New Issue
2021-01-04 14:15 Thomas Bulla File Added: gqueue_test.lpr
2021-01-04 16:11 Bart Broersma Note Added: 0128072
2021-01-04 16:26 Bart Broersma Note Edited: 0128072 View Revisions
2021-01-04 16:26 Bart Broersma Note Edited: 0128072 View Revisions
2021-01-04 16:27 Bart Broersma Note Edited: 0128072 View Revisions
2021-01-04 17:00 Bart Broersma Note Added: 0128076
2021-01-04 17:01 Bart Broersma Note Edited: 0128076 View Revisions
2021-01-05 13:10 Thomas Bulla Note Added: 0128090
2021-01-05 13:30 Bart Broersma Note Added: 0128091
2021-01-05 14:12 Thomas Bulla Note Added: 0128092
2021-01-05 23:22 Bart Broersma Note Added: 0128110
2021-01-05 23:32 Bart Broersma Note Added: 0128112
2021-01-05 23:40 Bart Broersma Note Edited: 0128112 View Revisions
2021-01-05 23:57 Bart Broersma Note Added: 0128114
2021-01-06 12:52 Bart Broersma Note Added: 0128120
2021-01-06 12:53 Bart Broersma Note Added: 0128121
2021-01-06 16:18 CudaText man Note Added: 0128126
2021-01-06 20:26 Thomas Bulla Note Added: 0128131
2021-01-06 20:26 Thomas Bulla File Added: gqueue_test.zip
2021-01-06 21:06 Sven Barth Relationship added child of 0034420
2021-01-06 23:51 Bart Broersma Note Added: 0128134
2021-01-07 16:02 Thomas Bulla Note Added: 0128141
2021-01-07 16:02 Thomas Bulla File Added: gqueue_test_2.zip
2021-01-08 19:45 Bart Broersma Note Added: 0128175
2021-01-08 19:47 Bart Broersma Note Edited: 0128175 View Revisions
2021-01-08 23:55 Bart Broersma Note Added: 0128178
2021-01-09 02:22 Colin Haywood Note Added: 0128183
2021-01-09 11:54 Bart Broersma Note Added: 0128194
2021-01-09 16:26 ravi dion Note Added: 0128201
2021-01-09 18:37 Bart Broersma Note Added: 0128209
2021-01-09 18:37 Bart Broersma Note Edited: 0128209 View Revisions
2021-01-11 23:06 Bart Broersma Note Added: 0128275
2021-01-11 23:06 Bart Broersma File Added: gdeque.increasecapacity.diff
2021-01-13 22:25 Florian Assigned To => Florian
2021-01-13 22:25 Florian Status new => resolved
2021-01-13 22:25 Florian Resolution open => fixed
2021-01-13 22:25 Florian Fixed in Version => 3.3.1
2021-01-13 22:25 Florian Fixed in Revision => 48154
2021-01-13 22:25 Florian FPCTarget => -
2021-01-13 22:25 Florian Note Added: 0128304
2021-01-13 23:31 Bart Broersma Note Added: 0128305
2021-01-13 23:38 Bart Broersma Note Edited: 0128305 View Revisions
2021-01-14 18:50 Florian Note Added: 0128321
2021-01-14 18:50 Florian Assigned To Florian =>
2021-01-14 18:50 Florian Status resolved => feedback
2021-01-14 18:50 Florian Resolution fixed => open
2021-01-14 20:50 Bart Broersma Note Added: 0128322
2021-01-14 21:28 Florian Note Added: 0128325
2021-01-15 19:20 Bart Broersma Note Added: 0128362
2021-01-15 19:20 Bart Broersma File Added: gdeque.movedata.diff
2021-01-15 21:23 Bart Broersma Note Added: 0128365
2021-01-24 18:41 Michael Van Canneyt Assigned To => Michael Van Canneyt
2021-01-24 18:41 Michael Van Canneyt Status feedback => resolved
2021-01-24 18:41 Michael Van Canneyt Resolution open => fixed
2021-01-24 18:41 Michael Van Canneyt Fixed in Revision 48154 => 48405
2021-01-24 18:41 Michael Van Canneyt Note Added: 0128566