Index: trunk/kernel/kern/process.c
===================================================================
--- trunk/kernel/kern/process.c	(revision 435)
+++ trunk/kernel/kern/process.c	(revision 436)
@@ -365,5 +365,4 @@
     cxy_t       parent_cxy;
     xptr_t      children_lock_xp;
-    xptr_t      copies_lock_xp;
 
 	assert( (process->th_nr == 0) , __FUNCTION__ ,
@@ -377,28 +376,13 @@
 #endif
 
-    // get local process manager pointer
-    pmgr_t * pmgr = &LOCAL_CLUSTER->pmgr;
-
-    // remove process from local_list in cluster manager
-    remote_spinlock_lock( XPTR( local_cxy , &pmgr->local_lock ) );
-    xlist_unlink( XPTR( local_cxy , &process->local_list ) );
-    remote_spinlock_unlock( XPTR( local_cxy , &pmgr->local_lock ) );
-
-    // get extended pointer on copies_lock in owner cluster manager
-    cxy_t  owner_cxy = CXY_FROM_PID( process->pid );
-	lpid_t lpid      = LPID_FROM_PID( process->pid );
-    copies_lock_xp   = XPTR( owner_cxy , &pmgr->copies_lock[lpid] );
-
-    // remove local process from copies_list
-    remote_spinlock_lock( copies_lock_xp );
-    xlist_unlink( XPTR( local_cxy , &process->copies_list ) );
-    remote_spinlock_unlock( copies_lock_xp );
-
-    // for reference process only
-    if( XPTR( local_cxy , process ) == process->ref_xp )
-    {
-        // remove reference process from txt_list
-        process_txt_detach( process );
-
+    // remove process from local_list in local cluster manager
+    cluster_process_local_unlink( process );
+
+    // remove process from copies_list in owner cluster manager
+    cluster_process_copies_unlink( process );
+
+    // remove process from children_list if process is in owner cluster
+    if( CXY_FROM_PID( process->pid ) == local_cxy )
+    {
         // get pointers on parent process
         parent_xp  = process->parent_xp;
@@ -461,8 +445,6 @@
     xptr_t             process_xp;        // extended pointer on process copy
     cxy_t              process_cxy;       // process copy cluster identifier
-    process_t        * process_ptr;       // local pointer on process copy
-    uint32_t           responses;         // number of remote process copies
-    uint32_t           rsp_count;         // used to assert number of copies
-    rpc_desc_t         rpc;               // rpc descriptor allocated in stack
+    reg_t              save_sr;           // for critical section
+    rpc_desc_t         rpc;               // shared RPC descriptor
 
     thread_t * client = CURRENT_THREAD;
@@ -475,5 +457,5 @@
 #endif
 
-    // get local pointer on local cluster manager
+    // get pointer on local cluster manager
     cluster = LOCAL_CLUSTER;
 
@@ -483,9 +465,6 @@
 
     // get root of list of copies, lock, and number of copies from owner cluster
-    responses = hal_remote_lw ( XPTR( owner_cxy , &cluster->pmgr.copies_nr[lpid] ) );
-    root_xp   = hal_remote_lwd( XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] ) );
-    lock_xp   = hal_remote_lwd( XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] ) );
-
-    rsp_count = 0;
+    root_xp   = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] );
+    lock_xp   = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] );
 
     // check action type
@@ -494,33 +473,51 @@
              (action_type == UNBLOCK_ALL_THREADS )), __FUNCTION__ , "illegal action type" );
              
-    // initialise rpc descriptor
-    rpc.index    = RPC_PROCESS_SIGACTION;
-    rpc.response = responses;
-    rpc.blocking = false;
-    rpc.thread   = client;
+    // allocate a - shared - RPC descriptor in client thread stack
+    // it can be shared because all parallel, non-blocking, server threads
+    // use the same input arguments, and use the shared RPC response field
+    // but use 
+
+    // the client thread makes the following sequence:
+    // 1. mask interrupts
+    // 2. block itself
+    // 3. send RPC requests to all copies
+    // 4. unmask interrupts
+    // 5. deschedule
+
+    // mask IRQs
+    hal_disable_irq( &save_sr);
+
+    // client register blocking condition for itself
+    thread_block( XPTR( local_cxy , client ) , THREAD_BLOCKED_RPC );
 
     // take the lock protecting the copies
     remote_spinlock_lock( lock_xp );
 
-    // send RPCs to remote clusters
+    // initialize shared RPC descriptor
+    rpc.response = 0;
+    rpc.blocking = false;
+    rpc.index    = RPC_PROCESS_SIGACTION;
+    rpc.thread   = client;
+    rpc.lid      = client->core->lid;
+    rpc.args[0]  = action_type;
+    rpc.args[1]  = pid;
+
+    // send RPCs to all clusters containing process copiess
     XLIST_FOREACH( root_xp , iter_xp )
     {
+
+#if CONFIG_DEBUG_PROCESS_SIGACTION 
+if( CONFIG_DEBUG_PROCESS_SIGACTION < cycle )
+printk("\n[DBG] %s : send RPC to %s process %x in cluster %x\n",
+__FUNCTION__ , process_action_str( action_type ) , pid , process_cxy );
+#endif
+        // atomically increment responses counter
+        hal_atomic_add( (void *)&rpc.response , 1 );
+
         process_xp  = XLIST_ELEMENT( iter_xp , process_t , copies_list );
         process_cxy = GET_CXY( process_xp );
-        process_ptr = GET_PTR( process_xp );
-
-#if CONFIG_DEBUG_PROCESS_SIGACTION 
-if( CONFIG_DEBUG_PROCESS_SIGACTION < cycle )
-printk("\n[DBG] %s : send RPC to cluster %x\n", __FUNCTION__ , process_cxy );
-#endif
-
-        // check PID
-        assert( (hal_remote_lw( XPTR( process_cxy , &process_ptr->pid) ) == pid),
-        __FUNCTION__ , "unconsistent PID value\n" );
-
-        rpc.args[0] = (uint64_t)action_type;
-        rpc.args[1] = (uint64_t)pid;
+
+        // call RPC in target cluster
         rpc_process_sigaction_client( process_cxy , &rpc );
-        rsp_count++;
     }
    
@@ -528,12 +525,9 @@
     remote_spinlock_unlock( lock_xp );
 
-    // check number of copies...
-    assert( (rsp_count == responses) , __FUNCTION__ ,
-    "unconsistent number of process copies : rsp_count = %d / responses = %d",
-    rsp_count , responses );
-
-    // block and deschedule to wait RPC responses 
-    thread_block( CURRENT_THREAD , THREAD_BLOCKED_RPC );
-    sched_yield("BLOCKED on RPC_PROCESS_SIGACTION");
+    // restore IRQs
+    hal_restore_irq( save_sr);
+
+    // client deschedule : will be unblocked by the last RPC server thread 
+    sched_yield("blocked on rpc_process_sigaction");
 
 #if CONFIG_DEBUG_PROCESS_SIGACTION
@@ -541,6 +535,5 @@
 if( CONFIG_DEBUG_PROCESS_SIGACTION < cycle )
 printk("\n[DBG] %s : thread %x exit after %s process %x in cluster %x / cycle %d\n",
-__FUNCTION__ , client, process_action_str( action_type ) ,
-process->pid , local_cxy , cycle );
+__FUNCTION__ , client, process_action_str( action_type ) , pid , local_cxy , cycle );
 #endif
 
@@ -553,9 +546,13 @@
     thread_t          * this;           // pointer on calling thread 
     uint32_t            ltid;           // index in process th_tbl
+    cxy_t               owner_cxy;      // target process owner cluster
     uint32_t            count;          // requests counter
-    volatile uint32_t   rsp_count;      // responses counter
+    volatile uint32_t   ack_count;      // scheduler acknowledge counter
 
     // get calling thread pointer
     this = CURRENT_THREAD;
+
+    // get target process owner cluster
+    owner_cxy = CXY_FROM_PID( process->pid );
 
 #if CONFIG_DEBUG_PROCESS_SIGACTION
@@ -569,44 +566,37 @@
     spinlock_lock( &process->th_lock );
 
-    // initialize local responses counter 
-    rsp_count = process->th_nr;
-
-    // loop on process threads to block and deschedule all threads in cluster
+    // loop to block all threads but the main thread
     // we use both "ltid" and "count" because it can exist "holes" in th_tbl
-    for( ltid = 0 , count = 0 ; count < process->th_nr ; ltid++ )
+    for( ltid = 0 , count = 0 , ack_count = 0 ; count < process->th_nr ; ltid++ )
     {
         target = process->th_tbl[ltid];
 
-        assert( (target != this) , __FUNCTION__ , "calling thread cannot be a target\n" );
-
-        if( target != NULL )             // thread found
+        if( target != NULL )                                 // thread exist
         {
             count++;
 
-            // - if the calling thread and the target thread are on the same core,
-            //   we block the target thread, we don't need confirmation from scheduler,
-            //   and we simply decrement the responses counter.
-            // - if the calling thread and the target thread are not running on the same
-            //   core, we ask the target scheduler to acknowlege the blocking
-            //   to be sure that the target thread is not running.
-            
-            if( this->core->lid == target->core->lid )
+            // main thread should not be deleted
+            if( (ltid != 0) || (owner_cxy != local_cxy) )
             {
                 // set the global blocked bit in target thread descriptor.
-                thread_block( target , THREAD_BLOCKED_GLOBAL );
-
-                // decrement responses counter
-                hal_atomic_add( (void *)&rsp_count , -1 );
-            }
-            else
-            {
-                // set the global blocked bit in target thread descriptor.
-                thread_block( target , THREAD_BLOCKED_GLOBAL );
-
-                // set FLAG_REQ_ACK and &ack_rsp_count in target descriptor
-                thread_set_req_ack( target , (void *)&rsp_count );
-
-                // force scheduling on target thread
-                dev_pic_send_ipi( local_cxy , target->core->lid );
+                thread_block( XPTR( local_cxy , target ) , THREAD_BLOCKED_GLOBAL );
+  
+                // - if the calling thread and the target thread are on the same core,
+                //   we don't need confirmation from scheduler,
+                // - if the calling thread and the target thread are not running on the same
+                //   core, we ask the target scheduler to acknowlege the blocking
+                //   to be sure that the target thread is not running.
+            
+                if( this->core->lid != target->core->lid )
+                {
+                    // increment responses counter 
+                    hal_atomic_add( (void*)&ack_count , 1 );
+
+                    // set FLAG_REQ_ACK and &ack_rsp_count in target descriptor
+                    thread_set_req_ack( target , (uint32_t *)&ack_count );
+
+                    // force scheduling on target thread
+                    dev_pic_send_ipi( local_cxy , target->core->lid );
+                }
             }
         }
@@ -616,9 +606,9 @@
     spinlock_unlock( &process->th_lock );
 
-    // wait all responses from schedulers 
+    // wait acknowledges 
     while( 1 )
     {
-        // exit loop when all local responses received
-        if ( rsp_count == 0 ) break;
+        // exit when all scheduler acknoledges received
+        if ( ack_count == 0 ) break;
     
         // wait 1000 cycles before retry
@@ -656,11 +646,9 @@
     spinlock_lock( &process->th_lock );
 
-    // loop on process threads to unblock all threads in cluster
+    // loop on process threads to unblock all threads
     // we use both "ltid" and "count" because it can exist "holes" in th_tbl
     for( ltid = 0 , count = 0 ; count < process->th_nr ; ltid++ )
     {
         target = process->th_tbl[ltid];
-
-        assert( (target != this) , __FUNCTION__ , "calling thread cannot be a target\n" );
 
         if( target != NULL )             // thread found
@@ -689,12 +677,6 @@
 {
     thread_t          * target;        // pointer on target thread 
-    thread_t          * this;          // pointer on calling thread 
     uint32_t            ltid;          // index in process th_tbl
-    uint32_t            count;         // request counter
-    cxy_t               owner_cxy;     // owner cluster identifier
-
-    // get calling thread pointer
-    this = CURRENT_THREAD;
-    owner_cxy = CXY_FROM_PID( process->pid );
+    uint32_t            count;         // threads counter
 
 #if CONFIG_DEBUG_PROCESS_SIGACTION
@@ -702,5 +684,5 @@
 if( CONFIG_DEBUG_PROCESS_SIGACTION < cycle )
 printk("\n[DBG] %s : thread %x enter for process %x in cluster %x / cycle %d\n",
-__FUNCTION__ , this , process->pid , local_cxy , cycle );
+__FUNCTION__ , CURRENT_THREAD , process->pid , local_cxy , cycle );
 #endif
 
@@ -708,5 +690,5 @@
     spinlock_lock( &process->th_lock );
 
-    // loop on threads to set the REQ_DELETE flag
+    // loop to set the REQ_DELETE flag on all threads but the main
     // we use both "ltid" and "count" because it can exist "holes" in th_tbl
     for( ltid = 0 , count = 0  ; count < process->th_nr ; ltid++ )
@@ -714,15 +696,11 @@
         target = process->th_tbl[ltid];
 
-        assert( (target != this) , __FUNCTION__ , "calling thread cannot be a target\n" );
-
-        if( target != NULL )            // thread found
+        if( target != NULL ) 
         {
             count++;
             
-            // the main thread should not be deleted
-            if( (owner_cxy != local_cxy) || (ltid != 0) )   
-            {
-                hal_atomic_or( &target->flags , THREAD_FLAG_REQ_DELETE );
-            }
+            thread_kill( XPTR( local_cxy , target ), 
+                         false,                       // is_exit
+                         true );                      // is_forced
         }
     }
@@ -735,5 +713,5 @@
 if( CONFIG_DEBUG_PROCESS_SIGACTION < cycle )
 printk("\n[DBG] %s : thread %x exit for process %x in cluster %x / cycle %d\n",
-__FUNCTION__ , this , process->pid , local_cxy , cycle );
+__FUNCTION__ , CURRENT_THREAD , process->pid , local_cxy , cycle );
 #endif
 
@@ -790,4 +768,25 @@
 
 }  // end process_get_local_copy()
+
+////////////////////////////////////////////
+pid_t process_get_ppid( xptr_t  process_xp )
+{
+    cxy_t       process_cxy;
+    process_t * process_ptr;
+    xptr_t      parent_xp;
+    cxy_t       parent_cxy;
+    process_t * parent_ptr;
+
+    // get process cluster and local pointer
+    process_cxy = GET_CXY( process_xp );
+    process_ptr = GET_PTR( process_xp );
+
+    // get pointers on parent process
+    parent_xp  = (xptr_t)hal_remote_lwd( XPTR( process_cxy , &process_ptr->parent_xp ) );
+    parent_cxy = GET_CXY( parent_xp );
+    parent_ptr = GET_PTR( parent_xp );
+
+    return hal_remote_lw( XPTR( parent_cxy , &parent_ptr->pid ) );
+}
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -1067,5 +1066,5 @@
                             parent_process_xp );
 
-#if CONFIG_DEBUG_PROCESS_MAKE_FORK
+#if( CONFIG_DEBUG_PROCESS_MAKE_FORK & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_FORK < cycle )
@@ -1086,5 +1085,5 @@
     }
 
-#if CONFIG_DEBUG_PROCESS_MAKE_FORK
+#if( CONFIG_DEBUG_PROCESS_MAKE_FORK & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_FORK < cycle )
@@ -1112,5 +1111,5 @@
     assert( (thread->trdid == 0) , __FUNCTION__ , "main thread must have index 0\n" );
 
-#if CONFIG_DEBUG_PROCESS_MAKE_FORK
+#if( CONFIG_DEBUG_PROCESS_MAKE_FORK & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_FORK < cycle )
@@ -1134,5 +1133,5 @@
     vmm_set_cow( process );
  
-#if CONFIG_DEBUG_PROCESS_MAKE_FORK
+#if( CONFIG_DEBUG_PROCESS_MAKE_FORK & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_FORK < cycle )
@@ -1236,7 +1235,7 @@
 
     // give TXT ownership to new_process
-    process_txt_set_ownership( XPTR( local_cxy , new_process ) );
-
-#if CONFIG_DEBUG_PROCESS_MAKE_EXEC
+    process_txt_set_ownership( XPTR( local_cxy , new_process) );
+
+#if( CONFIG_DEBUG_PROCESS_MAKE_EXEC & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_EXEC < cycle )
@@ -1255,5 +1254,5 @@
 	}
 
-#if CONFIG_DEBUG_PROCESS_MAKE_EXEC
+#if( CONFIG_DEBUG_PROCESS_MAKE_EXEC & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_EXEC < cycle )
@@ -1287,5 +1286,5 @@
     assert( (new_thread->trdid == 0) , __FUNCTION__ , "main thread must have index 0\n" );
 
-#if CONFIG_DEBUG_PROCESS_MAKE_EXEC
+#if( CONFIG_DEBUG_PROCESS_MAKE_EXEC & 1 )
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_MAKE_EXEC < cycle )
@@ -1312,6 +1311,9 @@
 	thread_unblock( XPTR( local_cxy , new_thread ) , THREAD_BLOCKED_GLOBAL );
 
+    // detach old_process from TXT
+    process_txt_detach( XPTR( local_cxy , old_process ) );
+
     // request old_thread destruction => old_process destruction
-    thread_block( old_thread , THREAD_BLOCKED_GLOBAL );
+    thread_block( XPTR( local_cxy , old_thread ) , THREAD_BLOCKED_GLOBAL );
     hal_atomic_or( &old_thread->flags , THREAD_FLAG_REQ_DELETE );
 
@@ -1600,10 +1602,10 @@
 if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
 printk("\n[DBG] %s : thread %x enter for process %x / txt_id = %d  / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, process, txt_id, cycle );
-#endif
-
-    // check process is reference
-    assert( (process->ref_xp == XPTR( local_cxy , process )) , __FUNCTION__ ,
-    "process is not the reference descriptor" );
+__FUNCTION__, CURRENT_THREAD, process->pid, txt_id, cycle );
+#endif
+
+    // check process is in owner cluster
+    assert( (CXY_FROM_PID( process->pid ) == local_cxy) , __FUNCTION__ ,
+    "process descriptor not in owner cluster" );
 
     // check terminal index
@@ -1629,12 +1631,16 @@
 if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
 printk("\n[DBG] %s : thread %x exit for process %x / txt_id = %d / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, process, txt_id , cycle );
+__FUNCTION__, CURRENT_THREAD, process->pid, txt_id , cycle );
 #endif
 
 } // end process_txt_attach()
 
-//////////////////////////////////////////////
-void process_txt_detach( process_t * process )
-{
+/////////////////////////////////////////////
+void process_txt_detach( xptr_t  process_xp )
+{
+    process_t * process_ptr;  // local pointer on process in owner cluster
+    cxy_t       process_cxy;  // process owner cluster
+    pid_t       process_pid;  // process identifier
+    xptr_t      file_xp;      // extended pointer on stdin file
     xptr_t      chdev_xp;     // extended pointer on TXT_RX chdev
     cxy_t       chdev_cxy;    // TXT_RX chdev cluster
@@ -1642,33 +1648,60 @@
     xptr_t      lock_xp;      // extended pointer on list lock in chdev
 
+    // get process cluster, local pointer, and PID
+    process_cxy = GET_CXY( process_xp );
+    process_ptr = GET_PTR( process_xp );
+    process_pid = hal_remote_lw( XPTR( process_cxy , &process_ptr->pid ) );
+
+    // check process descriptor in owner cluster
+    assert( (CXY_FROM_PID( process_pid ) == process_cxy ) , __FUNCTION__ ,
+    "process descriptor not in owner cluster" );
+
 #if CONFIG_DEBUG_PROCESS_TXT_ATTACH
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
 printk("\n[DBG] %s : thread %x enter for process %x / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, process, cycle );
-#endif
-
-    // check process is reference
-    assert( (process->ref_xp == XPTR( local_cxy , process )) , __FUNCTION__ ,
-    "process is not the reference descriptor" );
-
-    // get extended pointer on TXT_RX chdev
-    chdev_xp  = chdev_from_file( process->fd_array.array[0] );
+__FUNCTION__, CURRENT_THREAD, process_pid, cycle );
+#endif
+
+    // release TXT ownership (does nothing if not TXT owner)
+    process_txt_transfer_ownership( process_xp );
+
+    // get extended pointer on process stdin file
+    file_xp = (xptr_t)hal_remote_lwd( XPTR( process_cxy , &process_ptr->fd_array.array[0] ) );
+
+    // get pointers on TXT_RX chdev
+    chdev_xp  = chdev_from_file( file_xp );
     chdev_cxy = GET_CXY( chdev_xp );
     chdev_ptr = (chdev_t *)GET_PTR( chdev_xp );
 
-    // get extended pointer on lock of attached process list
+    // get extended pointer on lock protecting attached process list
     lock_xp = XPTR( chdev_cxy , &chdev_ptr->ext.txt.lock );
 
     // unlink process from attached process list
     remote_spinlock_lock( lock_xp );
-    xlist_unlink( XPTR( local_cxy , &process->txt_list ) );
+    xlist_unlink( XPTR( process_cxy , &process_ptr->txt_list ) );
     remote_spinlock_unlock( lock_xp );
-   
+
+#if( CONFIG_DEBUG_PROCESS_TXT_ATTACH & 1 )
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+{
+    xptr_t root_xp = XPTR( chdev_cxy , &chdev_ptr->ext.txt.root );
+    xptr_t iter_xp;
+    XLIST_FOREACH( root_xp , iter_xp )
+    {
+        xptr_t      current_xp  = XLIST_ELEMENT( iter_xp , process_t , txt_list );
+        process_t * current_ptr = GET_PTR( current_xp );
+
+        printk("\n[DBG] %s : attached_process %x (pid = %x)\n",
+        __FUNCTION__, current_ptr, current_ptr->pid );
+    }
+}
+#endif
+
 #if CONFIG_DEBUG_PROCESS_TXT_ATTACH
 cycle = (uint32_t)hal_get_cycles();
 if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
-printk("\n[DBG] %s : thread %x exit for process %x / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, process, cycle );
+printk("\n[DBG] %s : thread %x exit / process %x detached from TXT / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process->pid, cycle );
 #endif
 
@@ -1680,4 +1713,5 @@
     process_t * process_ptr;
     cxy_t       process_cxy;
+    pid_t       process_pid;
     xptr_t      file_xp;
     xptr_t      txt_xp;     
@@ -1685,7 +1719,21 @@
     cxy_t       txt_cxy;
 
-    // get cluster and local pointer on process
+    // get pointers on process in owner cluster
     process_cxy = GET_CXY( process_xp );
     process_ptr = GET_PTR( process_xp );
+
+    // get process PID
+    process_pid = hal_remote_lw( XPTR( process_cxy , &process_ptr->pid ) );
+
+    // check owner cluster
+    assert( (process_cxy == CXY_FROM_PID( process_pid )) , __FUNCTION__,
+    "process descriptor not in owner cluster\n" );
+
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x enter for process %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_pid, cycle );
+#endif
 
     // get extended pointer on stdin pseudo file
@@ -1700,14 +1748,19 @@
     hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , process_xp );
 
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x exit for process %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_pid, cycle );
+#endif
+
 }  // end process_txt_set ownership()
 
-/////////////////////////////////////////////////////
-void process_txt_reset_ownership( xptr_t process_xp )
-{
-    process_t * process_ptr;
-    cxy_t       process_cxy;
-    xptr_t      parent_xp;       // extended pointer on parent process
-    process_t * parent_ptr;
-    cxy_t       parent_cxy;
+////////////////////////////////////////////////////////
+void process_txt_transfer_ownership( xptr_t process_xp )
+{
+    process_t * process_ptr;     // local pointer on process releasing ownership
+    cxy_t       process_cxy;     // process cluster
+    pid_t       process_pid;     // process identifier
     xptr_t      file_xp;         // extended pointer on TXT_RX pseudo file
     xptr_t      txt_xp;          // extended pointer on TXT_RX chdev
@@ -1717,13 +1770,27 @@
     xptr_t      owner_xp;        // extended pointer on current TXT_RX owner
     xptr_t      root_xp;         // extended pointer on root of attached process list
+    xptr_t      lock_xp;         // extended pointer on lock protecting attached process list
     xptr_t      iter_xp;         // iterator for xlist
     xptr_t      current_xp;      // extended pointer on current process
     process_t * current_ptr;     // local pointer on current process
     cxy_t       current_cxy;     // cluster for current process
-    pid_t       ppid;            // parent process identifier for current process
-
-    // get cluster and local pointer on process
+
+    // get pointers on process in owner cluster
     process_cxy = GET_CXY( process_xp );
     process_ptr = GET_PTR( process_xp );
+
+    // get process PID
+    process_pid = hal_remote_lw( XPTR( process_cxy , &process_ptr->pid ) );
+
+    // check owner cluster
+    assert( (process_cxy == CXY_FROM_PID( process_pid )) , __FUNCTION__,
+    "process descriptor not in owner cluster\n" );
+
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x enter / process %x / pid %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_ptr, process_pid, cycle );
+#endif
 
     // get extended pointer on stdin pseudo file
@@ -1739,38 +1806,126 @@
     txt_id   = hal_remote_lw ( XPTR( txt_cxy , &txt_ptr->channel ) );
 
-    // transfer ownership to KSH if required
-    if( (owner_xp == process_xp) && (txt_id > 0) )   
-    {
-        // get extended pointer on root of list of attached processes
-        root_xp = hal_remote_lwd( XPTR( txt_cxy , &txt_ptr->ext.txt.root ) );
-
-        // scan attached process list to find KSH process
-        XLIST_FOREACH( root_xp , iter_xp )
+#if( CONFIG_DEBUG_PROCESS_TXT_ATTACH & 1 )
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : file_ptr %x / txt_ptr %x / txt_id %d / owner_ptr = %x\n",
+__FUNCTION__, GET_PTR(file_xp), txt_ptr, txt_id, GET_PTR(owner_xp) );
+#endif
+
+    // transfer ownership only if process is the TXT owner
+    if( (owner_xp == process_xp) && (txt_id > 0) )  
+    {
+        // get extended pointers on root and lock of attached processes list
+        root_xp = XPTR( txt_cxy , &txt_ptr->ext.txt.root );
+        lock_xp = XPTR( txt_cxy , &txt_ptr->ext.txt.lock );
+
+        // get lock
+        remote_spinlock_lock( lock_xp );
+
+        if( process_get_ppid( process_xp ) != 1 )           // process is not KSH
         {
-            current_xp  = XLIST_ELEMENT( iter_xp , process_t , txt_list );
-            current_cxy = GET_CXY( current_xp );
-            current_ptr = GET_PTR( current_xp );
-            parent_xp   = hal_remote_lwd( XPTR( current_cxy , &current_ptr->parent_xp ) );
-            parent_cxy  = GET_CXY( parent_xp );
-            parent_ptr  = GET_PTR( parent_xp );
-            ppid        = hal_remote_lw( XPTR( parent_cxy , &parent_ptr->pid ) );
-
-printk("\n@@@ %s : pid = %x / process = %x\n", __FUNCTION__ , current_ptr->pid, current_ptr );
-
-            if( ppid == 1 )  // current is KSH
+
+#if( CONFIG_DEBUG_PROCESS_TXT_ATTACH & 1 )
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : process is not the KSH process => search the KSH\n", __FUNCTION__ );
+#endif
+            // scan attached process list to find KSH process
+            XLIST_FOREACH( root_xp , iter_xp )
             {
-                // set owner field in TXT chdev 
-                hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , current_xp );
-                return;
+                current_xp  = XLIST_ELEMENT( iter_xp , process_t , txt_list );
+                current_cxy = GET_CXY( current_xp );
+                current_ptr = GET_PTR( current_xp );
+
+                if( process_get_ppid( current_xp ) == 1 )  // current is KSH 
+                {
+                    // release lock
+                    remote_spinlock_unlock( lock_xp );
+
+                    // set owner field in TXT chdev 
+                    hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , current_xp );
+
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x exit / process %x to KSH process %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_pid, 
+hal_remote_lw( XPTR( current_cxy , &current_ptr->pid ) ), cycle );
+#endif
+                     return;
+                }
             }
+ 
+            // release lock
+            remote_spinlock_unlock( lock_xp );
+
+            // PANIC if KSH not found
+            assert( false , __FUNCTION__ , "KSH process not found for TXT %d" ); 
+
+            return;
         }
-
-        assert( false , __FUNCTION__ , "KSH process not found" ); 
-    }
-}  // end process_txt_reset_ownership()
-
-
-//////////////////////////////////////////////////////     
-inline pid_t process_get_txt_owner( uint32_t channel )
+        else                                               // process is KSH
+        {
+
+#if( CONFIG_DEBUG_PROCESS_TXT_ATTACH & 1 )
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : process is the KSH process => search another\n", __FUNCTION__ );
+#endif
+
+            // scan attached process list to find another process 
+            XLIST_FOREACH( root_xp , iter_xp )
+            {
+                current_xp  = XLIST_ELEMENT( iter_xp , process_t , txt_list );
+                current_cxy = GET_CXY( current_xp );
+                current_ptr = GET_PTR( current_xp );
+
+                if( current_xp != process_xp )            // current is not KSH 
+                {
+                    // release lock
+                    remote_spinlock_unlock( lock_xp );
+
+                    // set owner field in TXT chdev 
+                    hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , current_xp );
+
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x exit / KSH process %x to process %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_pid,
+hal_remote_lw( XPTR( current_cxy , &current_ptr->pid ) ), cycle );
+#endif
+                     return;
+                }
+            }
+
+            // release lock
+            remote_spinlock_unlock( lock_xp );
+
+            // no more owner for TXT if no other process found
+            hal_remote_swd( XPTR( txt_cxy , &txt_ptr->ext.txt.owner_xp ) , XPTR_NULL );
+
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x exit / KSH process %x to nobody / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_pid, cycle );
+#endif
+            return;
+        }
+    }
+    else
+    {
+
+#if CONFIG_DEBUG_PROCESS_TXT_ATTACH
+cycle = (uint32_t)hal_get_cycles();
+if( CONFIG_DEBUG_PROCESS_TXT_ATTACH < cycle )
+printk("\n[DBG] %s : thread %x exit / process %x is not TXT owner / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD, process_pid, cycle );
+#endif
+
+    }
+}  // end process_txt_transfer_ownership()
+
+
+////////////////////////////////////////////////     
+xptr_t process_txt_get_owner( uint32_t channel )
 {
     xptr_t      txt_rx_xp  = chdev_dir.txt_rx[channel];
@@ -1778,11 +1933,5 @@
     chdev_t *   txt_rx_ptr = GET_PTR( txt_rx_xp );
 
-    xptr_t process_xp = (xptr_t)hal_remote_lwd( XPTR( txt_rx_cxy, 
-                                                &txt_rx_ptr->ext.txt.owner_xp ) );
-
-    cxy_t       process_cxy = GET_CXY( process_xp );
-    process_t * process_ptr = GET_PTR( process_xp );
-
-    return (pid_t)hal_remote_lw( XPTR( process_cxy , &process_ptr->pid ) );
+    return (xptr_t)hal_remote_lwd( XPTR( txt_rx_cxy , &txt_rx_ptr->ext.txt.owner_xp ) );
 }
 
@@ -1817,5 +1966,5 @@
     remote_spinlock_lock( lock_xp );
 
-    // scan attached process list to find KSH process
+    // scan attached process list 
     XLIST_FOREACH( root_xp , iter_xp )
     {
