@@ -308,7 +308,7 @@ jl_method_t *jl_mk_builtin_func(jl_datatype_t *dt, jl_sym_t *sname, jl_fptr_args
308308 m -> isva = 1 ;
309309 m -> nargs = 2 ;
310310 jl_atomic_store_relaxed (& m -> primary_world , 1 );
311- jl_atomic_store_relaxed (& m -> dispatch_status , METHOD_SIG_LATEST_ONLY | METHOD_SIG_LATEST_ONLY );
311+ jl_atomic_store_relaxed (& m -> dispatch_status , METHOD_SIG_LATEST_ONLY | METHOD_SIG_LATEST_WHICH );
312312 m -> sig = (jl_value_t * )jl_anytuple_type ;
313313 m -> slot_syms = jl_an_empty_string ;
314314 m -> nospecialize = 0 ;
@@ -1505,22 +1505,26 @@ jl_method_instance_t *cache_method(
15051505 size_t world , size_t min_valid , size_t max_valid ,
15061506 jl_svec_t * sparams )
15071507{
1508- // caller must hold the parent->writelock
1508+ // caller must hold the parent->writelock, which this releases
15091509 // short-circuit (now that we hold the lock) if this entry is already present
15101510 int8_t offs = mc ? jl_cachearg_offset () : 1 ;
15111511 { // scope block
15121512 if (mc ) {
15131513 jl_genericmemory_t * leafcache = jl_atomic_load_relaxed (& mc -> leafcache );
15141514 jl_typemap_entry_t * entry = lookup_leafcache (leafcache , (jl_value_t * )tt , world );
1515- if (entry )
1515+ if (entry ) {
1516+ if (mc ) JL_UNLOCK (& mc -> writelock );
15161517 return entry -> func .linfo ;
1518+ }
15171519 }
15181520 struct jl_typemap_assoc search = {(jl_value_t * )tt , world , NULL };
15191521 jl_typemap_t * cacheentry = jl_atomic_load_relaxed (cache );
15201522 assert (cacheentry != NULL );
15211523 jl_typemap_entry_t * entry = jl_typemap_assoc_by_type (cacheentry , & search , offs , /*subtype*/ 1 );
1522- if (entry && entry -> func .value )
1524+ if (entry && entry -> func .value ) {
1525+ if (mc ) JL_UNLOCK (& mc -> writelock );
15231526 return entry -> func .linfo ;
1527+ }
15241528 }
15251529
15261530 jl_method_instance_t * newmeth = NULL ;
@@ -1533,6 +1537,7 @@ jl_method_instance_t *cache_method(
15331537 JL_GC_PUSH1 (& newentry );
15341538 jl_typemap_insert (cache , parent , newentry , offs );
15351539 JL_GC_POP ();
1540+ if (mc ) JL_UNLOCK (& mc -> writelock );
15361541 return newmeth ;
15371542 }
15381543 }
@@ -1577,8 +1582,11 @@ jl_method_instance_t *cache_method(
15771582 if (newmeth -> cache_with_orig )
15781583 cache_with_orig = 1 ;
15791584
1585+ // Capture world counter at start to detect races
1586+ size_t current_world = mc ? jl_atomic_load_acquire (& jl_world_counter ) : ~(size_t )0 ;
1587+
15801588 jl_tupletype_t * cachett = tt ;
1581- jl_svec_t * guardsigs = jl_emptysvec ;
1589+ jl_svec_t * guardsigs = jl_emptysvec ;
15821590 if (!cache_with_orig && mt ) {
15831591 // now examine what will happen if we chose to use this sig in the cache
15841592 size_t min_valid2 = 1 ;
@@ -1648,6 +1656,10 @@ jl_method_instance_t *cache_method(
16481656 }
16491657 }
16501658
1659+ int unconstrained_max = max_valid == ~(size_t )0 ;
1660+ if (max_valid > current_world )
1661+ max_valid = current_world ;
1662+
16511663 // now scan `cachett` and ensure that `Type{T}` in the cache will be matched exactly by `typeof(T)`
16521664 // and also reduce the complexity of rejecting this entry in the cache
16531665 // by replacing non-simple types with jl_any_type to build a new `type`
@@ -1726,12 +1738,91 @@ jl_method_instance_t *cache_method(
17261738 }
17271739 }
17281740 }
1741+ if (mc ) {
1742+ JL_UNLOCK (& mc -> writelock );
1743+
1744+ // Only set METHOD_SIG_LATEST_ONLY on method instance if method does NOT have the bit, no guards required, and min_valid == primary_world
1745+ int should_set_dispatch_status = !(jl_atomic_load_relaxed (& definition -> dispatch_status ) & METHOD_SIG_LATEST_ONLY ) &&
1746+ (!cache_with_orig && jl_svec_len (guardsigs ) == 0 ) &&
1747+ min_valid == jl_atomic_load_relaxed (& definition -> primary_world ) &&
1748+ !(jl_atomic_load_relaxed (& newmeth -> dispatch_status ) & METHOD_SIG_LATEST_ONLY );
1749+
1750+ // Combined trylock for both dispatch_status setting and max_world restoration
1751+ if ((should_set_dispatch_status || unconstrained_max ) &&
1752+ jl_atomic_load_relaxed (& jl_world_counter ) == current_world ) {
1753+ JL_LOCK (& world_counter_lock );
1754+ if (jl_atomic_load_relaxed (& jl_world_counter ) == current_world ) {
1755+ if (should_set_dispatch_status ) {
1756+ jl_atomic_store_relaxed (& newmeth -> dispatch_status , METHOD_SIG_LATEST_ONLY );
1757+ }
1758+ if (unconstrained_max ) {
1759+ jl_atomic_store_relaxed (& newentry -> max_world , ~(size_t )0 );
1760+ }
1761+ }
1762+ JL_UNLOCK (& world_counter_lock );
1763+ }
1764+ }
17291765
17301766 JL_GC_POP ();
17311767 return newmeth ;
17321768}
17331769
1734- static jl_method_match_t * _gf_invoke_lookup (jl_value_t * types JL_PROPAGATES_ROOT , jl_methtable_t * mt , size_t world , size_t * min_valid , size_t * max_valid );
1770+ static void _jl_promote_ci_to_current (jl_code_instance_t * ci , size_t validated_world ) JL_NOTSAFEPOINT
1771+ {
1772+ if (jl_atomic_load_relaxed (& ci -> max_world ) != validated_world )
1773+ return ;
1774+ jl_atomic_store_relaxed (& ci -> max_world , ~(size_t )0 );
1775+ jl_svec_t * edges = jl_atomic_load_relaxed (& ci -> edges );
1776+ for (size_t i = 0 ; i < jl_svec_len (edges ); i ++ ) {
1777+ jl_value_t * edge = jl_svecref (edges , i );
1778+ if (!jl_is_code_instance (edge ))
1779+ continue ;
1780+ _jl_promote_ci_to_current ((jl_code_instance_t * )edge , validated_world );
1781+ }
1782+ }
1783+
1784+ JL_DLLEXPORT void jl_promote_cis_to_current (jl_code_instance_t * * cis , size_t n , size_t validated_world )
1785+ {
1786+ size_t current_world = jl_atomic_load_relaxed (& jl_world_counter );
1787+ // No need to acquire the lock if we've been invalidated anyway
1788+ if (current_world > validated_world )
1789+ return ;
1790+ JL_LOCK (& world_counter_lock );
1791+ current_world = jl_atomic_load_relaxed (& jl_world_counter );
1792+ if (current_world == validated_world ) {
1793+ for (size_t i = 0 ; i < n ; i ++ ) {
1794+ _jl_promote_ci_to_current (cis [i ], validated_world );
1795+ }
1796+ }
1797+ JL_UNLOCK (& world_counter_lock );
1798+ }
1799+
1800+ JL_DLLEXPORT void jl_promote_ci_to_current (jl_code_instance_t * ci , size_t validated_world )
1801+ {
1802+ jl_promote_cis_to_current (& ci , 1 , validated_world );
1803+ }
1804+
1805+ JL_DLLEXPORT void jl_promote_mi_to_current (jl_method_instance_t * mi , size_t min_world , size_t validated_world )
1806+ {
1807+ size_t current_world = jl_atomic_load_relaxed (& jl_world_counter );
1808+ // No need to acquire the lock if we've been invalidated anyway
1809+ if (current_world > validated_world )
1810+ return ;
1811+ // Only set METHOD_SIG_LATEST_ONLY on method instance if method does NOT have the bit and min_valid == primary_world
1812+ jl_method_t * definition = mi -> def .method ;
1813+ if ((jl_atomic_load_relaxed (& definition -> dispatch_status ) & METHOD_SIG_LATEST_ONLY ) ||
1814+ min_world != jl_atomic_load_relaxed (& definition -> primary_world ) ||
1815+ (jl_atomic_load_relaxed (& mi -> dispatch_status ) & METHOD_SIG_LATEST_ONLY ))
1816+ return ;
1817+ JL_LOCK (& world_counter_lock );
1818+ current_world = jl_atomic_load_relaxed (& jl_world_counter );
1819+ if (current_world == validated_world ) {
1820+ jl_atomic_store_relaxed (& mi -> dispatch_status , METHOD_SIG_LATEST_ONLY );
1821+ }
1822+ JL_UNLOCK (& world_counter_lock );
1823+ }
1824+
1825+ static jl_method_match_t * _gf_invoke_lookup (jl_value_t * types JL_PROPAGATES_ROOT , jl_methtable_t * mt , size_t world , int cache , size_t * min_valid , size_t * max_valid );
17351826
17361827JL_DLLEXPORT jl_typemap_entry_t * jl_mt_find_cache_entry (jl_methcache_t * mc JL_PROPAGATES_ROOT , jl_datatype_t * tt JL_MAYBE_UNROOTED JL_ROOTS_TEMPORARILY , size_t world )
17371828{ // exported only for debugging purposes, not for casual use
@@ -1765,11 +1856,13 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATE
17651856 if (!mi ) {
17661857 size_t min_valid = 0 ;
17671858 size_t max_valid = ~(size_t )0 ;
1768- matc = _gf_invoke_lookup ((jl_value_t * )tt , jl_method_table , world , & min_valid , & max_valid );
1859+ matc = _gf_invoke_lookup ((jl_value_t * )tt , jl_method_table , world , 0 , & min_valid , & max_valid );
17691860 if (matc ) {
17701861 jl_method_t * m = matc -> method ;
17711862 jl_svec_t * env = matc -> sparams ;
17721863 mi = cache_method (jl_method_table , mc , & mc -> cache , (jl_value_t * )mc , tt , m , world , min_valid , max_valid , env );
1864+ JL_GC_POP ();
1865+ return mi ;
17731866 }
17741867 }
17751868 JL_UNLOCK (& mc -> writelock );
@@ -2126,6 +2219,7 @@ static int _invalidate_dispatch_backedges(jl_method_instance_t *mi, jl_value_t *
21262219// invalidate cached methods that overlap this definition
21272220static void invalidate_backedges (jl_method_instance_t * replaced_mi , size_t max_world , const char * why )
21282221{
2222+ // Reset dispatch_status when method instance is replaced
21292223 JL_LOCK (& replaced_mi -> def .method -> writelock );
21302224 _invalidate_backedges (replaced_mi , NULL , max_world , 1 );
21312225 JL_UNLOCK (& replaced_mi -> def .method -> writelock );
@@ -2136,6 +2230,7 @@ static void invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_w
21362230 jl_array_ptr_1d_push (_jl_debug_method_invalidation , loctag );
21372231 JL_GC_POP ();
21382232 }
2233+ jl_atomic_store_relaxed (& replaced_mi -> dispatch_status , 0 );
21392234}
21402235
21412236// add a backedge from callee to caller
@@ -2633,6 +2728,8 @@ void jl_method_table_activate(jl_typemap_entry_t *newentry)
26332728 // call invalidate_backedges(mi, max_world, "jl_method_table_insert");
26342729 // but ignore invoke-type edges
26352730 int invalidatedmi = _invalidate_dispatch_backedges (mi , type , m , d , n , replaced_dispatch , ambig , max_world , morespec );
2731+ if (replaced_dispatch )
2732+ jl_atomic_store_relaxed (& mi -> dispatch_status , 0 );
26362733 jl_array_ptr_1d_push (oldmi , (jl_value_t * )mi );
26372734 if (_jl_debug_method_invalidation && invalidatedmi ) {
26382735 jl_array_ptr_1d_push (_jl_debug_method_invalidation , (jl_value_t * )mi );
@@ -3404,7 +3501,6 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *matc
34043501 assert (mc );
34053502 JL_LOCK (& mc -> writelock );
34063503 mi = cache_method (jl_method_get_table (m ), mc , & mc -> cache , (jl_value_t * )mc , ti , m , world , min_valid , max_valid , env );
3407- JL_UNLOCK (& mc -> writelock );
34083504 }
34093505 else {
34103506 jl_value_t * tt = jl_normalize_to_compilable_sig (ti , env , m , 1 );
@@ -3892,15 +3988,15 @@ JL_DLLEXPORT jl_value_t *jl_apply_generic(jl_value_t *F, jl_value_t **args, uint
38923988 return _jl_invoke (F , args , nargs , mfunc , world );
38933989}
38943990
3895- static jl_method_match_t * _gf_invoke_lookup (jl_value_t * types JL_PROPAGATES_ROOT , jl_methtable_t * mt , size_t world , size_t * min_valid , size_t * max_valid )
3991+ static jl_method_match_t * _gf_invoke_lookup (jl_value_t * types JL_PROPAGATES_ROOT , jl_methtable_t * mt , size_t world , int cache_result , size_t * min_valid , size_t * max_valid )
38963992{
38973993 jl_value_t * unw = jl_unwrap_unionall ((jl_value_t * )types );
38983994 if (!jl_is_tuple_type (unw ))
38993995 return NULL ;
39003996 if (jl_tparam0 (unw ) == jl_bottom_type )
39013997 return NULL ;
39023998 jl_methcache_t * mc = ((jl_methtable_t * )mt )-> cache ;
3903- jl_value_t * matches = ml_matches ((jl_methtable_t * )mt , mc , (jl_tupletype_t * )types , 1 , 0 , 0 , world , 1 , min_valid , max_valid , NULL );
3999+ jl_value_t * matches = ml_matches ((jl_methtable_t * )mt , mc , (jl_tupletype_t * )types , 1 , 0 , 0 , world , cache_result , min_valid , max_valid , NULL );
39044000 if (matches == jl_nothing || jl_array_nrows (matches ) != 1 )
39054001 return NULL ;
39064002 jl_method_match_t * matc = (jl_method_match_t * )jl_array_ptr_ref (matches , 0 );
@@ -3914,7 +4010,7 @@ JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_value_t *types, jl_value_t *mt,
39144010 size_t max_valid = ~(size_t )0 ;
39154011 if (mt == jl_nothing )
39164012 mt = (jl_value_t * )jl_method_table ;
3917- jl_method_match_t * matc = _gf_invoke_lookup (types , (jl_methtable_t * )mt , world , & min_valid , & max_valid );
4013+ jl_method_match_t * matc = _gf_invoke_lookup (types , (jl_methtable_t * )mt , world , 1 , & min_valid , & max_valid );
39184014 if (matc == NULL )
39194015 return jl_nothing ;
39204016 return (jl_value_t * )matc -> method ;
@@ -3925,7 +4021,7 @@ JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup_worlds(jl_value_t *types, jl_value_
39254021{
39264022 if (mt == jl_nothing )
39274023 mt = (jl_value_t * )jl_method_table ;
3928- jl_method_match_t * matc = _gf_invoke_lookup (types , (jl_methtable_t * )mt , world , min_world , max_world );
4024+ jl_method_match_t * matc = _gf_invoke_lookup (types , (jl_methtable_t * )mt , world , 1 , min_world , max_world );
39294025 if (matc == NULL )
39304026 return jl_nothing ;
39314027 return (jl_value_t * )matc ;
@@ -3987,7 +4083,6 @@ jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, jl_value
39874083 int sub = jl_subtype_matching ((jl_value_t * )tt , (jl_value_t * )method -> sig , & tpenv );
39884084 assert (sub ); (void )sub ;
39894085 }
3990-
39914086 mfunc = cache_method (NULL , NULL , & method -> invokes , (jl_value_t * )method , tt , method , 1 , 1 , ~(size_t )0 , tpenv );
39924087 }
39934088 JL_UNLOCK (& method -> writelock );
@@ -4486,26 +4581,28 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
44864581 if (entry && (((jl_datatype_t * )unw )-> isdispatchtuple || entry -> guardsigs == jl_emptysvec )) {
44874582 jl_method_instance_t * mi = entry -> func .linfo ;
44884583 jl_method_t * meth = mi -> def .method ;
4489- if (!jl_is_unionall (meth -> sig ) && ((jl_datatype_t * )unw )-> isdispatchtuple ) {
4490- env .match .env = jl_emptysvec ;
4491- env .match .ti = unw ;
4492- }
4493- else {
4494- // this just calls jl_subtype_env (since we know that `type <: meth->sig` by transitivity)
4495- env .match .ti = jl_type_intersection_env ((jl_value_t * )type , (jl_value_t * )meth -> sig , & env .match .env );
4496- }
4497- env .matc = make_method_match ((jl_tupletype_t * )env .match .ti ,
4498- env .match .env , meth , FULLY_COVERS );
4499- env .t = (jl_value_t * )jl_alloc_vec_any (1 );
4500- jl_array_ptr_set (env .t , 0 , env .matc );
45014584 size_t min_world = jl_atomic_load_relaxed (& entry -> min_world );
4502- size_t max_world = jl_atomic_load_relaxed (& entry -> max_world );
4503- if (* min_valid < min_world )
4504- * min_valid = min_world ;
4505- if (* max_valid > max_world )
4506- * max_valid = max_world ;
4507- JL_GC_POP ();
4508- return env .t ;
4585+ if (min_world == meth -> primary_world ) {
4586+ size_t max_world = jl_atomic_load_relaxed (& entry -> max_world );
4587+ if (!jl_is_unionall (meth -> sig ) && ((jl_datatype_t * )unw )-> isdispatchtuple ) {
4588+ env .match .env = jl_emptysvec ;
4589+ env .match .ti = unw ;
4590+ }
4591+ else {
4592+ // this just calls jl_subtype_env (since we know that `type <: meth->sig` by transitivity)
4593+ env .match .ti = jl_type_intersection_env ((jl_value_t * )type , (jl_value_t * )meth -> sig , & env .match .env );
4594+ }
4595+ env .matc = make_method_match ((jl_tupletype_t * )env .match .ti ,
4596+ env .match .env , meth , FULLY_COVERS );
4597+ env .t = (jl_value_t * )jl_alloc_vec_any (1 );
4598+ jl_array_ptr_set (env .t , 0 , env .matc );
4599+ if (* min_valid < min_world )
4600+ * min_valid = min_world ;
4601+ if (* max_valid > max_world )
4602+ * max_valid = max_world ;
4603+ JL_GC_POP ();
4604+ return env .t ;
4605+ }
45094606 }
45104607 }
45114608 }
@@ -4770,7 +4867,6 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
47704867 jl_svec_t * tpenv = env .matc -> sparams ;
47714868 JL_LOCK (& mc -> writelock );
47724869 cache_method (mt , mc , & mc -> cache , (jl_value_t * )mc , (jl_tupletype_t * )unw , meth , world , env .match .min_valid , env .match .max_valid , tpenv );
4773- JL_UNLOCK (& mc -> writelock );
47744870 }
47754871 }
47764872 * min_valid = env .match .min_valid ;
0 commit comments