/********************************************************************
        Пакет многозадачности для Borland C++ 3.1 .

     Данный пакет позволяет писать программы на С++ в среде DOS
в терминах параллельных процессов.  Он включает в себя средства
создания задач и средства их взаимодействия. В качестве образца
для подражания выступал язык Ада. Из соображений эффективности
конечных программ я решил не отказываться от таких примитивных
средств взаимодействия задч, как семафоры и сигналы. В языке
Ада из соображений надежности их нет, а есть только механизм рандеву.

     В пакете имеется базовый класс task из которого можно
породить класс задач пользователя. В нем необходимо определить
виртуальную функцию virtual void body() , которая будет выполняться
в каждом экземпляре задач параллельно с такими же функциями других
экземпляров задач как этого же класса, так и других классов задач.
Можно создать конструктор класса для передачи параметров экземпляру
задачи при ее запуске.

     Задачу можно запустить на исполнение, создав экземпляр задачи.

     Программный блок не завершается до тех пор, пока не завершатся
все задачи, запущенные в нем! Задача завершается, когда управление
достигает ее конца.

     Всегда существует главная задача, которую не требуется никак
запускать. В ней работает функция main .

     Задачи, описанные как глобальные переменные, запускаются
до main, и программа не завершается, пока не завершатся все такие задачи.

     Задачи исполняются квазипараллельно, переключение управления
с задачи на задачу происходит в моменты взаимодействия задач,
а также при явном исполнении "функции" pause() .

     При описании класса пользовательской задачи необходимо определить
деструктор, выполняющий pause. Причина этому довольно тонкая, и при
невыполнении этого условия может произойти аварийное завершение
всей программы.

     Взаимодействие задач осуществляется с помощью механизмов
семафоров, сигналов и рандеву.

     Доступ к общим ресурсам задач регулируется с помощью
семафоров. Семафор - это двоичная переменная, которая регистрирует,
является ли доступным ресурс, который она защищает. Семафор имеет
две функции: void semaphor::get()  и  void semaphor::free() .
Взаимно исключающий доступ к общему ресурсу может быть обеспечен
путем заключения обращений к функциям доступа к ресурсу между
обращениями к функциям get и free .

     Для синхронизации задач можно пользоваться сигналами.
Сигналы используются задачей для сообщения другой задаче факта
совершения некоторого события. Для работы с сигналами имеются четыре
функции: void signal::send() , void signal::wait(), int signal::waited()
и signal* wait(signal*,...). Функция send посылает сигнал, wait ждет
сигнал, waited возвращает истину, если какая-либо задача ждет данный
сигнал, и ложь в противном случае. В качестве аргументов последней
функции передаются указатели на сигналы. Функция ожидает прихода хотя бы
одного из перечисленных сигналов. Результат этой функции - указатель
на тот сигнал, который пришел.

     В пакете имеется класс timer , порожденный от signal.
Переменные этого класса представляют собой таймеры, которые можно
запустить на некоторое число тиков ( 55 мс ) функцией
void timer::start(unsigned n). По истечение заданного времени придет
сигнал. Таймеры можно использовать для приостановки процесса на некоторое
время и для ограничения времени ожидания других сигналов
(функция signal* wait(signal*,...) ). Система гарантирует, что 
сигнал придет не раньше указанного времени, но он может прийти позже,
если к моменту прихода сигнала будет выполняться задача, не ожидающая 
никаких событий (семафоры, сигналы, рандеву) в течение длительного 
времени (например, выполняющая длинные вычисления или вызывающая 
системные функции работы с диском).
     
     Механизм рандеву осуществляет как синхронизацию задач, так
и передачу данных между задачами. Использование этого механизма
предпочтительнее, чем использование семафоров и сигналов, т.к.
взаимодействие задач в этом случае более понятно. К сожалению,
C++ не имеет языковых конструкций, позволяющих органично встроить
механизм рандеву в язык. Компилятор не в состоянии проследить за
правильностью использования этого механизма, вся ответственность
ложится на программиста. В пакете определен класс entry, объекты
которого можно создавать как члены классов задач. Это будут входы
в задачу. Класс entry имеет функции члены void call(void*),
void* accept(), void end_accept() и int ready(). Другая задача может
вызвать вход нашей задачи, обратившись к функции call этого входа и
передать указатель на данные. Пока задача, вход которой вызван, не
примет его, вызвавшая вход задача приостанавливается. Для того, чтобы
принять свой вход, задача должна вызвать функцию accept этого входа
и получить указатель на данные, переданные задачей, которая этот
вход вызвала. Если к моменту принятия входа он никем не был вызван,
принимающая вход задача приостанавливается до тех пор, пока вход не
будет вызван. Принимающая вход задача может работать с указателем на
данные, переданные вызывающей вход задачей, до исполнения функции
end_accept. До этого момента вызывающая вход задача стоит в функции
вызова входа. Таким образом, взаимодействующие задачи "встречаются"
в месте взаимодействия, принимающая вход задача работает с данными,
переданными вызывающей вход задачей, а после того, как принимающая
вход задача исполнит функцию end_accept, обе задачи продолжают работать.

     Для того, чтобы задача, занимающаяся вводом с клавиатуры, не
монополизировала процессор на время ожидания нажатия клавиши,
пользуйтесь для ввода с клавиатуры функциями conio.h, а не stdio.h.
Если используется библиотека Turbo Vision, необходимо определить функцию
void TMyApplication::idle() { TApplication::idle(); pause(); } .

     Каждой задаче отводится в куче стек размером 4k. В связи с
изменением расположения стека данный пакет работает только в моделях
памяти compact, large и huge, но только для модели large можно считать
его достаточно отлаженным.


					     Крупский В.В. СПб 1994

*********************************************************************/

//     пример использования

#include <conio.h>
#include "task.h"



class TReceiver : task
{
        void body();
   public:
        entry put_char;
        ~TReceiver() { pause(); }
}Receiver;

void TReceiver::body()
{
        void* ptr;
        char c;
        for(;;)
        {
           ptr=put_char.accept();
           {
             c=*((char*)ptr);
           }
           put_char.end_accept();
           putch(c);
           if(c==27) break;
        }
}

class TProducer : task
{
        void body();
  public:
        ~TProducer() { pause(); }
}Producer;

void TProducer::body()
{
        char c;
        for(;;)
        {
           c=getch();
           Receiver.put_char.call(&c);
           if(c==27) break;
        }
}


class TP : task
{
        void body();
  public:
        ~TP() { pause(); }
}P;

void TP::body()
{
        for(;;)
        {
           timer tt;
           tt.start(10);
           wait(&tt);
           Receiver.put_char.call(" ");
        }
}


void main()
{
}






