{******************************************************************

                       JEDI-VCL Demo

 Copyright (C) 2002 Project JEDI

 Original author:

 Contributor(s):

 You may retrieve the latest version of this file at the JEDI-JVCL
 home page, located at http://jvcl.delphi-jedi.org

 The contents of this file are used with permission, subject to
 the Mozilla Public License Version 1.1 (the "License"); you may
 not use this file except in compliance with the License. You may
 obtain a copy of the License at
 http://www.mozilla.org/MPL/MPL-1_1Final.html

 Software distributed under the License is distributed on an
 "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 implied. See the License for the specific language governing
 rights and limitations under the License.

******************************************************************}

unit fPhilosophers;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, JvMTThreading, ExtCtrls, ComCtrls,
  JvMtComponents, JvComponent;

type
  TPhilosopherState = (psThinking, psHungry, psEating);

  TPerson = class
  public
    Nr: Integer;
    constructor Create(ANumber: Integer);
  end;

  TMsg = class(TObject)
  public
    Msg: string;
    constructor Create(AMsg: string);
  end;

  TfrmDiningPhilosophers = class(TForm)
    PhilosopherManager: TJvMtManager;
    PhilosopherThread: TJvMtThread;
    MonitorSection: TJvMtMonitorSection;
    Memo: TMemo;
    BtnStart: TButton;
    BtnTerminate: TButton;
    PersonBuffer: TJvMtVCLToThread;
    MsgToVCL: TJvMtThreadToVCL;
    PhilLabel1: TLabel;
    PhilLabel2: TLabel;
    PhilLabel3: TLabel;
    PhilLabel4: TLabel;
    PhilLabel5: TLabel;
    Shape1: TShape;
    SpeedBar: TTrackBar;
    LblSpeed: TLabel;
    BtnClose: TButton;
    procedure BtnStartClick(Sender: TObject);
    procedure BtnTerminateClick(Sender: TObject);
    procedure PersonBufferCanWrite(Sender: TObject);
    procedure MsgToVCLCanRead(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure SpeedBarChange(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure BtnCloseClick(Sender: TObject);
    procedure PhilosopherThreadExecute(Sender: TJvMtThread;
      MTThread: TJvMtSingleThread);
  private
    { Private declarations }
    FSpeed: Integer;
    FNrCycle: Integer;
    FState: Array[0..4] of TPhilosopherState;
    procedure SetPhilLabelsEnabled(Value: Boolean);
    procedure TerminatePhilosophers;
    procedure ProcessMsg(M: string);
    procedure WaitRandom;
  public
    { Public declarations }
  end;

var
  frmDiningPhilosophers: TfrmDiningPhilosophers;

implementation

{$R *.dfm}

{ TPerson }

constructor TPerson.Create(ANumber: Integer);
begin
  Nr := ANumber;
end;

{ TMsg }

constructor TMsg.Create(AMsg: string);
begin
  Msg := AMsg;
end;

{ TForm2 }


procedure TfrmDiningPhilosophers.WaitRandom;
var
  I: Integer;
begin
  for I := 0 to 5+Random(15) do
  begin
    Sleep(FSpeed);
    CurrentMTThread.CheckTerminate;
  end;
end;

procedure TfrmDiningPhilosophers.BtnStartClick(Sender: TObject);
var
  I: Integer;
begin
  Memo.Lines.Add('Terminating previous philosophers...');
  TerminatePhilosophers;
  PhilosopherManager.WaitThreads;

  Memo.Lines.Add('Initiating five new philosophers...');

  // reset the states
  for I := 0 to 4 do
    FState[I] := psThinking;

  // make 5 philosophers
  for I := 0 to 4 do
    PhilosopherThread.RunCopy;

  SetPhilLabelsEnabled(True);
end;

procedure TfrmDiningPhilosophers.BtnTerminateClick(Sender: TObject);
begin
  Memo.Lines.Add('Terminating all philosophers...');
  TerminatePhilosophers;
end;

procedure TfrmDiningPhilosophers.PersonBufferCanWrite(Sender: TObject);
begin
  PersonBuffer.Write(TPerson.Create(FNrCycle));
  FNrCycle := (FNrCycle+1) mod 5;
end;

procedure TfrmDiningPhilosophers.ProcessMsg(M: string);
var
  Nr: Integer;
  Lbl: TLabel;
begin
  Nr := StrToInt(Copy(M,1,1))+1;
  Lbl:=FindComponent('PhilLabel'+IntToStr(Nr)) as TLabel;
  if Assigned(Lbl) then Lbl.Caption := Copy(M,2,255);
end;

procedure TfrmDiningPhilosophers.MsgToVCLCanRead(Sender: TObject);
var
  M: TMsg;
begin
  M := TMsg(MsgToVCL.Read);
  try
    ProcessMsg(M.Msg);
  finally
    M.Free;
  end;
end;

procedure TfrmDiningPhilosophers.FormCreate(Sender: TObject);
begin
  Randomize;
end;

procedure TfrmDiningPhilosophers.TerminatePhilosophers;
begin
  // terminate any philosphers
  PhilosopherManager.TerminateThreads;
  SetPhilLabelsEnabled(False);
end;

procedure TfrmDiningPhilosophers.SetPhilLabelsEnabled(Value: Boolean);
begin
  PhilLabel1.Enabled := Value;
  PhilLabel2.Enabled := Value;
  PhilLabel3.Enabled := Value;
  PhilLabel4.Enabled := Value;
  PhilLabel5.Enabled := Value;
end;

procedure TfrmDiningPhilosophers.SpeedBarChange(Sender: TObject);
begin
  FSpeed := SpeedBar.Position;
end;

procedure TfrmDiningPhilosophers.FormShow(Sender: TObject);
begin
  FSpeed := SpeedBar.Position;
end;

procedure TfrmDiningPhilosophers.BtnCloseClick(Sender: TObject);
begin
  Close;
end;

procedure TfrmDiningPhilosophers.PhilosopherThreadExecute(
  Sender: TJvMtThread; MTThread: TJvMtSingleThread);
  procedure Test(Nr: Integer);
  begin
    if (FState[(Nr+4) mod 5] <> psEating) and (FState[Nr] = psHungry) and
      (FState[(Nr+1) mod 5] <> psEating) then
    begin
      FState[Nr] := psEating;
      MonitorSection[Nr].Signal;
    end;
  end;

  procedure PickupChopsticks(Nr: Integer);
  begin
    MonitorSection.Enter;
    try
      FState[Nr] := psHungry;
      Test(Nr);
      if FState[Nr] <> psEating then
        MonitorSection[Nr].Wait;
    finally
      MonitorSection.Leave;
    end;
  end;

  procedure PutdownChopsticks(Nr: Integer);
  begin
    MonitorSection.Enter;
    try
      FState[Nr] := psThinking;
      Test((Nr+4) mod 5);
      Test((Nr+1) mod 5);
    finally
      MonitorSection.Leave;
    end;
  end;

  procedure Msg(S: string);
  begin
    //OutputDebugString(PChar(S));
    MsgToVCL.Write(TMsg.Create(S));
  end;

var
  Person: TPerson;

begin
  OutputDebugString(PChar('Philosopher is waiting for personality...'));
  Person := PersonBuffer.Read as TPerson;
  try
    Msg(IntToStr(Person.Nr)+' Acquired personality');

    while True do
    begin
      // philosopher is thinking
      Msg(IntToStr(Person.Nr)+' Thinking');
      WaitRandom;

      // philosopher is hungry
      Msg(IntToStr(Person.Nr)+' Pickup chopsticks');
      PickupChopsticks(Person.Nr);

      // philosopher is eating
      Msg(IntToStr(Person.Nr)+' Eating');
      WaitRandom;

      // philosopher is finished eating
      //Msg(IntToStr(Person.Nr)+' Putdown chopsticks');
      PutdownChopsticks(Person.Nr);
    end;

  finally
    Person.Free;
  end;
end;

end.