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 // Message types for control routing
26 // Type for control messages
31 // Node operational states
37 // --- SHARED LOCAL STATE ---
38 // Must be protected by mutex because basic_thread and control_thread access it concurrently
39 static pthread_mutex_t state_mutex
= PTHREAD_MUTEX_INITIALIZER
;
40 static node_state_t state
= PASSIVE_STATE
;
41 static int parent
= -1; // -1 indicates null/no parent
42 static int deficit
= 0; // C_i counter (unacknowledged messages)
43 static bool is_initiator
= false;
44 static bool started
= false; // RACE CONDITION FIX
46 // --- HELPER FUNCTION ---
47 // MUST BE DECLARED ABOVE THE OTHER FUNCTIONS!
48 // Evaluates if the node can detach from the tree. Caller must hold state_mutex.
49 static void try_resolve_tree(int my_id
)
51 if (started
&& state
== PASSIVE_STATE
&& deficit
== 0)
54 // Root is passive and deficit is 0 -> Global Termination!
60 control_message_t sig
= { MSG_ACK
};
61 // Send acknowledgment signal up the tree to our parent
62 MPI_Send(&sig
, sizeof(control_message_t
), MPI_BYTE
, parent
,
63 CONTROL_SIGNAL
, MPI_COMM_WORLD
);
65 trace("%d: [CONTROL] Sent tree-signal to parent %d\n", my_id
, parent
);
66 parent
= -1; // Detach from tree
72 Main loop of the termination detection algorithm.
74 void *detect_termination(void *_args
)
76 thread_args_t
*args
= _args
;
78 int processes
= args
->processes
; // Needed for the KILL broadcast
79 is_initiator
= args
->initiator
;
83 pthread_mutex_lock(&state_mutex
);
85 // CHECK GLOBAL TERMINATION CONDITION
86 // Only evaluate if the basic algorithm has officially started
87 if (started
&& is_initiator
&& state
== PASSIVE_STATE
&& deficit
== 0)
89 trace("%d: [CONTROL] GLOBAL TERMINATION DETECTED!\n", id
);
91 // Broadcast KILL signal to unblock all other control threads
92 control_message_t kill_sig
= { MSG_KILL
};
93 for (int p
= 0; p
< processes
; p
++) {
95 MPI_Send(&kill_sig
, sizeof(control_message_t
), MPI_BYTE
, p
,
96 CONTROL_SIGNAL
, MPI_COMM_WORLD
);
100 pthread_mutex_unlock(&state_mutex
);
101 break; // Breaks loop, tells main() to shut down
104 pthread_mutex_unlock(&state_mutex
);
106 // Check network for incoming child signals (non-blocking)
109 MPI_Iprobe(MPI_ANY_SOURCE
, CONTROL_SIGNAL
, MPI_COMM_WORLD
, &flag
, &status
);
113 control_message_t sig
;
114 MPI_Recv(&sig
, sizeof(control_message_t
), MPI_BYTE
, status
.MPI_SOURCE
,
115 CONTROL_SIGNAL
, MPI_COMM_WORLD
, MPI_STATUS_IGNORE
);
117 if (sig
.type
== MSG_KILL
) {
118 trace("%d: [CONTROL] Received KILL signal. Shutting down.\n", id
);
121 else if (sig
.type
== MSG_ACK
) {
122 pthread_mutex_lock(&state_mutex
);
123 deficit
--; // Rule D: A child finished its work and reported back
124 trace("%d: [CONTROL] Got signal from %d (New Deficit: %d)\n",
125 id
, status
.MPI_SOURCE
, deficit
);
127 try_resolve_tree(id
);
128 pthread_mutex_unlock(&state_mutex
);
133 // Sleep briefly to prevent pinning the CPU core at 100% usage
142 Called at startup of the basic algorithm in process ID.
144 void control_basic_start_hook(int id
)
146 pthread_mutex_lock(&state_mutex
);
147 is_initiator
= (id
== 0);
148 state
= is_initiator
? ACTIVE_STATE
: PASSIVE_STATE
;
151 started
= true; // Signal the control thread that it is safe to check for termination
152 pthread_mutex_unlock(&state_mutex
);
156 Called when the basic algorithm process ID becomes passive.
158 void control_become_passive_hook(int id
)
160 pthread_mutex_lock(&state_mutex
);
161 state
= PASSIVE_STATE
;
162 try_resolve_tree(id
); // Check if we can leave the tree right now
163 pthread_mutex_unlock(&state_mutex
);
167 Called when the basic algorithm process ID becomes active.
169 void control_become_active_hook(int id
)
171 pthread_mutex_lock(&state_mutex
);
172 state
= ACTIVE_STATE
;
173 pthread_mutex_unlock(&state_mutex
);
177 Called when the basic algorithm process ID sends a basic message to
180 void control_basic_send_hook(int id
, int peer
)
182 pthread_mutex_lock(&state_mutex
);
183 deficit
++; // Rule A: Sent unacknowledged work
184 pthread_mutex_unlock(&state_mutex
);
188 Called when the basic algorithm process ID receives a basic message
191 void control_basic_receive_hook(int id
, int peer
)
193 pthread_mutex_lock(&state_mutex
);
195 // A node is only "out of the tree" if parent == -1.
196 // It can be PASSIVE but still in the tree if it is waiting for children (deficit > 0).
197 if (parent
== -1 && !is_initiator
)
199 // Not in the tree -> Join it
201 trace("%d: [CONTROL] Joined tree under parent %d\n", id
, parent
);
205 // Already in the tree (or is root) -> Reject parent change, immediately signal back
206 control_message_t sig
= { MSG_ACK
};
207 MPI_Send(&sig
, sizeof(control_message_t
), MPI_BYTE
, peer
,
208 CONTROL_SIGNAL
, MPI_COMM_WORLD
);
209 trace("%d: [CONTROL] Rejected parent %d (Already in tree or Root)\n", id
, peer
);
212 pthread_mutex_unlock(&state_mutex
);