2 Parallel Programming - 2026/2027
4 Implementation of a termination detection algorithm (Dijkstra-Scholten)
17 // Tag for control messages. Distinct from basic algorithm's tags.
18 #define CONTROL_SIGNAL 99
20 // Type for control messages (empty payload, just a signal)
25 // Node operational states
31 // --- SHARED LOCAL STATE ---
32 // Must be protected by mutex because basic_thread and control_thread access it concurrently
33 static pthread_mutex_t state_mutex
= PTHREAD_MUTEX_INITIALIZER
;
34 static node_state_t state
= PASSIVE_STATE
;
35 static int parent
= -1; // -1 indicates null/no parent
36 static int deficit
= 0; // C_i counter (unacknowledged messages)
37 static bool initiator
= false;
39 // --- HELPER FUNCTION ---
40 // MUST BE DECLARED ABOVE THE OTHER FUNCTIONS!
41 // Evaluates if the node can detach from the tree. Caller must hold state_mutex.
42 static void try_resolve_tree(int my_id
)
44 if (state
== PASSIVE_STATE
&& deficit
== 0)
47 // Root is passive and deficit is 0 -> Global Termination!
53 control_message_t sig
= {0};
54 // Send acknowledgment signal up the tree to our parent
55 MPI_Send(&sig
, sizeof(control_message_t
), MPI_BYTE
, parent
,
56 CONTROL_SIGNAL
, MPI_COMM_WORLD
);
58 trace("%d: [CONTROL] Sent tree-signal to parent %d\n", my_id
, parent
);
59 parent
= -1; // Detach from tree
65 Main loop of the termination detection algorithm.
67 void *detect_termination(void *_args
)
69 thread_args_t
*args
= _args
;
71 initiator
= args
->initiator
;
73 // (Removed unused 'processes' variable to fix compiler warning)
77 pthread_mutex_lock(&state_mutex
);
79 // CHECK GLOBAL TERMINATION CONDITION
80 if (initiator
&& state
== PASSIVE_STATE
&& deficit
== 0)
82 trace("%d: [CONTROL] GLOBAL TERMINATION DETECTED!\n", id
);
83 pthread_mutex_unlock(&state_mutex
);
84 break; // Breaks loop, tells main() to shut down
87 pthread_mutex_unlock(&state_mutex
);
89 // Check network for incoming child signals (non-blocking)
92 MPI_Iprobe(MPI_ANY_SOURCE
, CONTROL_SIGNAL
, MPI_COMM_WORLD
, &flag
, &status
);
96 control_message_t sig
;
97 MPI_Recv(&sig
, sizeof(control_message_t
), MPI_BYTE
, status
.MPI_SOURCE
,
98 CONTROL_SIGNAL
, MPI_COMM_WORLD
, MPI_STATUS_IGNORE
);
100 pthread_mutex_lock(&state_mutex
);
101 deficit
--; // Rule D: A child finished its work and reported back
102 trace("%d: [CONTROL] Got signal from %d (New Deficit: %d)\n",
103 id
, status
.MPI_SOURCE
, deficit
);
105 try_resolve_tree(id
);
106 pthread_mutex_unlock(&state_mutex
);
110 // Sleep briefly to prevent pinning the CPU core at 100% usage
119 Called at startup of the basic algorithm in process ID.
121 void control_basic_start_hook(int id
)
123 pthread_mutex_lock(&state_mutex
);
124 state
= initiator
? ACTIVE_STATE
: PASSIVE_STATE
;
127 pthread_mutex_unlock(&state_mutex
);
131 Called when the basic algorithm process ID becomes passive.
133 void control_become_passive_hook(int id
)
135 pthread_mutex_lock(&state_mutex
);
136 state
= PASSIVE_STATE
;
137 try_resolve_tree(id
); // Check if we can leave the tree right now
138 pthread_mutex_unlock(&state_mutex
);
142 Called when the basic algorithm process ID becomes active.
144 void control_become_active_hook(int id
)
146 pthread_mutex_lock(&state_mutex
);
147 state
= ACTIVE_STATE
;
148 pthread_mutex_unlock(&state_mutex
);
152 Called when the basic algorithm process ID sends a basic message to
155 void control_basic_send_hook(int id
, int peer
)
157 pthread_mutex_lock(&state_mutex
);
158 deficit
++; // Rule A: Sent unacknowledged work
159 pthread_mutex_unlock(&state_mutex
);
163 Called when the basic algorithm process ID receives a basic message
166 void control_basic_receive_hook(int id
, int peer
)
168 pthread_mutex_lock(&state_mutex
);
172 // Rule B (Case 1): Woken up from passive state
173 state
= ACTIVE_STATE
;
174 parent
= peer
; // Adopt sender as parent in the tree
175 trace("%d: [CONTROL] Joined tree under parent %d\n", id
, parent
);
179 // Rule B (Case 2): Already active! Reject parent change, immediately signal back
180 control_message_t sig
= {0};
181 MPI_Send(&sig
, sizeof(control_message_t
), MPI_BYTE
, peer
,
182 CONTROL_SIGNAL
, MPI_COMM_WORLD
);
183 trace("%d: [CONTROL] Rejected parent %d (Already Active)\n", id
, peer
);
186 pthread_mutex_unlock(&state_mutex
);