Closed
Bug 546407
Opened 16 years ago
Closed 16 years ago
MMgc deadlock with MMGC_MEMORY_PROFILER enabled and OOM condition
Categories
(Tamarin Graveyard :: Garbage Collection (mmGC), defect, P2)
Tracking
(Not tracked)
VERIFIED
FIXED
flash10.1
People
(Reporter: rwinchel, Assigned: treilly)
References
Details
Attachments
(2 files, 3 obsolete files)
|
9.61 KB,
patch
|
lhansen
:
superreview+
|
Details | Diff | Splinter Review |
|
12.30 KB,
patch
|
lhansen
:
review+
|
Details | Diff | Splinter Review |
If the memory profiler is enabled, a deadlock is possible if multiple player instances are running and one of the causes an OOM condition.
Example call stacks (2 threads):
VMPI_lockAcquire(vmpi_spin_lock_t * lock=0x06ef06a0)
MMgc::GCAcquireSpinlockWithRecursion::GCAcquireSpinlockWithRecursion(vmpi_spin_lock_t * sl=0x06ef06a0, void * owner=0x0000164c)
MMgc::GCHeap::AllocHook(const void * item=0x134eb730, unsigned int askSize=101, unsigned int gotSize=104)
MMgc::FixedAlloc::Alloc(unsigned int size=101, MMgc::FixedMallocOpts opts=kNone)
MMgc::FixedAllocSafe::Alloc(unsigned int size=101, MMgc::FixedMallocOpts flags=kNone)
MMgc::FixedMalloc::Alloc(unsigned int size=101, MMgc::FixedMallocOpts flags=kNone)
MMgc::FixedMalloc::OutOfLineAlloc(unsigned int size=101, MMgc::FixedMallocOpts flags=kNone
MMgc::AllocCallInline(unsigned int size=101, MMgc::FixedMallocOpts opts=kNone)
Flash10f.ocx!MMgc::TaggedAlloc(unsigned int size=101, MMgc::FixedMallocOpts opts=kNone, unsigned int guard=3216965634
Flash10f.ocx!MMgc::NewTaggedArray(unsigned int count=93, unsigned int elsize=1, MMgc::FixedMallocOpts opts=kNone, bool isPrimitive=true)
MMgcConstructTaggedArray<char>(char * __formal=0x00000000, unsigned int count=93, MMgc::FixedMallocOpts opts=kNone
StrAlloc(unsigned int len=93
CopyUTF16to8(const unsigned short * pSrc=0x0c70c62c, bool reverseBytes=false)
VMPI_lockAcquire(vmpi_spin_lock_t * lock=0x06eefe78
MMgc::GCAcquireSpinlock::GCAcquireSpinlock(vmpi_spin_lock_t * spinlock=0x06eefe78)
MMgc::FixedAllocSafe::QueryOwnsObject(const void * item=0x0a953718)
MMgc::FixedMalloc::EnsureFixedMallocMemory(const void * item=0x0a953718)
MMgc::FixedMalloc::Free(void * item=0x0a953718)
MMgc::FixedMalloc::OutOfLineFree(void * p=0x0a953718)
MMgc::DeleteCallInline(void * p=0x0a953718)
MMgc::DeleteTaggedArrayWithHeader(void * p=0x0a953718)
MMgcDestructTaggedArrayChecked<HashTable::Bucket *>()
HashTable::BucketArrayFree(HashTable::Bucket * * buckets=0x0a953728)
HashTable::Destroy()
HashTable::~HashTable()
avmplus::ListenerHashTable::~ListenerHashTable()
avmplus::ListenerHashTable::`scalar deleting destructor'()
MMgcDestructTaggedScalarChecked<avmplus::ListenerHashTable(avmplus::ListenerHashTable * mem=0x09e14ae0)
avmplus::ListenerNodeList::~ListenerNodeList()
avmplus::ListenerNodeList::`scalar deleting destructor'()
MMgc::GCAlloc::Finalize()
MMgc::GC::Finalize()
MMgc::GC::Sweep()
MMgc::GC::FinishIncrementalMark(bool scanStack=false)
MMgc::GC::Collect(bool scanStack=false
MMgc::GC::memoryStatusChange(MMgc::_MemoryStatus _formal=kFreeMemoryIfPossible, MMgc::_MemoryStatus to=kFreeMemoryIfPossible)
MMgc::GCHeap::SendFreeMemorySignal(unsigned int minimumBlocksToFree=82945)
MMgc::GCHeap::Alloc(unsigned int size=82945, unsigned int flags=9) Locked LOCK2
MMgc::GC::heapAlloc(unsigned int siz=82945, int flags=9)
MMgc::GC::AllocBlock(int size=82945, int pageType=3, bool zero=false, bool canFail=true)
MMgc::GCLargeAlloc::Alloc(unsigned int originalSize=339738623, unsigned int requestSize=339738640, int flags=16) L
MMgc::GC::Alloc(unsigned int size=339738640, int flags=16)
MMgc::GC::PleaseAlloc(unsigned int size=339738623, int flags=0)
| Assignee | ||
Comment 1•16 years ago
|
||
My first instinct is to have the profiler use its own lock instead of the GCHeap lock. There needs to be an invariant that the GCHeap lock is the only lock that can be held when we call out into other code (since it recurses properly). FixedAllocSafe locks and the largeObjectTracker lock for instance either don't call out while the lock is held (largeObjectTracker) or release it before calling out (FixedAllocSafe::CreateChunk).
Another fix would be to have FixedAllocSafe unlock before calling AllocHook. Actually both "fixes" are probably good ideas.
| Assignee | ||
Updated•16 years ago
|
Severity: normal → major
Status: NEW → ASSIGNED
Priority: -- → P2
Target Milestone: --- → flash10.1
| Assignee | ||
Comment 2•16 years ago
|
||
Having the profiler use its own lock will prevent us locking the heap lock on every alloc/free which could cause contention.
| Assignee | ||
Comment 3•16 years ago
|
||
Attachment #427420 -
Flags: superreview?(lhansen)
| Assignee | ||
Updated•16 years ago
|
Attachment #427420 -
Flags: review?(rwinchel)
| Assignee | ||
Comment 4•16 years ago
|
||
attempts to avoid all locking in the common case, including double checked bool in VMPI_spyCallback.
Attachment #427430 -
Flags: superreview?(lhansen)
Updated•16 years ago
|
Attachment #427420 -
Flags: superreview?(lhansen) → superreview-
Comment 5•16 years ago
|
||
Comment on attachment 427420 [details] [diff] [review]
don't hold FixedAlloc locks while invoking hooks
The code now performs more work than before: it recomputes the gcheap value, where previously it only had to pick it out of a property in the allocator.
I'm also concerned about code size increases from inlining all the hooking code and would like to see an analysis of that.
Comment 6•16 years ago
|
||
Comment on attachment 427430 [details] [diff] [review]
removes AllocHook GCHeap locking
I'm hardly qualified to review the first change to the winmo platform code, so I'll have to take your word for it. The rest is all good.
Attachment #427430 -
Flags: superreview?(lhansen) → superreview+
| Assignee | ||
Comment 7•16 years ago
|
||
(In reply to comment #5)
> (From update of attachment 427420 [details] [diff] [review])
> I'm also concerned about code size increases from inlining all the hooking code
> and would like to see an analysis of that.
44k increase in avmplus.a. Oops. If I flip things around and make Alloc/Free out of line functions with the AllocSansHook and Hooks inlined into it, we actually get 12k smaller as the FixedAllocSafe lock acquisition code is no longer inlined into FixedMalloc::Alloc call sites. Nice catch. Will post a new patch after investigating the extra work comment.
| Assignee | ||
Comment 8•16 years ago
|
||
tamarin-redux-argo: changeset: 3723:fcaffd5694c9
tamarin-redux: changeset: 3856:fcaffd5694c9
Leaving bug open to land the FixedAllocSafe lock fixing.
| Assignee | ||
Comment 9•16 years ago
|
||
Made FixedAllocSafe::Alloc/Free completely inlined (into its one caller, FixedMalloc::Alloc)
Left FixedAlloc::Alloc/Free out of line (this is called by some FixedAlloc instances in the player)
Made some new/delete overrides using FixedMalloc::Alloc/Free use the out of line versions (I think we just missed these).
Code size is slightly smaller (12k smaller ocx release and release debugger)
Also checked that the FixedBlock* calculation is only done once by the compiler once all the inlining is done.
Attachment #427772 -
Flags: superreview?(lhansen)
| Assignee | ||
Updated•16 years ago
|
Attachment #427420 -
Attachment is obsolete: true
Attachment #427420 -
Flags: review?(rwinchel)
Comment 10•16 years ago
|
||
Comment on attachment 427772 [details] [diff] [review]
redo fixed alloc lock changes
The call to Alloc and not OutOfLineAlloc in PleaseAlloc was deliberate - we want PleaseAlloc to inline as well as Alloc does.
The GCRoot new and delete operators also called FixedMalloc::Alloc/Delete (and not the OutOfLine versions) deliberately because 'size' is pretty much always known and the inlining should boil most of the code away and we should be left with a call directly to FixedAllocSafe::Alloc or FixedLargeAlloc::Alloc.
In both those cases we could probably go either way; they're not heavily used either statically or dynamically. But it wasn't an oversight.
The restructuring as large REALLY_INLINE functions is OK but those inline functions should not be in the header file; they're used only in the cpp file so put them there. Ditto the hook functions, then.
Attachment #427772 -
Flags: superreview?(lhansen) → superreview-
| Assignee | ||
Comment 11•16 years ago
|
||
I realize that, but I changed FixedAllocSafe to inline so now FixedMalloc::Alloc isn't a small function to inline. Anyways things get smaller w/ my change because we aren't inlining a size check, branch and a VMPI_lock/release into FixedMalloc::Alloc call sites which was dubious I think. Now only the uncommon large allocs and times where CreateChunk/FreeChunk are needed require two call frames. Anyway I measured call times and its a wash in release and a little faster in debugger and its smaller, win win!
So I don't need to put inline functions in the inlines, I'll move to the cpp and repost.
| Assignee | ||
Comment 12•16 years ago
|
||
(In reply to comment #10)
> The restructuring as large REALLY_INLINE functions is OK but those inline
> functions should not be in the header file; they're used only in the cpp file
> so put them there. Ditto the hook functions, then.
Actually FixedAllocSafe::Alloc/Free are used externally and since they use the the hook funcs and the SansHook functions I think everything needs to go in the inlines header. At least that's what gcc is telling me.
| Assignee | ||
Comment 13•16 years ago
|
||
(In reply to comment #10)
> (From update of attachment 427772 [details] [diff] [review])
> The call to Alloc and not OutOfLineAlloc in PleaseAlloc was deliberate - we
> want PleaseAlloc to inline as well as Alloc does.
Alloc is only used once in FixedMalloc::Alloc, PleaseAlloc (and all the new operators) are used a lot statically and probably shouldn't be inlined. My thinking is that statically numerous call sites should call out of line functions that are themselves maximally inlined. I think this patch moves us in that direction although I think it would be better if the out of line functions were the default and the inline version were special/private (ie only used by MMgc).
Re-requesting review.
| Assignee | ||
Updated•16 years ago
|
Attachment #427772 -
Flags: superreview- → superreview?
| Assignee | ||
Comment 14•16 years ago
|
||
First patch:
tamarin-redux-argo: changeset: 3724:d634a1cff7ae, changeset: 3723:fcaffd5694c9
tamarin-redux: changeset: 3858:d634a1cff7ae, changeset: 3857:1c6bce438ca5
Comment 15•16 years ago
|
||
Did you just push those without awaiting review?
| Assignee | ||
Comment 16•16 years ago
|
||
no, the first patch was pushed, attachment 427430 [details] [diff] [review]
| Assignee | ||
Updated•16 years ago
|
Attachment #427772 -
Flags: superreview? → superreview?(lhansen)
| Assignee | ||
Comment 17•16 years ago
|
||
Second patch isn't critical, can wait till Lar's is back.
Comment 18•16 years ago
|
||
(In reply to comment #12)
> (In reply to comment #10)
> > The restructuring as large REALLY_INLINE functions is OK but those inline
> > functions should not be in the header file; they're used only in the cpp file
> > so put them there. Ditto the hook functions, then.
>
> Actually FixedAllocSafe::Alloc/Free are used externally and since they use the
> the hook funcs and the SansHook functions I think everything needs to go in the
> inlines header. At least that's what gcc is telling me.
Then that's a symptom of a breakdown in the design somewhere. SR- again, and let's take it to IM or telephone.
Updated•16 years ago
|
Attachment #427772 -
Flags: superreview?(lhansen) → superreview-
| Assignee | ||
Comment 19•16 years ago
|
||
holding the lock across the AllocHook/FreeHook makes me nervous but is not known to be a problem moving to 10.1.1.
Target Milestone: flash10.1 → flash10.1.1
Comment 20•16 years ago
|
||
Moving back to 10.1 after discussion with Tom.
Flags: flashplayer-qrb+
Target Milestone: flash10.1.1 → flash10.1
| Assignee | ||
Comment 22•16 years ago
|
||
Attachment #427772 -
Attachment is obsolete: true
Attachment #433395 -
Flags: review?(lhansen)
Comment 23•16 years ago
|
||
Comment on attachment 433395 [details] [diff] [review]
makes FixedAllocSafe private to tighten things up
Technically speaking the change to GCRoot::new is not right because the size is always going to be known at compile time, and most of the inlined code will boil away to a call to the correct underlying operator. But this is not a big deal and it's fine the way you've changed it.
More worrisome, don't the updates to m_totalAskSize in AllocHook and FreeHook, when called from FixedAllocSafe, need to be protected by a mutex?
Stylistically it would probably be better to rename AllocHook, AllocSansHook, etc as InlineAllocHook, InlineAllocSansHook, etc, to make the purpose clear and throw up warnings, but I'm not going to insist (at least not now).
Holding the review until the issue of the locking has been resolved.
| Assignee | ||
Comment 24•16 years ago
|
||
Kinda messy, wonder if there's a way to use templates to clean up the compile time ifdef's.
Attachment #433395 -
Attachment is obsolete: true
Attachment #433555 -
Flags: review?(lhansen)
Attachment #433395 -
Flags: review?(lhansen)
Comment 25•16 years ago
|
||
(In reply to comment #24)
> Created an attachment (id=433555) [details]
> Kinda messy, wonder if there's a way to use templates to clean up the compile
> time ifdef's.
I sometimes use this pattern:
#ifdef BLAH
#define MAYBEARG(x) , x
#else
#define MAYBEARG(x)
#endif
and then
int foo(int x MAYBEARG(int y)) { ... }
and then
foo(10 MAYBEARG(20))
which at least stops my eyes from bleeding and with a good name for MAYBEARG is pretty easy to understand when reading.
Comment 26•16 years ago
|
||
eg, MEMPROFARG might be an adequate name in this circumstance.
Comment 27•16 years ago
|
||
Comment on attachment 433555 [details] [diff] [review]
Makes m_totalAskSize updates happen in locked Alloc/FreeSansHook and updates names to include "Inline" prefix
It's a bit absurd for AllocSansHook to have an #ifdef MMGC_HOOKS in it, but let's get on with it.
Attachment #433555 -
Flags: review?(lhansen) → review+
| Assignee | ||
Comment 28•16 years ago
|
||
tamarin-redux-argo: changeset: 3842:cfcaebb5c0c8
tamarin-redux: changeset: 4101:cfcaebb5c0c8
Status: ASSIGNED → RESOLVED
Closed: 16 years ago
Resolution: --- → FIXED
Updated•16 years ago
|
QA Contact: gc → dschaffe
Comment 29•16 years ago
|
||
how can this bug be regression testing for marking verified? Would it be enough to run several instance of the player with memory profiling enabled?
Comment 30•16 years ago
|
||
(In reply to comment #29)
> how can this bug be regression testing for marking verified? Would it be
> enough to run several instance of the player with memory profiling enabled?
At least that addresses the original report, though I can't speak to whether that would be enough to provoke the original bug, which is what you'd need to verify in such a test case, of course.
Updated•16 years ago
|
Flags: in-testsuite?
Comment 31•16 years ago
|
||
ran the oom testsuite with plugin and memory profiling enabled with build 10,1,53,57. tests worked correctly. when oom condition was reached a refresh reloaded the player correctly. oom testsuite is here: http://ats.macromedia.com/Players/ATS/OTS/OTS10/OOM/asteam/alltests.html
marking bug as verified.
Status: RESOLVED → VERIFIED
Updated•16 years ago
|
Flags: in-testsuite?
You need to log in
before you can comment on or make changes to this bug.
Description
•