I do most of my multithreading using Ada. The following Ada example shows how applying a timeout to the wait for a condition variable allows the consumer to handle a non-responsive producer, and the producer to handle a non-responsive consumer.
------------------------------------------------------------------
-- Producer-Consumer Package --
------------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
procedure Protected_Producer_Consumer is
protected Buffer is
entry Put(Item : in Integer);
entry Get(Item : out Integer);
private
Value : Integer := Integer'First;
Is_New : Boolean := False;
end Buffer;
protected body Buffer is
entry Put(Item : in Integer) when not Is_New is
begin
Value := Item;
Is_New := True;
end Put;
entry Get(Item : out Integer) when Is_New is
begin
Item := Value;
Is_New := False;
end Get;
end Buffer;
task producer;
task body producer is
Wait_Limit : constant Natural := 5;
Written : Boolean := False;
begin
for value in 1..15 loop
Written := False;
for try in 1..Wait_Limit loop
Select
Buffer.Put(Value);
Written := True;
or
delay 0.5;
end select;
exit when Written;
end loop;
if not Written then
Put_Line("Producer terminating. Consumer not responding.");
exit;
end if;
end loop;
end producer;
task consumer;
task body consumer is
Wait_Limit : Natural := 5;
Value_Read : Boolean;
The_Value : Integer;
begin
Loop
Value_Read := False;
for try in 1..Wait_Limit loop
select
Buffer.Get(The_Value);
Value_Read := True;
Put_Line("Consumer read value: " & Integer'Image(The_Value));
or
delay 0.5;
end select;
exit when Value_Read;
end loop;
if not Value_Read then
Put_Line("Consumer terminating. Producer not responding.");
exit;
end if;
end loop;
end Consumer;
begin
null;
end Protected_Producer_Consumer;
Ada protected objects, such as Buffer in the example above, provide automatic mutual exclusion. In the example above the Buffer object has two entries, Put and Get. The Put entry can only be executed when the Buffer internal variable Is_New is False. The Get entry can only be executed when the Buffer internal variable Is_New is True.
The Producer task (similar to a thread in C++) contains an outer loop that sets the variable "value" to first 1, then 2, and so on up to 15. The inner loop tries to Put the value in the Buffer up to Wait_Limit times. Each time the Producer sets a timer for one half second, and then tries again if not successful. If the producer fails Wait_Limit times it writes an error message and terminates.
The consumer behavior is similar to the producer. It reads values from Buffer by calling the Get entry. It also waits one half second for each try, and terminates after Wait_Limit successive failures to read a value from Buffer.
The output of this program is:
Consumer read value: 1
Consumer read value: 2
Consumer read value: 3
Consumer read value: 4
Consumer read value: 5
Consumer read value: 6
Consumer read value: 7
Consumer read value: 8
Consumer read value: 9
Consumer read value: 10
Consumer read value: 11
Consumer read value: 12
Consumer read value: 13
Consumer read value: 14
Consumer read value: 15
Consumer terminating. Producer not responding.