@@ -1871,7 +1871,7 @@ DispIDCache* AppDomain::SetupRefDispIDCache()
18711871
18721872#endif // FEATURE_COMINTEROP
18731873
1874- FileLoadLock * FileLoadLock::Create (PEFileListLock * pLock, PEAssembly * pPEAssembly, Assembly *pAssembly )
1874+ FileLoadLock* FileLoadLock::Create (PEFileListLock* pLock, PEAssembly* pPEAssembly)
18751875{
18761876 CONTRACTL
18771877 {
@@ -1884,7 +1884,7 @@ FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEAssembly * pPEAssemb
18841884 }
18851885 CONTRACTL_END;
18861886
1887- NewHolder<FileLoadLock> result (new FileLoadLock (pLock, pPEAssembly, pAssembly ));
1887+ NewHolder<FileLoadLock> result (new FileLoadLock (pLock, pPEAssembly));
18881888
18891889 pLock->AddElement (result);
18901890 result->AddRef (); // Add one ref on behalf of the ListLock's reference. The corresponding Release() happens in FileLoadLock::CompleteLoadLevel.
@@ -1910,6 +1910,14 @@ Assembly *FileLoadLock::GetAssembly()
19101910 return m_pAssembly;
19111911}
19121912
1913+ PEAssembly* FileLoadLock::GetPEAssembly ()
1914+ {
1915+ LIMITED_METHOD_CONTRACT;
1916+ // Underlying PEAssembly pointer is stored in the constructor in base ListLockEntry::m_data.
1917+ _ASSERTE (m_data != NULL );
1918+ return (PEAssembly*)m_data;
1919+ }
1920+
19131921FileLoadLevel FileLoadLock::GetLoadLevel ()
19141922{
19151923 LIMITED_METHOD_CONTRACT;
@@ -1958,6 +1966,7 @@ BOOL FileLoadLock::CanAcquire(FileLoadLevel targetLevel)
19581966static const char *fileLoadLevelName[] =
19591967{
19601968 " CREATE" , // FILE_LOAD_CREATE
1969+ " ALLOCATE" , // FILE_LOAD_ALLOCATE
19611970 " BEGIN" , // FILE_LOAD_BEGIN
19621971 " BEFORE_TYPE_LOAD" , // FILE_LOAD_BEFORE_TYPE_LOAD
19631972 " EAGER_FIXUPS" , // FILE_LOAD_EAGER_FIXUPS
@@ -1983,7 +1992,9 @@ BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success)
19831992 if (level > m_level)
19841993 {
19851994 // Must complete each level in turn, unless we have an error
1986- CONSISTENCY_CHECK (m_pAssembly->IsError () || (level == (m_level+1 )));
1995+ CONSISTENCY_CHECK ((level == (m_level+1 )) || (m_pAssembly != nullptr && m_pAssembly->IsError ()));
1996+ CONSISTENCY_CHECK (m_pAssembly != nullptr || level < FILE_LOAD_ALLOCATE);
1997+
19871998 // Remove the lock from the list if the load is completed
19881999 if (level >= FILE_ACTIVE)
19892000 {
@@ -2019,7 +2030,7 @@ BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success)
20192030 {
20202031 m_level = (FileLoadLevel)level;
20212032
2022- if (success)
2033+ if (success && level >= FILE_LOAD_ALLOCATE )
20232034 m_pAssembly->SetLoadLevel (level);
20242035 }
20252036
@@ -2042,6 +2053,18 @@ BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success)
20422053 return FALSE ;
20432054}
20442055
2056+ void FileLoadLock::SetAssembly (Assembly* pAssembly)
2057+ {
2058+ LIMITED_METHOD_CONTRACT;
2059+
2060+ _ASSERTE (HasLock ());
2061+ _ASSERTE (m_level == FILE_LOAD_CREATE); // Only valid to set during CREATE -> ALLOCATE
2062+ _ASSERTE (m_pAssembly == nullptr );
2063+ _ASSERTE (pAssembly != nullptr && pAssembly->GetPEAssembly () == (PEAssembly *)m_data);
2064+
2065+ m_pAssembly = pAssembly;
2066+ }
2067+
20452068void FileLoadLock::SetError (Exception *ex)
20462069{
20472070 CONTRACTL
@@ -2088,10 +2111,10 @@ UINT32 FileLoadLock::Release()
20882111 return count;
20892112}
20902113
2091- FileLoadLock::FileLoadLock (PEFileListLock * pLock, PEAssembly * pPEAssembly, Assembly *pAssembly )
2114+ FileLoadLock::FileLoadLock (PEFileListLock* pLock, PEAssembly* pPEAssembly)
20922115 : ListLockEntry(pLock, pPEAssembly, " File load lock" ),
20932116 m_level((FileLoadLevel) (FILE_LOAD_CREATE)),
2094- m_pAssembly(pAssembly ),
2117+ m_pAssembly(nullptr ),
20952118 m_cachedHR(S_OK)
20962119{
20972120 WRAPPER_NO_CONTRACT;
@@ -2420,23 +2443,6 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity,
24202443
24212444 if (result == NULL )
24222445 {
2423- LoaderAllocator *pLoaderAllocator = NULL ;
2424-
2425- AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder ();
2426- // Assemblies loaded with CustomAssemblyBinder need to use a different LoaderAllocator if
2427- // marked as collectible
2428- pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator ();
2429- if (pLoaderAllocator == NULL )
2430- {
2431- pLoaderAllocator = this ->GetLoaderAllocator ();
2432- }
2433-
2434- // Allocate the DomainAssembly a bit early to avoid GC mode problems. We could potentially avoid
2435- // a rare redundant allocation by moving this closer to FileLoadLock::Create, but it's not worth it.
2436- AllocMemTracker amTracker;
2437- AllocMemTracker *pamTracker = &amTracker;
2438- NewHolder<DomainAssembly> pDomainAssembly = new DomainAssembly (pPEAssembly, pLoaderAllocator, pamTracker);
2439-
24402446 LoadLockHolder lock (this );
24412447
24422448 // Find the list lock entry
@@ -2448,20 +2454,9 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity,
24482454 result = FindAssembly (pPEAssembly, FindAssemblyOptions_IncludeFailedToLoad);
24492455 if (result == NULL )
24502456 {
2451- // We are the first one in - create the DomainAssembly
2457+ // We are the first one in - create the FileLoadLock. Creation of the Assembly will happen at FILE_LOAD_ALLOCATE stage
24522458 registerNewAssembly = true ;
2453- fileLock = FileLoadLock::Create (lock, pPEAssembly, pDomainAssembly->GetAssembly ());
2454- pDomainAssembly.SuppressRelease ();
2455- pamTracker->SuppressRelease ();
2456-
2457- // Set the assembly module to be tenured now that we know it won't be deleted
2458- pDomainAssembly->GetAssembly ()->SetIsTenured ();
2459- if (pDomainAssembly->GetAssembly ()->IsCollectible ())
2460- {
2461- // We add the assembly to the LoaderAllocator only when we are sure that it can be added
2462- // and won't be deleted in case of a concurrent load from the same ALC
2463- ((AssemblyLoaderAllocator *)pLoaderAllocator)->AddDomainAssembly (pDomainAssembly);
2464- }
2459+ fileLock = FileLoadLock::Create (lock, pPEAssembly);
24652460 }
24662461 }
24672462 else
@@ -2479,6 +2474,8 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity,
24792474 // so it will not be removed until app domain unload. So there is no need
24802475 // to release our ref count.
24812476 result = LoadAssembly (fileLock, targetLevel);
2477+ // By now FILE_LOAD_ALLOCATE should have run and the Assembly should exist
2478+ _ASSERTE (result != NULL );
24822479 }
24832480 else
24842481 {
@@ -2487,7 +2484,7 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity,
24872484
24882485 if (registerNewAssembly)
24892486 {
2490- pPEAssembly->GetAssemblyBinder ()->AddLoadedAssembly (pDomainAssembly-> GetAssembly () );
2487+ pPEAssembly->GetAssemblyBinder ()->AddLoadedAssembly (result );
24912488 }
24922489 }
24932490 else
@@ -2517,6 +2514,7 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel
25172514 STANDARD_VM_CHECK;
25182515 PRECONDITION (CheckPointer (pLock));
25192516 PRECONDITION (AppDomain::GetCurrentDomain () == this );
2517+ PRECONDITION (targetLevel >= FILE_LOAD_ALLOCATE);
25202518 POSTCONDITION (RETVAL->GetLoadLevel () >= GetCurrentFileLoadLevel ()
25212519 || RETVAL->GetLoadLevel () >= targetLevel);
25222520 POSTCONDITION (RETVAL->CheckNoError (targetLevel));
@@ -2531,6 +2529,7 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel
25312529 // Do a quick out check for the already loaded case.
25322530 if (pLock->GetLoadLevel () >= targetLevel)
25332531 {
2532+ _ASSERTE (pAssembly != nullptr );
25342533 pAssembly->ThrowIfError (targetLevel);
25352534
25362535 RETURN pAssembly;
@@ -2553,8 +2552,9 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel
25532552 if (immediateTargetLevel > limit.GetLoadLevel ())
25542553 immediateTargetLevel = limit.GetLoadLevel ();
25552554
2555+ const char *simpleName = pLock->GetPEAssembly ()->GetSimpleName ();
25562556 LOG ((LF_LOADER, LL_INFO100, " LOADER: ***%s*\t >>>Load initiated, %s/%s\n " ,
2557- pAssembly-> GetSimpleName () ,
2557+ simpleName ,
25582558 fileLoadLevelName[immediateTargetLevel], fileLoadLevelName[targetLevel]));
25592559
25602560 // Now loop and do the load incrementally to the target level.
@@ -2577,30 +2577,32 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel
25772577
25782578 LOG ((LF_LOADER,
25792579 (workLevel == FILE_LOAD_BEGIN
2580- || workLevel == FILE_LOADED
2581- || workLevel == FILE_ACTIVE)
2582- ? LL_INFO10 : LL_INFO1000,
2583- " LOADER: %p:***%s*\t loading at level %s\n " ,
2584- this , pAssembly-> GetSimpleName () , fileLoadLevelName[workLevel]));
2580+ || workLevel == FILE_LOADED
2581+ || workLevel == FILE_ACTIVE)
2582+ ? LL_INFO10 : LL_INFO1000,
2583+ " LOADER: %p:***%s*\t loading at level %s\n " ,
2584+ this , simpleName , fileLoadLevelName[workLevel]));
25852585
2586- TryIncrementalLoad (pAssembly, workLevel, fileLock);
2586+ TryIncrementalLoad (workLevel, fileLock);
25872587 }
25882588 }
25892589
25902590 if (pLock->GetLoadLevel () == immediateTargetLevel-1 )
25912591 {
25922592 LOG ((LF_LOADER, LL_INFO100, " LOADER: ***%s*\t <<<Load limited due to detected deadlock, %s\n " ,
2593- pAssembly-> GetSimpleName () ,
2593+ simpleName ,
25942594 fileLoadLevelName[immediateTargetLevel-1 ]));
25952595 }
25962596 }
25972597
25982598 LOG ((LF_LOADER, LL_INFO100, " LOADER: ***%s*\t <<<Load completed, %s\n " ,
2599- pAssembly-> GetSimpleName () ,
2599+ simpleName ,
26002600 fileLoadLevelName[pLock->GetLoadLevel ()]));
2601-
26022601 }
26032602
2603+ pAssembly = pLock->GetAssembly ();
2604+ _ASSERTE (pAssembly != nullptr ); // We should always be loading to at least FILE_LOAD_ALLOCATE, so the assembly should be created
2605+
26042606 // There may have been an error stored on the domain file by another thread, or from a previous load
26052607 pAssembly->ThrowIfError (targetLevel);
26062608
@@ -2624,19 +2626,49 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel
26242626 RETURN pAssembly;
26252627}
26262628
2627- void AppDomain::TryIncrementalLoad (Assembly *pAssembly, FileLoadLevel workLevel, FileLoadLockHolder & lockHolder)
2629+ void AppDomain::TryIncrementalLoad (FileLoadLevel workLevel, FileLoadLockHolder& lockHolder)
26282630{
26292631 STANDARD_VM_CONTRACT;
26302632
26312633 // This is factored out so we don't call EX_TRY in a loop (EX_TRY can _alloca)
26322634
26332635 BOOL released = FALSE ;
26342636 FileLoadLock* pLoadLock = lockHolder.GetValue ();
2637+ Assembly* pAssembly = pLoadLock->GetAssembly ();
26352638
26362639 EX_TRY
26372640 {
2638- // Do the work
2639- BOOL success = pAssembly->DoIncrementalLoad (workLevel);
2641+ BOOL success;
2642+ if (workLevel == FILE_LOAD_ALLOCATE)
2643+ {
2644+ // FileLoadLock should not have an assembly yet
2645+ _ASSERTE (pAssembly == NULL );
2646+
2647+ // Allocate DomainAssembly & Assembly
2648+ PEAssembly *pPEAssembly = pLoadLock->GetPEAssembly ();
2649+ AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder ();
2650+ LoaderAllocator *pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator ();
2651+ if (pLoaderAllocator == NULL )
2652+ pLoaderAllocator = this ->GetLoaderAllocator ();
2653+
2654+ AllocMemTracker amTracker;
2655+ AllocMemTracker *pamTracker = &amTracker;
2656+ NewHolder<DomainAssembly> pDomainAssembly = new DomainAssembly (pPEAssembly, pLoaderAllocator, pamTracker);
2657+ pLoadLock->SetAssembly (pDomainAssembly->GetAssembly ());
2658+ pDomainAssembly->GetAssembly ()->SetIsTenured ();
2659+ if (pDomainAssembly->GetAssembly ()->IsCollectible ())
2660+ {
2661+ ((AssemblyLoaderAllocator *)pLoaderAllocator)->AddDomainAssembly (pDomainAssembly);
2662+ }
2663+ pDomainAssembly.SuppressRelease ();
2664+ pamTracker->SuppressRelease ();
2665+ pAssembly = pLoadLock->GetAssembly ();
2666+ success = TRUE ;
2667+ }
2668+ else
2669+ {
2670+ success = pAssembly->DoIncrementalLoad (workLevel);
2671+ }
26402672
26412673 // Complete the level.
26422674 if (pLoadLock->CompleteLoadLevel (workLevel, success) &&
@@ -2651,9 +2683,9 @@ void AppDomain::TryIncrementalLoad(Assembly *pAssembly, FileLoadLevel workLevel,
26512683 {
26522684 Exception *pEx = GET_EXCEPTION ();
26532685
2654- // We will cache this error and wire this load to forever fail,
2686+ // We will cache this error and wire this load to forever fail,
26552687 // unless the exception is transient or the file is loaded OK but just cannot execute
2656- if (!pEx->IsTransient () && !pAssembly->IsLoaded ())
2688+ if (pAssembly != nullptr && !pEx->IsTransient () && !pAssembly->IsLoaded ())
26572689 {
26582690 if (released)
26592691 {
0 commit comments