Project

General

Profile

Bug #10483

memory leak in BufferManager.openBuffers

Added by Constantin Asofiei 11 months ago. Updated 10 months ago.

Status:
WIP
Priority:
Normal
Target version:
-
Start date:
Due date:
% Done:

70%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
production:
No
env_name:
topics:

Related issues

Related to Base Language - Bug #10567: same buffer can be used as argument multiple times Test

History

#2 Updated by Constantin Asofiei 11 months ago

In BufferManager.bufferInitialized, the buffer.activeScopeDepth is 0-based; this code:

      if (global)
      {
        ...
      }
      else
      {
         int scopes = getOpenBufferScopes();
         if (buffer.activeScopeDepth <= scopes)
         {
            openBuffers.addAt(scopes - buffer.activeScopeDepth, buffer);
            buffer.addOpenBuffersScope(buffer.activeScopeDepth);
         }
      }

In the case where I see this, the 'activeScopeDepth' is from a dataset parameter, TemporaryBuffer.associateImpl ~line 2648:

                        // open scope for this new buffer. Bound buffers go to global scope, the other local
                        tbuf.openScopeAt(bind ? 0 : tbuf.bufferManager.getOpenBufferScopes() - 1);

Things are confusing is wrong with the meaning of 'depth'. For TemporaryBuffer.openScopeAt:

    * @param   blockDepth
    *          Zero-based depth of the block (starting from the outermost scope) at which the
    *          buffer scope is opened.

seems to be the depth of the stack, from the bottom (0 is the bottom of the stack). So using tbuf.bufferManager.getOpenBufferScopes() - 1 for 'current scope' is right.

For ScopedList.addAt:

    * Add the given element to the tail of the scope at the given scope depth, where zero depth
    * represents the current scope, and a positive depth indicate older scopes. A depth of
    * {@code scopes() - 1} indicates the global scope.
    *
    * @param   depth
    *          Depth of the scope to which to add the element, as described above.

depth meaning is reversed: a zero means current scope, while scopes() - 1 the global scope.

So I think the code in bufferInitilized needs to be this:

         int scopes = getOpenBufferScopes();
         if (buffer.activeScopeDepth + 1 <= scopes)
         {
            openBuffers.addAt(scopes - (buffer.activeScopeDepth + 1), buffer);
            buffer.addOpenBuffersScope(buffer.activeScopeDepth);
         }

as we need to transform from a 0 to scopes' depth (for the activeScopeDepth) to a 'scopes - 1' to zero depth, as we need 0 to be 'current scope'.

#3 Updated by Constantin Asofiei 11 months ago

  • % Done changed from 0 to 70
  • Assignee set to Constantin Asofiei
  • Status changed from New to Review
  • reviewer Alexandru Lungu, Greg Shah added

Actually, the memory leak was from a case where a buffer from a super-class is opened (in the pseudo-execute method for converted .cls) both in the super-class and child class. This ended up messing the openBuffers list for the current scope, as a buffer's scope must be opened only once in the current scope.

I've created branch 10483a from trunk rev 16136; in rev 16137:
  • Ensure a buffer is opened only once for the pseudo-execute calls for a type hierarchy in a converted .cls.

Eric - please also take a look. I've added a SEVERE logging in ScopedList if the same entry is added again in the scope. Maybe also log the stacktrace?

What is posted in #10483-2 still remains confusing for me.

#4 Updated by Greg Shah 11 months ago

Code Review Task Branch 10483a Revision 16137 and 16138

For RB.openScope(), what are 4GL code examples that cause a single buffer instance to ever have an open count > 0? I understand that your change is safer because of the conditions, but it seems like our approach should protect against any way that the same buffer instance can be opened more than once.

#5 Updated by Constantin Asofiei 11 months ago

Greg Shah wrote:

Code Review Task Branch 10483a Revision 16137 and 16138

For RB.openScope(), what are 4GL code examples that cause a single buffer instance to ever have an open count > 0? I understand that your change is safer because of the conditions, but it seems like our approach should protect against any way that the same buffer instance can be opened more than once.

The change prevents the same buffer instance to open the scope more than once in the same 'execute'/external block. I agree that this should be somewhere lower in RB.openScope, and maybe consider any kind of block, to prevent this problem. Or maybe even ScopedList, if the key already exists, things get... weird with the linked nodes.

The scenario is like this:
  • oo/Foo.cls
    class oo.Foo inherits oo.Bar:
      method public void m1().
         find first tt1.
      end.
    end.
    
  • oo/Bar.cls
    class oo.Bar:
       def temp-table tt1 field f1 as int.
       method public void m2().
         find first tt1.
       end.
    end.
    

RB.openScope(tt1) appears in the pseudo-execute method for both Foo.java and Bar.java. The protection ensures only for this specific case.

#6 Updated by Greg Shah 11 months ago

The protection ensures only for this specific case.

I understand. But should we expand this to be unconditional? I can't think of a scenario where the 4GL allows nested open scope on the same buffer instance.

#7 Updated by Constantin Asofiei 10 months ago

  • Related to Bug #10567: same buffer can be used as argument multiple times added

#8 Updated by Constantin Asofiei 10 months ago

  • Status changed from Review to Merge Pending

I'll merge after 10662a.

#9 Updated by Constantin Asofiei 10 months ago

  • Status changed from Merge Pending to WIP

Branch 10483a was merged to trunk rev 16206 and archived.

What's left is to check if the solution needs to be refined.

Also available in: Atom PDF