Index: libcfa/src/bits/defs.hfa
===================================================================
--- libcfa/src/bits/defs.hfa	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/bits/defs.hfa	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -26,5 +26,4 @@
 
 typedef void (* fptr_t)();
-typedef uint64_t func_id_t;
 typedef int_fast16_t __lock_size_t;
 
Index: libcfa/src/concurrency/invoke.h
===================================================================
--- libcfa/src/concurrency/invoke.h	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/concurrency/invoke.h	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -159,7 +159,4 @@
 		// last function that acquired monitors
 		fptr_t func;
-
-		// hash-based function identity for cross-TU matching
-		func_id_t func_id;
 	};
 
@@ -291,12 +288,10 @@
 			(this.size){0};
 			(this.func){NULL};
-			(this.func_id){0};
-		}
-
-		static inline void ?{}(__monitor_group_t & this, struct monitor$ ** data, __lock_size_t size, fptr_t func, func_id_t func_id) {
+		}
+
+		static inline void ?{}(__monitor_group_t & this, struct monitor$ ** data, __lock_size_t size, fptr_t func) {
 			(this.data){data};
 			(this.size){size};
 			(this.func){func};
-			(this.func_id){func_id};
 		}
 
@@ -304,12 +299,5 @@
 			if( (lhs.data != 0) != (rhs.data != 0) ) return false;
 			if( lhs.size != rhs.size ) return false;
-			// Use hash-based comparison when both sides have a valid func_id,
-			// otherwise fall back to function pointer comparison (for library code
-			// like join() that cannot compute the hash at compile time).
-			if( lhs.func_id != 0 && rhs.func_id != 0 ) {
-				if( lhs.func_id != rhs.func_id ) return false;
-			} else {
-				if( lhs.func != rhs.func ) return false;
-			}
+			if( lhs.func != rhs.func ) return false;
 
 			// Check that all the monitors match
@@ -326,5 +314,4 @@
 			lhs.size = rhs.size;
 			lhs.func = rhs.func;
-			lhs.func_id = rhs.func_id;
 		}
 	}
Index: libcfa/src/concurrency/kernel/startup.cfa
===================================================================
--- libcfa/src/concurrency/kernel/startup.cfa	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/concurrency/kernel/startup.cfa	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -517,5 +517,5 @@
 	doregister(curr_cluster, this);
 
-	monitors{ &self_mon_p, 1, (fptr_t)0, (func_id_t)0 };
+	monitors{ &self_mon_p, 1, (fptr_t)0 };
 }
 
Index: libcfa/src/concurrency/monitor.cfa
===================================================================
--- libcfa/src/concurrency/monitor.cfa	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/concurrency/monitor.cfa	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -140,5 +140,5 @@
 }
 
-static void __dtor_enter( monitor$ * this, fptr_t func, func_id_t func_id, bool join ) {
+static void __dtor_enter( monitor$ * this, fptr_t func, bool join ) {
 	thread$ * thrd = active_thread();
 	#if defined( __CFA_WITH_VERIFY__ )
@@ -190,5 +190,5 @@
 	__lock_size_t count = 1;
 	monitor$ ** monitors = &this;
-	__monitor_group_t group = { &this, 1, func, func_id };
+	__monitor_group_t group = { &this, 1, func };
 	if ( is_accepted( this, group) ) {
 		__cfaabi_dbg_print_safe( "Kernel : mon accepts dtor, block and signal it \n" );
@@ -341,5 +341,5 @@
 // Ctor for monitor guard
 // Sorts monitors before entering
-void ?{}( monitor_guard_t & this, monitor$ * m [], __lock_size_t count, fptr_t func, func_id_t func_id ) libcfa_public {
+void ?{}( monitor_guard_t & this, monitor$ * m [], __lock_size_t count, fptr_t func ) libcfa_public {
 	thread$ * thrd = active_thread();
 
@@ -355,10 +355,10 @@
 
 	// Update thread context (needed for conditions)
-	(thrd->monitors){m, count, func, func_id};
+	(thrd->monitors){m, count, func};
 
 	// __cfaabi_dbg_print_safe( "MGUARD : enter %d\n", count);
 
 	// Enter the monitors in order
-	__monitor_group_t group = {this.m, this.count, func, func_id};
+	__monitor_group_t group = {this.m, this.count, func};
 	enter( group );
 
@@ -367,5 +367,5 @@
 
 void ?{}( monitor_guard_t & this, monitor$ * m [], __lock_size_t count ) libcfa_public {
-	this{ m, count, 0p, 0 };
+	this{ m, count, 0p };
 }
 
@@ -386,5 +386,5 @@
 // Ctor for monitor guard
 // Sorts monitors before entering
-void ?{}( monitor_dtor_guard_t & this, monitor$ * m [], fptr_t func, func_id_t func_id, bool join ) libcfa_public {
+void ?{}( monitor_dtor_guard_t & this, monitor$ * m [], fptr_t func, bool join ) libcfa_public {
 	// optimization
 	thread$ * thrd = active_thread();
@@ -400,7 +400,7 @@
 
 	// Update thread context (needed for conditions)
-	(thrd->monitors){m, 1, func, func_id};
-
-	__dtor_enter( this.m, func, func_id, join );
+	(thrd->monitors){m, 1, func};
+
+	__dtor_enter( this.m, func, join );
 }
 
Index: libcfa/src/concurrency/monitor.hfa
===================================================================
--- libcfa/src/concurrency/monitor.hfa	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/concurrency/monitor.hfa	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -48,5 +48,5 @@
 };
 
-void ?{}( monitor_guard_t & this, monitor$ ** m, __lock_size_t count, void (*func)(), func_id_t func_id );
+void ?{}( monitor_guard_t & this, monitor$ ** m, __lock_size_t count, void (*func)() );
 void ?{}( monitor_guard_t & this, monitor$ ** m, __lock_size_t count );
 void ^?{}( monitor_guard_t & this );
@@ -58,5 +58,5 @@
 };
 
-void ?{}( monitor_dtor_guard_t & this, monitor$ ** m, void (*func)(), func_id_t func_id, bool join );
+void ?{}( monitor_dtor_guard_t & this, monitor$ ** m, void (*func)(), bool join );
 void ^?{}( monitor_dtor_guard_t & this );
 
Index: libcfa/src/concurrency/thread.cfa
===================================================================
--- libcfa/src/concurrency/thread.cfa	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/concurrency/thread.cfa	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -61,5 +61,5 @@
 
 	doregister(curr_cluster, this);
-	monitors{ &self_mon_p, 1, (fptr_t)0, (func_id_t)0 };
+	monitors{ &self_mon_p, 1, (fptr_t)0 };
 }
 
@@ -93,5 +93,5 @@
     | { EHM_DEFAULT_VTABLE(ThreadCancelled(T)); })
 void ?{}( thread_dtor_guard_t & this,
-		T & thrd, func_id_t func_id, void(*cancelHandler)(ThreadCancelled(T) &)) {
+		T & thrd, void(*cancelHandler)(ThreadCancelled(T) &)) {
 	monitor$ * m = get_monitor(thrd);
 	thread$ * desc = get_thread(thrd);
@@ -100,6 +100,5 @@
 	void (*dtor)(T& mutex this) = ^?{};
 	bool join = cancelHandler != (void(*)(ThreadCancelled(T)&))0;
-	this.func_id = func_id;
-	(this.mg){&m, (void(*)())dtor, func_id, join};
+	(this.mg){&m, (void(*)())dtor, join};
 
 
@@ -173,5 +172,5 @@
 	| { EHM_DEFAULT_VTABLE(ThreadCancelled(T)); })
 T & join( T & this ) {
-	thread_dtor_guard_t guard = { this, (func_id_t)0, defaultResumptionHandler };
+	thread_dtor_guard_t guard = { this, defaultResumptionHandler };
 	return this;
 }
Index: libcfa/src/concurrency/thread.hfa
===================================================================
--- libcfa/src/concurrency/thread.hfa	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ libcfa/src/concurrency/thread.hfa	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -83,10 +83,9 @@
 struct thread_dtor_guard_t {
 	monitor_dtor_guard_t mg;
-	func_id_t func_id;
 };
 
 forall( T & | is_thread(T) | IS_EXCEPTION(ThreadCancelled(T))
 	| { EHM_DEFAULT_VTABLE(ThreadCancelled(T)); })
-void ?{}( thread_dtor_guard_t & this, T & thrd, func_id_t func_id, void(*)(ThreadCancelled(T) &) );
+void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) );
 void ^?{}( thread_dtor_guard_t & this );
 
Index: src/Concurrency/Keywords.cpp
===================================================================
--- src/Concurrency/Keywords.cpp	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ src/Concurrency/Keywords.cpp	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -29,5 +29,4 @@
 #include "Common/Examine.hpp"
 #include "Common/Utility.hpp"
-#include "Concurrency/MutexFuncHash.hpp"
 #include "Common/UniqueName.hpp"
 #include "ControlStruct/LabelGenerator.hpp"
@@ -1078,5 +1077,5 @@
 
 	// In reverse order:
-	// monitor_dtor_guard_t __guard = { __monitor, func, func_id, false };
+	// monitor_dtor_guard_t __guard = { __monitor, func, false };
 	mutBody->push_front(
 		new ast::DeclStmt( location, new ast::ObjectDecl(
@@ -1095,6 +1094,4 @@
 							generic_func,
 							ast::ExplicitCast ) ),
-					new ast::SingleInit( location,
-						Concurrency::hashMangleExpr( location, func ) ),
 					new ast::SingleInit( location,
 						ast::ConstantExpr::from_bool( location, false ) ),
@@ -1178,6 +1175,4 @@
 						ast::ExplicitCast
 					) ),
-					new ast::SingleInit( location,
-						Concurrency::hashMangleExpr( location, func ) ),
 				},
 				{},
@@ -1480,5 +1475,5 @@
 
 ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
-		const ast::FunctionDecl* func, const ast::CompoundStmt * body,
+		const ast::FunctionDecl*, const ast::CompoundStmt * body,
 		const std::vector<const ast::DeclWithType * > & args ) {
 	assert( args.size() == 1 );
@@ -1492,5 +1487,5 @@
 	const CodeLocation & location = mutBody->location;
 
-	// thread_dtor_guard_t __guard = { this, func_id, intptr( 0 ) };
+	// thread_dtor_guard_t __guard = { this, intptr( 0 ) };
 	mutBody->push_front( new ast::DeclStmt(
 		location,
@@ -1505,6 +1500,4 @@
 						new ast::CastExpr( location,
 							new ast::VariableExpr( location, arg ), argType ) ),
-					new ast::SingleInit( location,
-						Concurrency::hashMangleExpr( location, func ) ),
 					new ast::SingleInit(
 						location,
Index: c/Concurrency/MutexFuncHash.hpp
===================================================================
--- src/Concurrency/MutexFuncHash.hpp	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ 	(revision )
@@ -1,50 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// MutexFuncHash.hpp -- Hash utility for mutex function identity.
-//
-// Author           : Matthew Au-Yeung
-// Created On       : Tue Jan 28 2026
-//
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-#include "AST/Decl.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Type.hpp"
-#include "SymTab/Mangler.hpp"
-
-namespace Concurrency {
-
-// FNV-1a hash of a function declaration's mangled name.
-// Used to identify mutex functions across translation units,
-// since function pointers may differ for static inline functions.
-static inline uint64_t hashMangle( const ast::DeclWithType * decl ) {
-	std::string name = Mangle::mangle( decl );
-	uint64_t hash = 14695981039346656037ULL; // FNV offset basis
-	for ( char c : name ) {
-		hash ^= static_cast<uint64_t>( c );
-		hash *= 1099511628211ULL; // FNV prime
-	}
-	return hash;
-}
-
-// Create a ConstantExpr for the hash with proper ULL suffix to avoid
-// C compiler warnings about large unsigned constants.
-static inline ast::ConstantExpr * hashMangleExpr(
-		const CodeLocation & location, const ast::DeclWithType * decl ) {
-	uint64_t hash = hashMangle( decl );
-	return new ast::ConstantExpr{
-		location,
-		new ast::BasicType{ ast::BasicKind::LongLongUnsignedInt },
-		std::to_string( hash ) + "ull",
-		(unsigned long long)hash };
-}
-
-} // namespace Concurrency
Index: src/Concurrency/Waitfor.cpp
===================================================================
--- src/Concurrency/Waitfor.cpp	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ src/Concurrency/Waitfor.cpp	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -22,5 +22,4 @@
 #include "InitTweak/InitTweak.hpp"
 #include "ResolvExpr/Resolver.hpp"
-#include "Concurrency/MutexFuncHash.hpp"
 
 #include "AST/Print.hpp"
@@ -332,8 +331,4 @@
 		makeAccStmt( location, acceptables, index, "func",
 			funcExpr, context ),
-		makeAccStmt( location, acceptables, index, "func_id",
-			Concurrency::hashMangleExpr( location,
-				variableExpr->var.strict_as<ast::DeclWithType>() ),
-			context ),
 		makeAccStmt( location, acceptables, index, "data",
 			new ast::VariableExpr( location, monitors ), context ),
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision a0548c298812fe171176559746d6d55e06664ac4)
+++ src/Validate/Autogen.cpp	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -402,5 +402,17 @@
 	}
 
-	return genProto( "^?{}", { dst }, {} );
+	ast::FunctionDecl * decl = genProto( "^?{}", { dst }, {} );
+	// For concurrent types, remove static storage and inline specifier, and add
+	// cfa_linkonce attribute so the destructor has external linkage with linkonce
+	// semantics. This is required for waitfor to work correctly across translation
+	// units - the function pointer must be the same everywhere, and cfa_linkonce
+	// ensures only one definition survives linking.
+	if ( isConcurrentType() ) {
+		auto mut = ast::mutate( decl );
+		mut->storage = ast::Storage::Classes();
+		mut->funcSpec = ast::Function::Specs();
+		mut->attributes.push_back( new ast::Attribute( "cfa_linkonce" ) );
+	}
+	return decl;
 }
 
