Вы находитесь на странице: 1из 9

Лабораторная работа №5

«Расширенные функции синхронизации потоков»

Цель работы: детальное изучение возможностей стандартной библиотеки Pthreads,


предназначенных для синхронизации потоков в ОС Linux..

Задачи: получение практических навыков в области разработки специализированных функций с


использование стандартной библиотеки Pthreads.

Задание

1) Напишите функцию, удовлетворяющую следующим условиям:


a. Прототип функции:
int lab_pthread_mutex_trylock(pthread_mutex_t *mutex);
b. Входной параметр: адрес мьютекса
c. Функция возвращает следующие значения:
0 в случае, если мьютекс может быть захвачен без блокировки потока,
1 если попытка захвата мьютекса с помощью функции pthread_mutex_lock(…) приведет к
блокировке вызывающего ее потока,
-1 в случае любой ошибки.

2) Напишите функцию, удовлетворяющую следующим условиям:


a. Прототип функции:

int lab_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t


*mutex, unsigned int timetowait);
b. Входные параметры: адрес условной переменной, адрес мьютекса, количество ми л- лисекунд,
устанавливаемое для ожидания условной переменной.
c. Функция возвращает следующие значения:
0 в случае успешного выполнения функции,
1 в случае, если время ожидания условной переменной превысило заданное в п а- раметре
timetowait количество миллисекунд,
-1 в случае любой ошибки.

Ход работы
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <sys/errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <map>

#define PTHREAD_MUTEX_INCONSISTENT __INT_MAX__

#define PTHREAD_MUTEX_NOTRECOVERABLE (__INT_MAX__ - 1)

Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 1
using namespace std;

struct CustomCondTimedWait_Data
{
timer_t RelatedTimerAddress;
bool UnlockedByTimer = false;
};

struct CustomMutexTryLock_Data
{
pthread_mutex_t *RelatedMutex = nullptr;
bool IsGoingToGetLocked = false;
};

map<pthread_cond_t *, CustomCondTimedWait_Data> ConditionPointerToState;


map<pthread_mutex_t *, CustomMutexTryLock_Data *> MutexToRelatedMutexData;

pthread_mutex_t Mutex;

pthread_mutex_t Mutex_1 = PTHREAD_MUTEX_INITIALIZER;


pthread_mutex_t Mutex_2 = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t Condition_1 = PTHREAD_COND_INITIALIZER;


pthread_cond_t Condition_2 = PTHREAD_COND_INITIALIZER;

bool ThreadCanStart = false;

void InitializeMutex(pthread_mutex_t *MutexToInitialize)


{
*MutexToInitialize = PTHREAD_MUTEX_INITIALIZER;

auto it = MutexToRelatedMutexData.find(MutexToInitialize);
if (it == MutexToRelatedMutexData.end())
{
CustomMutexTryLock_Data *MutexData = new CustomMutexTryLock_Data;
MutexData->IsGoingToGetLocked = false;
MutexData->RelatedMutex = new pthread_mutex_t;
*MutexData->RelatedMutex = PTHREAD_MUTEX_INITIALIZER;
MutexToRelatedMutexData.insert(pair<pthread_mutex_t *, CustomMutexTryLock_Data
*>(MutexToInitialize, MutexData));
}
}

//__data.__lock == 0 if mutex is not locked, 1 if locked ?????


//__data.__nusers == current number of threads locked on this mutex (including 1st one)
//__data.__owner == thread ID of the thread that is holding this mutexMutex
//__data.count == 1 if mutex has been locked by any thread, 0 otherwise ?????
//__data.kind == PTHREAD_MUTEX_KIND_MASK_NP or
PTHREAD_MUTEX_ROBUST_NORMAL_NP

int lab_pthread_mutex_trylock(pthread_mutex_t *mutex)


{
//If mutex pointer is not valid
Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 2
if (!mutex)
{
cout << "lab_pthread_mutex_trylock: Mutex to be locked was null" << endl;
return -1;
}
//It mutex, recieved as argument, was not initialized through custom function (has no related mutex
and lock flag), return with error.
auto it = MutexToRelatedMutexData.find(mutex);
if (it == MutexToRelatedMutexData.end())
{
cout << "lab_pthread_mutex_trylock: Mutex to be locked was not initialized using
InitializeMutex()" << endl;
return -1;
}
else
{
CustomMutexTryLock_Data* RelatedMutexData = it->second;
//Locking related mutex, so only one thread at a time can check mutex's owner.
pthread_mutex_lock(RelatedMutexData->RelatedMutex);
//To make situation where thread, that is going to lock main mutex, unlocked related mutex, but not
yet locked the main one, and any other thread
//got to checking main mutex's owner while it is still null/inconsistent/notrecoverable impossible.
if (RelatedMutexData->IsGoingToGetLocked)
{
return 1;
}
//If Mutex Is Has No Owner, Owner Has Already Died, Or Is Not Recoverable.
//We Should Be Able To Lock It, Even If It Has Not Been Unlocked.
if ((mutex->__data.__owner == 0) || (mutex->__data.__owner ==
PTHREAD_MUTEX_INCONSISTENT) || (mutex->__data.__owner ==
PTHREAD_MUTEX_NOTRECOVERABLE))
{
RelatedMutexData->IsGoingToGetLocked = true;
pthread_mutex_unlock(RelatedMutexData->RelatedMutex);
pthread_mutex_lock(mutex);
RelatedMutexData->IsGoingToGetLocked = false;
return 0;
}
else
{
pthread_mutex_unlock(RelatedMutexData->RelatedMutex);
return 1;
}
}
}

void TimerHandler(int Signal, siginfo_t *SignalInfo, void *uc)


{
pthread_cond_t *ConditionToUnlock = (pthread_cond_t *)SignalInfo->si_value.sival_ptr;
auto it = ConditionPointerToState.find(ConditionToUnlock);
//Setting this condition's UnlockedByTimer flag in map to true.
it->second.UnlockedByTimer = true;
//Destroying related timer.
Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 3
timer_delete(it->second.RelatedTimerAddress);
pthread_cond_signal(ConditionToUnlock);
}

//Returns TimerAddress if timer has been successflly created, nullptr otherwise.


timer_t CreateTimer(char *Value, int64_t Interval_InMilliSeconds)
{
struct sigevent TimerEvent;
struct itimerspec TimerIntervals;
struct sigaction SignalAction;
//Selecting signal that timer will send.
int SignamNumber = SIGRTMIN;

//For signal to send 3 arguments in handler.


SignalAction.sa_flags = SA_SIGINFO;
//Binding handler to action.
SignalAction.sa_sigaction = TimerHandler;
sigemptyset(&SignalAction.sa_mask);

//Binding action to signal from timer.


if (sigaction(SIGRTMIN, &SignalAction, NULL) == -1)
return nullptr;

// Set up timer
TimerEvent.sigev_notify = SIGEV_SIGNAL;
TimerEvent.sigev_signo = SignamNumber;
TimerEvent.sigev_value.sival_ptr = Value;

timer_t TimerAddress;
if (timer_create(CLOCK_REALTIME, &TimerEvent, &TimerAddress) == -1)
return nullptr;

//Setting only current value (interval stays at 0) for timer to trigger once.
//Getting Seconds
TimerIntervals.it_value.tv_sec = Interval_InMilliSeconds / 1000000;
//And Remainder In Milliseconds
TimerIntervals.it_value.tv_nsec = Interval_InMilliSeconds % 1000000;
TimerIntervals.it_interval.tv_sec = 0;
TimerIntervals.it_interval.tv_nsec = 0;

if (timer_settime(TimerAddress, 0, &TimerIntervals, NULL) == -1)


return nullptr;

return TimerAddress;
}

int lab_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, unsigned long int


timetowait)
{
//If cond or mutex is null, return -1.
if (!(cond && mutex))
{
return -1;
Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 4
}
timeval CurrentTime;
struct timezone TimeZone;
gettimeofday(&CurrentTime, &TimeZone);
//Absolute time to relative.
int64_t SelfUnlock_TimeThreshold = (timetowait - (CurrentTime.tv_sec * 1000000 +
CurrentTime.tv_usec));
//Checking If Time Already Ran Out
if (SelfUnlock_TimeThreshold <= 0)
{
return -1;
}

//Creating timer. Converting seconds to milliseconds.


timer_t CreatedTimerAddress = CreateTimer((char *)cond, SelfUnlock_TimeThreshold);

//Resetting this condition's UnlockedByTimer flag in map.


CustomCondTimedWait_Data CondData;
CondData.UnlockedByTimer = false;
CondData.RelatedTimerAddress = CreatedTimerAddress;
if (!ConditionPointerToState.insert(pair<pthread_cond_t *, CustomCondTimedWait_Data>(cond,
CondData)).second)
{
auto it = ConditionPointerToState.find(cond);
if (it != ConditionPointerToState.end())
{
it->second = CondData;
}
}

int OriginalCondWait_ReturnValue = pthread_cond_wait(cond, mutex);

//If there was an error in original pthread_cond_wait.


if (OriginalCondWait_ReturnValue != 0)
{
//Error code does not matter according to task.
return -1;
}
else
{
//If condition variable was unlocked by timer, return 1.
bool UnlockedOnTimer = false;
auto it = ConditionPointerToState.find(cond);
if (it != ConditionPointerToState.end())
{
UnlockedOnTimer = it->second.UnlockedByTimer;
}
if (UnlockedOnTimer)
{
return 1;
}
else
{
Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 5
return 0;
}
}
}

void *Mutex_Thread_StartingPoint(void *ThreadNumber)


{
int l_Threadnumber = *(long int *)ThreadNumber;
srand(pthread_self());
float TimeToSleep = (float)((float)(rand() % 500) / (float)100) + 0.25;
sleep(TimeToSleep);
int TryLockReturnValue = lab_pthread_mutex_trylock(&Mutex);
//Give COUT Time To Print String.
//if (TryLockReturnValue)
// sleep(0.1);
char* TryLockResultString;
switch (TryLockReturnValue)
{
case 1:
{
TryLockResultString = "Failed To Lock Mutex";
break;
}
case -1:
{
TryLockResultString = "Encountered An Error In lab_pthread_mutex_trylock";
break;
}
case 0:
{
TryLockResultString = "Locked Mutex";
break;
}
}
cout << "Thread Number " << l_Threadnumber << " Had To Sleep For " << TimeToSleep << "
Seconds And " << TryLockResultString << endl;
//cout << "Thread Number " << l_Threadnumber << " Finished" << endl;
}

void *Cond_Thread_StartingPoint(void *ThreadNumber)


{
int l_Threadnumber = *(long int *)ThreadNumber;
//First thread is stuck - condition would never get false. Automatically unlock it after X seconds.
if (l_Threadnumber == 1)
{
time_t Time = time(0);
cout << "Thread 1 Will Now Call Cond_TimedWait. " << ctime(&Time) << endl;
pthread_mutex_lock(&Mutex_1);
while (1)
{
timeval TimeToUnlockConditionAt;
struct timezone TimeZone;
gettimeofday(&TimeToUnlockConditionAt, &TimeZone);
Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 6
cout << l_Threadnumber << " Thread Locked On Condition" << endl;
int ReturnedValue = lab_pthread_cond_timedwait(&Condition_1, &Mutex_1,
(TimeToUnlockConditionAt.tv_sec + 3) * 1000000 + TimeToUnlockConditionAt.tv_usec);
Time = time(0);
cout << l_Threadnumber << " Thread's Condition Wait Returned: " << ReturnedValue << " At "
<< ctime(&Time) << endl;
if (ReturnedValue == 1)
{
break;
}
}
}
//Second thread will unlock in 31 year - we need to unlock it ourselves.
else if (l_Threadnumber == 2)
{
pthread_mutex_lock(&Mutex_2);
while (!ThreadCanStart)
{
timeval TimeToUnlockConditionAt;
struct timezone TimeZone;
gettimeofday(&TimeToUnlockConditionAt, &TimeZone);
cout << l_Threadnumber << " Thread Locked On Condition" << endl;
int ReturnedValue = lab_pthread_cond_timedwait(&Condition_2, &Mutex_2,
(TimeToUnlockConditionAt.tv_sec + 999999999) * 1000000 + TimeToUnlockConditionAt.tv_usec);
cout << l_Threadnumber << " Thread's Condition Wait Returned: " << ReturnedValue << endl;
}
}
//Third thread will unlock second in 1 second.
else if (l_Threadnumber == 3)
{
sleep(1);
ThreadCanStart = true;
pthread_cond_signal(&Condition_2);
}
}

int main(int argc, char *argv[])


{
InitializeMutex(&Mutex);
if (argv[1][0] == 'm')
{
pthread_t Threads[2];
int ThreadNumber_1 = 1;
if (pthread_create(&Threads[0], NULL, Mutex_Thread_StartingPoint, &ThreadNumber_1) != 0)
{
cout << "Failed To Create First Thread "
<< " : " << strerror(errno);
errno = 0;
}
int ThreadNumber_2 = 2;
if (pthread_create(&Threads[1], NULL, Mutex_Thread_StartingPoint, &ThreadNumber_2) != 0)
{
cout << "Failed To Create Second Thread "
Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 7
<< " : " << strerror(errno);
errno = 0;
}
int i = 0;
while (i < 2)
{
pthread_join(Threads[i], NULL);
i++;
}
}
else if (argv[1][0] == 'c')
{
pthread_t Threads[3];
int ThreadNumbers[3] = {1, 2, 3};
if (pthread_create(&Threads[0], NULL, Cond_Thread_StartingPoint, &ThreadNumbers[0]) != 0)
{
cout << "Failed To Create First Thread "
<< " : " << strerror(errno);
errno = 0;
}
sleep(0.1);
if (pthread_create(&Threads[1], NULL, Cond_Thread_StartingPoint, &ThreadNumbers[1]) != 0)
{
cout << "Failed To Create Second Thread "
<< " : " << strerror(errno);
errno = 0;
}
sleep(0.1);
if (pthread_create(&Threads[2], NULL, Cond_Thread_StartingPoint, &ThreadNumbers[2]) != 0)
{
cout << "Failed To Create Second Thread "
<< " : " << strerror(errno);
errno = 0;
}
int i = 0;
while (i < 3)
{
pthread_join(Threads[i], NULL);
i++;
}
}
}

Результаты работы:

Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 8
Результат работы программы. Попытки заблокировать мьютекс:

Результат работы программы. Условная переменная 1 потока разблокируется сама через 3 –


[время выполнения инструкций до запуска таймера] секунды.
Условная переменная 2 потока будет разблокирована 3 потоком через 1 секунду.

Лист
Лабораторная работа №5 ТОГУ, ИС-51, Храмцов А.А.
по Системному ПО 9

Вам также может понравиться