Ignore:
Timestamp:
Feb 29, 2016, 7:18:59 PM (9 years ago)
Author:
[email protected]
Message:

regress/script-tests/double-pollution-putbyoffset.js.ftl-eager timed out because of a lock ordering deadlock involving InferredType and CodeBlock
https://bugs.webkit.org/show_bug.cgi?id=154841

Reviewed by Benjamin Poulain.

Here's the deadlock:

Main thread:

1) Change an InferredType. This acquires InferredType::m_lock.
2) Fire watchpoint set. This triggers CodeBlock invalidation, which acquires

CodeBlock::m_lock.

DFG thread:

1) Iterate over the information in a CodeBlock. This acquires CodeBlock::m_lock.
2) Ask an InferredType for its descriptor(). This acquires InferredType::m_lock.

I think that the DFG thread's ordering should be legal, because the best logic for lock
hierarchies is that locks that protect the largest set of stuff should be acquired first.

This means that the main thread shouldn't be holding the InferredType::m_lock when firing
watchpoint sets. That's what this patch ensures.

At the time of writing, this test was deadlocking for me on trunk 100% of the time. With
this change I cannot get it to deadlock.

  • runtime/InferredType.cpp:

(JSC::InferredType::willStoreValueSlow):
(JSC::InferredType::makeTopSlow):
(JSC::InferredType::set):
(JSC::InferredType::removeStructure):
(JSC::InferredType::InferredStructureWatchpoint::fireInternal):

  • runtime/InferredType.h:
Location:
trunk/Source/JavaScriptCore/runtime
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/InferredType.cpp

    r190916 r197381  
    401401bool InferredType::willStoreValueSlow(VM& vm, PropertyName propertyName, JSValue value)
    402402{
    403     ConcurrentJITLocker locker(m_lock);
    404     Descriptor myType = descriptor(locker);
    405     Descriptor otherType = Descriptor::forValue(value);
    406 
    407     myType.merge(otherType);
    408 
    409     ASSERT(descriptor(locker) != myType); // The type must have changed if we're on the slow path.
    410 
    411     set(locker, vm, propertyName, value, myType);
    412 
    413     return kind(locker) != Top;
     403    Descriptor oldType;
     404    Descriptor myType;
     405    bool result;
     406    {
     407        ConcurrentJITLocker locker(m_lock);
     408        oldType = descriptor(locker);
     409        myType = Descriptor::forValue(value);
     410
     411        myType.merge(oldType);
     412       
     413        ASSERT(oldType != myType); // The type must have changed if we're on the slow path.
     414
     415        bool setResult = set(locker, vm, myType);
     416        result = kind(locker) != Top;
     417        if (!setResult)
     418            return result;
     419    }
     420   
     421    InferredTypeFireDetail detail(this, propertyName.uid(), oldType, myType, value);
     422    m_watchpointSet.fireAll(detail);
     423    return result;
    414424}
    415425
    416426void InferredType::makeTopSlow(VM& vm, PropertyName propertyName)
    417427{
    418     ConcurrentJITLocker locker(m_lock);
    419     set(locker, vm, propertyName, JSValue(), Top);
    420 }
    421 
    422 void InferredType::set(
    423     const ConcurrentJITLocker& locker, VM& vm, PropertyName propertyName, JSValue offendingValue,
    424     Descriptor newDescriptor)
     428    Descriptor oldType;
     429    {
     430        ConcurrentJITLocker locker(m_lock);
     431        oldType = descriptor(locker);
     432        if (!set(locker, vm, Top))
     433            return;
     434    }
     435
     436    InferredTypeFireDetail detail(this, propertyName.uid(), oldType, Top, JSValue());
     437    m_watchpointSet.fireAll(detail);
     438}
     439
     440bool InferredType::set(const ConcurrentJITLocker& locker, VM& vm, Descriptor newDescriptor)
    425441{
    426442    // We will trigger write barriers while holding our lock. Currently, write barriers don't GC, but that
     
    432448    // Be defensive: if we're not really changing the type, then we don't have to do anything.
    433449    if (descriptor(locker) == newDescriptor)
    434         return;
    435 
     450        return false;
     451
     452    bool shouldFireWatchpointSet = false;
     453   
    436454    // The new descriptor must be more general than the previous one.
    437455    ASSERT(newDescriptor.subsumes(descriptor(locker)));
     
    447465        ASSERT(m_watchpointSet.state() != IsInvalidated);
    448466
    449         InferredTypeFireDetail detail(
    450             this, propertyName.uid(), descriptor(locker), newDescriptor, offendingValue);
    451        
    452467        // We're about to do expensive things because some compiler thread decided to watch this type and
    453468        // then the type changed. Assume that this property is crazy, and don't ever do any more things for
     
    455470        newDescriptor = Top;
    456471
    457         m_watchpointSet.fireAll(detail);
     472        shouldFireWatchpointSet = true;
    458473    }
    459474
     
    478493    // Assert that we did things.
    479494    ASSERT(descriptor(locker) == newDescriptor);
     495
     496    return shouldFireWatchpointSet;
    480497}
    481498
     
    487504    VM& vm = *Heap::heap(this)->vm();
    488505
    489     ConcurrentJITLocker locker(m_lock);
    490    
    491     Descriptor newDescriptor = descriptor(locker);
    492     newDescriptor.removeStructure();
    493    
    494     set(locker, vm, PropertyName(nullptr), JSValue(), newDescriptor);
     506    Descriptor oldDescriptor;
     507    Descriptor newDescriptor;
     508    {
     509        ConcurrentJITLocker locker(m_lock);
     510        oldDescriptor = descriptor(locker);
     511        newDescriptor = oldDescriptor;
     512        newDescriptor.removeStructure();
     513       
     514        if (!set(locker, vm, newDescriptor))
     515            return;
     516    }
     517
     518    InferredTypeFireDetail detail(this, nullptr, oldDescriptor, newDescriptor, JSValue());
     519    m_watchpointSet.fireAll(detail);
    495520}
    496521
  • trunk/Source/JavaScriptCore/runtime/InferredType.h

    r190916 r197381  
    230230    bool willStoreValueSlow(VM&, PropertyName, JSValue);
    231231    void makeTopSlow(VM&, PropertyName);
    232     void set(const ConcurrentJITLocker&, VM&, PropertyName, JSValue, Descriptor);
     232
     233    // Helper for willStoreValueSlow() and makeTopSlow(). This returns true if we should fire the
     234    // watchpoint set.
     235    bool set(const ConcurrentJITLocker&, VM&, Descriptor);
     236   
    233237    void removeStructure();
    234238
Note: See TracChangeset for help on using the changeset viewer.