Index: libcfa/src/concurrency/locks.cfa
===================================================================
--- libcfa/src/concurrency/locks.cfa	(revision 14a51d3104b5022bc886f4bc8e602e7c8f568565)
+++ libcfa/src/concurrency/locks.cfa	(revision 95330c335047f8584d6529b923b2a80afc78698f)
@@ -583,64 +583,95 @@
 	}
 }
+
 //-----------------------------------------------------------------------------
-// Semaphore
-void ?{}( semaphore & this, int count = 1 ) {
-	(this.lock){};
-	this.count = count;
-	(this.waiting){};
-}
-void ^?{}(semaphore & this) {}
-
-bool P(semaphore & this) with( this ){
-	lock( lock __cfaabi_dbg_ctx2 );
-	count -= 1;
-	if ( count < 0 ) {
-		// queue current task
-		append( waiting, active_thread() );
-
-		// atomically release spin lock and block
-		unlock( lock );
+// Semaphore, counting
+
+// PRIVATE
+
+thread$ * V( semaphore & sem, const bool doUnpark ) with( sem ) {
+	thread$ * thrd = 0p;
+	lock( lock$ __cfaabi_dbg_ctx2 );
+	count$ += 1;
+	if ( count$ <= 0 ) {
+		thrd = pop_head( waiting$ );						// remove task at head of waiting list
+	}
+	unlock( lock$ );
+	if ( doUnpark ) unpark( thrd );						// make new owner
+	return thrd;
+}
+
+// PUBLIC
+
+void ?{}( semaphore & sem, ssize_t count = 1 ) with( sem ) {
+	count$ = count;
+	(lock$){};
+	(waiting$){};
+}
+
+bool P( semaphore & sem ) with( sem ) {
+	lock( lock$ __cfaabi_dbg_ctx2 );
+	count$ -= 1;
+	if ( count$ < 0 ) {									// in use ?
+		append( waiting$, active_thread() );				// queue current task
+		unlock( lock$ );									// atomically release spin lock and block
 		park();
-		return true;
+		return false;
+	} // if
+	unlock( lock$ );
+	return true;
+}
+
+bool P( semaphore & sem, semaphore & lock ) with( sem ) {
+	lock( lock$ __cfaabi_dbg_ctx2 );
+	if ( &sem == &lock ) {								// perform operation on self ?
+		if ( count$ < 0 ) {								// V my semaphore
+			unpark( pop_head( waiting$ ) );				// unblock task at head of waiting list
+		} // if
 	} else {
-		unlock( lock );
+		V( sem );										// V other semaphore
+		count$ -= 1;
+	} // if
+
+	if ( count$ < 0 ) {									// in use ?
+		append( waiting$, active_thread() );			// queue current task
+		unlock( lock$ );								// atomically release spin lock and block
+		park();
 		return false;
-	}
-}
-
-thread$ * V (semaphore & this, const bool doUnpark ) with( this ) {
+	} // if
+	unlock( lock$ );
+	return true;
+}
+
+bool try_P( semaphore & sem ) with( sem ) {
+	lock( lock$ __cfaabi_dbg_ctx2 );
+	if ( count$ <= 0 ) {								// in use ?
+		unlock( lock$ );
+		return false;
+	} // if
+	count$ -= 1;										// acquire
+	unlock( lock$ );
+	return true;
+}
+
+bool V( semaphore & sem ) with( sem ) {
 	thread$ * thrd = 0p;
-	lock( lock __cfaabi_dbg_ctx2 );
-	count += 1;
-	if ( count <= 0 ) {
-		// remove task at head of waiting list
-		thrd = pop_head( waiting );
-	}
-
-	unlock( lock );
-
-	// make new owner
-	if ( doUnpark ) unpark( thrd );
-
-	return thrd;
-}
-
-bool V(semaphore & this) with( this ) {
-	thread$ * thrd = V(this, true);
+	lock( lock$ __cfaabi_dbg_ctx2 );
+	count$ += 1;
+	if ( count$ <= 0 ) {
+		thrd = pop_head( waiting$ );					// remove task at head of waiting list
+	}
+	unlock( lock$ );
+	if ( true ) unpark( thrd );						// make new owner
+//	thread$ * thrd = V(sem, true);
 	return thrd != 0p;
 }
 
-bool V(semaphore & this, unsigned diff) with( this ) {
+bool V( semaphore & sem, size_t diff ) with( sem ) {
 	thread$ * thrd = 0p;
-	lock( lock __cfaabi_dbg_ctx2 );
-	int release = max(-count, (int)diff);
-	count += diff;
-	for(release) {
-		unpark( pop_head( waiting ) );
-	}
-
-	unlock( lock );
-
+	lock( lock$ __cfaabi_dbg_ctx2 );
+	size_t release = max( (size_t)-count$, diff );
+	count$ += diff;
+	for ( release ) unpark( pop_head( waiting$ ) );
+	unlock( lock$ );
 	return thrd != 0p;
 }
-
Index: libcfa/src/concurrency/locks.hfa
===================================================================
--- libcfa/src/concurrency/locks.hfa	(revision 14a51d3104b5022bc886f4bc8e602e7c8f568565)
+++ libcfa/src/concurrency/locks.hfa	(revision 95330c335047f8584d6529b923b2a80afc78698f)
@@ -11,6 +11,6 @@
 // Created On       : Thu Jan 21 19:46:50 2021
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Aug 21 22:36:44 2025
-// Update Count     : 23
+// Last Modified On : Fri Oct 31 09:20:22 2025
+// Update Count     : 59
 //
 
@@ -81,19 +81,41 @@
 
 
-
-//-----------------------------------------------------------------------------
-// Semaphore
+//-----------------------------------------------------------------------------
+// Semaphore, counting
+
 struct semaphore {
-	__spinlock_t lock;
-	int count;
-	__queue_t( thread$) waiting;
-};
-
-void ?{}( semaphore & this, int count = 1 );
-void ^?{}( semaphore & this );
-bool P( semaphore & this );
-bool V( semaphore & this );
-bool V( semaphore & this, unsigned count );
-thread$ * V( semaphore & this, bool );
+	// PRIVATE
+	ssize_t count$;										 // - => # waiting threads, 0 => block, + => acquire
+	__spinlock_t lock$;									 // protect blocking-lock critical sections
+	__queue_t( thread$ ) waiting$;						 // waiting threads
+};
+
+// PRIVATE
+//thread$ * V( semaphore & sem, bool );
+
+// PUBLIC
+void ?{}( semaphore & sem, ssize_t count = 1 );
+// Return values are used for composition. Calling threads know if a thread is unblocked, which can be useful for
+// debugging, performance instrumentation and other metadata tracking purposes.
+bool P( semaphore & sem );
+static inline bool P( semaphore & sem, uintptr_t shadow ) { active_thread()->shadow$ = shadow; return P( sem ); }
+bool P( semaphore & sem, semaphore & lock );			// atomic block and release
+bool try_P( semaphore & sem );
+static inline bool P( semaphore & sem, semaphore & lock, uintptr_t shadow ) { active_thread()->shadow$ = shadow; return P( sem, lock ); }
+bool V( semaphore & sem );
+bool V( semaphore & sem, size_t count );
+static inline uintptr_t front( semaphore & sem ) with( sem ) { // return shadow information for first waiting thread
+	#ifdef __CFA_DEBUG__
+	if ( waiting$ ) {									// condition queue must not be empty
+		abort( "Attempt to access user data on an empty semaphore lock.\n"
+			   "Possible cause is not checking if the condition lock is empty before reading stored data." );
+	} // if
+	#endif // __CFA_DEBUG__
+	return waiting$.head->shadow$;						// return condition information stored with blocked task
+}
+static inline ssize_t counter( semaphore & sem ) with( sem ) { return count$; } // - => # waiting threads, 0 => block, + => acquire
+//static inline int ?!=?( semaphore & sem, zero_t ) with( sem ) { return count$ != 0; } // empty waiting queue
+static inline bool empty( semaphore & sem ) with( sem ) { return count$ == 0; } // empty waiting queue
+
 
 //----------
