mirror of https://github.com/t1meshift/os_labs.git
Add 11th lab
parent
9be2f2302e
commit
f6cf3ad3eb
|
@ -10,11 +10,14 @@
|
||||||
- [Лабораторная работа 7](lab7/README.md)
|
- [Лабораторная работа 7](lab7/README.md)
|
||||||
- [Лабораторная работа 9](lab9/README.md)
|
- [Лабораторная работа 9](lab9/README.md)
|
||||||
- [Лабораторная работа 10](lab10/README.md)
|
- [Лабораторная работа 10](lab10/README.md)
|
||||||
|
- [Лабораторная работа 11](lab11/README.md)
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
Требуется CMake 3.16+ (более ранние версии не проверялись) и компилятор с поддержкой C11.
|
Требуется CMake 3.16+ (более ранние версии не проверялись) и компилятор с поддержкой C11.
|
||||||
|
|
||||||
|
Для лабораторной №11 требуются make и gcc.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./run_lab.sh <lab_directory>
|
./run_lab.sh <lab_directory>
|
||||||
```
|
```
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
ALL = vector-deadlock vector-global-order vector-try-wait vector-avoid-hold-and-wait vector-nolock
|
||||||
|
COMMON = vector-header.h main-common.c main-header.h
|
||||||
|
|
||||||
|
all: $(ALL)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(ALL) *~
|
||||||
|
|
||||||
|
vector-deadlock: vector-deadlock.c $(COMMON)
|
||||||
|
gcc -o vector-deadlock vector-deadlock.c -Wall -pthread -O
|
||||||
|
|
||||||
|
vector-global-order: vector-global-order.c $(COMMON)
|
||||||
|
gcc -o vector-global-order vector-global-order.c -Wall -pthread -O
|
||||||
|
|
||||||
|
vector-try-wait: vector-try-wait.c $(COMMON)
|
||||||
|
gcc -o vector-try-wait vector-try-wait.c -Wall -pthread -O
|
||||||
|
|
||||||
|
vector-avoid-hold-and-wait: vector-avoid-hold-and-wait.c $(COMMON)
|
||||||
|
gcc -o vector-avoid-hold-and-wait vector-avoid-hold-and-wait.c -Wall -pthread -O
|
||||||
|
|
||||||
|
vector-nolock: vector-nolock.c $(COMMON)
|
||||||
|
gcc -o vector-nolock vector-nolock.c -Wall -pthread -O
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Лабораторная работа №11
|
||||||
|
|
||||||
|
## Задание 1
|
||||||
|
|
||||||
|
Программа печатает в каждом потоке начальные данные перед `vector_add` и результат выполнения данной функции.
|
||||||
|
Результат может меняться от вызова к вызову, так как весь worker не покрыт мьютексами, но на практике на такой небольшой
|
||||||
|
программе это маловероятно достижимо.
|
||||||
|
|
||||||
|
При добавлении `-d` взаимная блокировка возникает не всегда, а только при попадании переключения потоков между мьютексами.
|
||||||
|
|
||||||
|
В случае, если число потоков = 1, взаимной блокировки не возникнет.
|
||||||
|
|
||||||
|
## Задание 2
|
||||||
|
|
||||||
|
Программа избегает мёртвой блокировки за счёт упорядочивания по адресам, что позволяет постоянно сохранять порядок
|
||||||
|
блокировки.
|
||||||
|
|
||||||
|
В случае если адреса совпадают, то это один мьютекс, и для корректной работы программы его надо блокировать 1 раз.
|
||||||
|
|
||||||
|
В случае увеличения числа циклов и потоков, время выполнения растёт.
|
||||||
|
|
||||||
|
В случае включения `-p` время уменьшается, так как разрешается параллелизм.
|
||||||
|
|
||||||
|
## Задание 3
|
||||||
|
|
||||||
|
Вызовы pthread_mutex_trylock необходимы для создания порядка блокировки, для того чтобы избежать дедлока.
|
||||||
|
|
||||||
|
С увеличением числа потоков происходит рост повторных попыток, что является логичным, так как переключение между потоками
|
||||||
|
становится более частым.
|
||||||
|
|
||||||
|
## Задание 4
|
||||||
|
|
||||||
|
Данный подход защищает уязвимое место дедлока, созданием глобального мьютекса, но при этом не даёт различным векторам
|
||||||
|
выполняться параллельно.
|
||||||
|
|
||||||
|
При использовании `-p` время уменьшается.
|
||||||
|
|
||||||
|
## Задание 5
|
||||||
|
|
||||||
|
Указав memory, мы дожидаемся завершения всех операцией с памятью, что своего рода позволяет заменить мьютексы.
|
||||||
|
(https://ru.wikipedia.org/wiki/GCC_Inline_Assembly)
|
||||||
|
|
||||||
|
Сравним время выполнения следующих команд:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./vector-nolock -t -n 2 -l 1000000 -d = 4.08
|
||||||
|
./vector-nolock -t -n 2 -l 1000000 -d -p = 0.65
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d = 2.98
|
||||||
|
./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d -p = 0.45
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
./vector-try-wait -t -n 2 -l 1000000 -d = 1.30
|
||||||
|
./vector-try-wait -t -n 2 -l 1000000 -d -p = 0.18
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
./vector-global-order -t -n 2 -l 1000000 -d = 0.69
|
||||||
|
./vector-global-order -t -n 2 -l 1000000 -d -p = 0.19
|
||||||
|
```
|
||||||
|
|
||||||
|
Таким образом видно, что vector-nolock работает медленнее других в любом случае.
|
|
@ -0,0 +1,89 @@
|
||||||
|
|
||||||
|
This homework lets you play around with a number of ways to implement a small,
|
||||||
|
deadlock-free vector object in C. The vector object is quite limited (e.g., it
|
||||||
|
only has add() and init() functions) but is just used to illustrate different
|
||||||
|
approaches to avoiding deadlock.
|
||||||
|
|
||||||
|
Some files that you should pay attention to are as follows. They, in
|
||||||
|
particular, are used by all the variants in this homework.
|
||||||
|
|
||||||
|
- mythreads.h
|
||||||
|
The usual wrappers around many different pthread (and other) library calls,
|
||||||
|
so as to ensure they are not failing silently
|
||||||
|
|
||||||
|
- vector-header.h
|
||||||
|
A simple header for the vector routines, mostly defining a fixed vector size
|
||||||
|
and then a struct that is used per vector (vector_t)
|
||||||
|
|
||||||
|
- main-header.h
|
||||||
|
A number of global variables common to each different program
|
||||||
|
|
||||||
|
- main-common.c
|
||||||
|
Contains the main() routine (with arg parsing) that initializes two vectors,
|
||||||
|
starts some threads to access them (via a worker() routine), and then waits
|
||||||
|
for the many vector_add()'s to complete
|
||||||
|
|
||||||
|
The variants of this homework are found in the following files. Each takes a
|
||||||
|
different approach to dealing with concurrency inside a "vector addition"
|
||||||
|
routine called vector_add(); examine the code in these files to get a sense of
|
||||||
|
what is going on. They all use the files above to make a complete runnable
|
||||||
|
program.
|
||||||
|
|
||||||
|
- vector-deadlock.c
|
||||||
|
This version blithely grabs the locks in a particular order (dst then
|
||||||
|
src). By doing so, it creates an "invitation to deadlock", as one thread
|
||||||
|
might call vector_add(v1, v2) while another concurrently calls
|
||||||
|
vector_add(v2, v1).
|
||||||
|
|
||||||
|
- vector-global-order.c
|
||||||
|
This version of vector_add() grabs the locks in a total order, based on
|
||||||
|
address of the vector.
|
||||||
|
|
||||||
|
- vector-try-wait.c
|
||||||
|
This version of vector_add() uses pthread_mutex_trylock() to attempt to grab
|
||||||
|
locks; when the try fails, the code releases any locks it may hold and goes
|
||||||
|
back to the top and tries it all over again.
|
||||||
|
|
||||||
|
- vector-avoid-hold-and-wait.c
|
||||||
|
This version of vector_add() ensures it can't get stuck in a hold and wait
|
||||||
|
pattern by using a single lock around lock acquisition.
|
||||||
|
|
||||||
|
- vector-nolock.c
|
||||||
|
This version of vector_add() doesn't even use locks; rather, it uses an
|
||||||
|
atomic fetch-and-add to implement the vector_add() routine. Its semantics
|
||||||
|
(as a result) are slightly different.
|
||||||
|
|
||||||
|
Type "make" (and read the Makefile) to build each of five executables.
|
||||||
|
|
||||||
|
prompt> make
|
||||||
|
|
||||||
|
Then you can run a program by simply typing its name:
|
||||||
|
|
||||||
|
prompt> ./vector-deadlock
|
||||||
|
|
||||||
|
Each program takes the same set of arguments (see main-common.c for details):
|
||||||
|
|
||||||
|
-d
|
||||||
|
This flag turns on the ability for threads to deadlock.
|
||||||
|
When you pass -d to the program, every other thread calls vector_add()
|
||||||
|
with the vectors in a different order, e.g., with two threads, and -d enabled,
|
||||||
|
Thread 0 calls vector_add(v1, v2) and Thread 1 calls vector_add(v2, v1)
|
||||||
|
|
||||||
|
-p
|
||||||
|
This flag gives each thread a different set of vectors to call add()
|
||||||
|
upon, instead of just two vectors. Use this to see how things perform
|
||||||
|
when there isn't contention for the same set of vectors.
|
||||||
|
|
||||||
|
-n num_threads
|
||||||
|
Creates some number of threads; you need more than one to deadlock.
|
||||||
|
|
||||||
|
-l loops
|
||||||
|
How many times should each thread call vector_add()?
|
||||||
|
|
||||||
|
-v
|
||||||
|
Verbose flag: prints out a little more about what is going on.
|
||||||
|
|
||||||
|
-t
|
||||||
|
Turns on timing and shows how long everything took.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
|
||||||
|
vector_t v[2 * MAX_THREADS];
|
||||||
|
|
||||||
|
// used to ensure print outs make sense
|
||||||
|
pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
fprintf(stderr, "usage: %s [-d (turn on deadlocking behavior)] [-l loops] [-n num_threads] [-t (do timing)] [-v (for verbose)]\n", prog);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only called by one thread (not thread-safe)
|
||||||
|
void vector_init(vector_t *v, int value) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
v->values[i] = value;
|
||||||
|
}
|
||||||
|
Pthread_mutex_init(&v->lock, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only called by one thread (not thread-safe)
|
||||||
|
void vector_print(vector_t *v, char *str) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
printf("%s[%d] %d\n", str, i, v->values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_info(int call_return, int thread_id, int v0, int v1) {
|
||||||
|
if (verbose == 0)
|
||||||
|
return;
|
||||||
|
Pthread_mutex_lock(&print_lock);
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < thread_id; j++) printf(" ");
|
||||||
|
if (call_return)
|
||||||
|
printf("<-add(%d, %d)\n", v0, v1);
|
||||||
|
else
|
||||||
|
printf("->add(%d, %d)\n", v0, v1);
|
||||||
|
Pthread_mutex_unlock(&print_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct __thread_arg_t {
|
||||||
|
int tid;
|
||||||
|
int vector_add_order;
|
||||||
|
int vector_0;
|
||||||
|
int vector_1;
|
||||||
|
} thread_arg_t;
|
||||||
|
|
||||||
|
void *worker(void *arg) {
|
||||||
|
thread_arg_t *args = (thread_arg_t *) arg;
|
||||||
|
int i, v0, v1;
|
||||||
|
for (i = 0; i < loops; i++) {
|
||||||
|
if (args->vector_add_order == 0) {
|
||||||
|
v0 = args->vector_0;
|
||||||
|
v1 = args->vector_1;
|
||||||
|
} else {
|
||||||
|
v0 = args->vector_1;
|
||||||
|
v1 = args->vector_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_info(0, args->tid, v0, v1);
|
||||||
|
|
||||||
|
vector_add(&v[v0], &v[v1]);
|
||||||
|
|
||||||
|
print_info(1, args->tid, v0, v1);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
opterr = 0;
|
||||||
|
int c;
|
||||||
|
while ((c = getopt (argc, argv, "l:n:vtdp")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'l':
|
||||||
|
loops = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
num_threads = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = 1;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
do_timing = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
cause_deadlock = 1;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
enable_parallelism = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(num_threads < MAX_THREADS);
|
||||||
|
|
||||||
|
pthread_t pid[MAX_THREADS];
|
||||||
|
thread_arg_t args[MAX_THREADS];
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_threads; i++) {
|
||||||
|
args[i].tid = i;
|
||||||
|
if (enable_parallelism == 0) {
|
||||||
|
args[i].vector_0 = 0;
|
||||||
|
args[i].vector_1 = 1;
|
||||||
|
} else {
|
||||||
|
args[i].vector_0 = i * 2;
|
||||||
|
args[i].vector_1 = i * 2 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cause_deadlock && i % 2 == 1)
|
||||||
|
args[i].vector_add_order = 1;
|
||||||
|
else
|
||||||
|
args[i].vector_add_order = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 2 * MAX_THREADS; i++)
|
||||||
|
vector_init(&v[i], i);
|
||||||
|
|
||||||
|
double t1 = Time_GetSeconds();
|
||||||
|
|
||||||
|
for (i = 0; i < num_threads; i++)
|
||||||
|
Pthread_create(&pid[i], NULL, worker, (void *) &args[i]);
|
||||||
|
for (i = 0; i < num_threads; i++)
|
||||||
|
Pthread_join(pid[i], NULL);
|
||||||
|
|
||||||
|
double t2 = Time_GetSeconds();
|
||||||
|
|
||||||
|
fini();
|
||||||
|
if (do_timing) {
|
||||||
|
printf("Time: %.2f seconds\n", t2 - t1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//vector_print(&v[0], "v1");
|
||||||
|
//vector_print(&v[1], "v2");
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef __main_header_h__
|
||||||
|
#define __main_header_h__
|
||||||
|
|
||||||
|
#define MAX_THREADS (100)
|
||||||
|
|
||||||
|
int loops = 1;
|
||||||
|
int verbose = 0;
|
||||||
|
int num_threads = 2;
|
||||||
|
int do_timing = 0;
|
||||||
|
int cause_deadlock = 0;
|
||||||
|
int enable_parallelism = 0;
|
||||||
|
|
||||||
|
#endif // __main_header_h__
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef __MYTHREADS_h__
|
||||||
|
#define __MYTHREADS_h__
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
void *Malloc(size_t size) {
|
||||||
|
void *rc = malloc(size);
|
||||||
|
assert(rc != NULL);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Time_GetSeconds() {
|
||||||
|
struct timeval t;
|
||||||
|
int rc = gettimeofday(&t, NULL);
|
||||||
|
assert(rc == 0);
|
||||||
|
return (double) ((double)t.tv_sec + (double)t.tv_usec / 1e6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pthread_mutex_init(pthread_mutex_t *mutex,
|
||||||
|
const pthread_mutexattr_t *attr) {
|
||||||
|
int rc = pthread_mutex_init(mutex, attr);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Pthread_mutex_lock(pthread_mutex_t *m) {
|
||||||
|
int rc = pthread_mutex_lock(m);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pthread_mutex_unlock(pthread_mutex_t *m) {
|
||||||
|
int rc = pthread_mutex_unlock(m);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
void *(*start_routine)(void*), void *arg) {
|
||||||
|
int rc = pthread_create(thread, attr, start_routine, arg);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pthread_join(pthread_t thread, void **value_ptr) {
|
||||||
|
int rc = pthread_join(thread, value_ptr);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __MYTHREADS_h__
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "mythreads.h"
|
||||||
|
|
||||||
|
#include "main-header.h"
|
||||||
|
#include "vector-header.h"
|
||||||
|
|
||||||
|
// use this to make lock acquisition ATOMIC
|
||||||
|
pthread_mutex_t global = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void vector_add(vector_t *v_dst, vector_t *v_src) {
|
||||||
|
// put GLOBAL lock around all lock acquisition...
|
||||||
|
Pthread_mutex_lock(&global);
|
||||||
|
Pthread_mutex_lock(&v_dst->lock);
|
||||||
|
Pthread_mutex_lock(&v_src->lock);
|
||||||
|
Pthread_mutex_unlock(&global);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
|
||||||
|
}
|
||||||
|
Pthread_mutex_unlock(&v_dst->lock);
|
||||||
|
Pthread_mutex_unlock(&v_src->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fini() {}
|
||||||
|
|
||||||
|
#include "main-common.c"
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "mythreads.h"
|
||||||
|
|
||||||
|
#include "main-header.h"
|
||||||
|
#include "vector-header.h"
|
||||||
|
|
||||||
|
void vector_add(vector_t *v_dst, vector_t *v_src) {
|
||||||
|
Pthread_mutex_lock(&v_dst->lock);
|
||||||
|
Pthread_mutex_lock(&v_src->lock);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
|
||||||
|
}
|
||||||
|
Pthread_mutex_unlock(&v_dst->lock);
|
||||||
|
Pthread_mutex_unlock(&v_src->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fini() {}
|
||||||
|
|
||||||
|
#include "main-common.c"
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "mythreads.h"
|
||||||
|
|
||||||
|
#include "main-header.h"
|
||||||
|
#include "vector-header.h"
|
||||||
|
|
||||||
|
void vector_add(vector_t *v_dst, vector_t *v_src) {
|
||||||
|
if (v_dst < v_src) {
|
||||||
|
Pthread_mutex_lock(&v_dst->lock);
|
||||||
|
Pthread_mutex_lock(&v_src->lock);
|
||||||
|
} else if (v_dst > v_src) {
|
||||||
|
Pthread_mutex_lock(&v_src->lock);
|
||||||
|
Pthread_mutex_lock(&v_dst->lock);
|
||||||
|
} else {
|
||||||
|
// special case: src and dst are the same
|
||||||
|
Pthread_mutex_lock(&v_src->lock);
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
|
||||||
|
}
|
||||||
|
Pthread_mutex_unlock(&v_src->lock);
|
||||||
|
if (v_dst != v_src)
|
||||||
|
Pthread_mutex_unlock(&v_dst->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fini() {}
|
||||||
|
|
||||||
|
|
||||||
|
#include "main-common.c"
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef __vector_header_h__
|
||||||
|
#define __vector_header_h__
|
||||||
|
|
||||||
|
#define VECTOR_SIZE (100)
|
||||||
|
|
||||||
|
typedef struct __vector {
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
int values[VECTOR_SIZE];
|
||||||
|
} vector_t;
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __vector_header_h__
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "mythreads.h"
|
||||||
|
|
||||||
|
#include "main-header.h"
|
||||||
|
#include "vector-header.h"
|
||||||
|
|
||||||
|
// taken from https://en.wikipedia.org/wiki/Fetch-and-add
|
||||||
|
int fetch_and_add(int * variable, int value) {
|
||||||
|
asm volatile("lock; xaddl %%eax, %2;"
|
||||||
|
:"=a" (value)
|
||||||
|
:"a" (value), "m" (*variable)
|
||||||
|
:"memory");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vector_add(vector_t *v_dst, vector_t *v_src) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
fetch_and_add(&v_dst->values[i], v_src->values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fini() {}
|
||||||
|
|
||||||
|
|
||||||
|
#include "main-common.c"
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "mythreads.h"
|
||||||
|
|
||||||
|
#include "main-header.h"
|
||||||
|
#include "vector-header.h"
|
||||||
|
|
||||||
|
int retry = 0;
|
||||||
|
|
||||||
|
void vector_add(vector_t *v_dst, vector_t *v_src) {
|
||||||
|
top:
|
||||||
|
if (pthread_mutex_trylock(&v_dst->lock) != 0) {
|
||||||
|
goto top;
|
||||||
|
}
|
||||||
|
if (pthread_mutex_trylock(&v_src->lock) != 0) {
|
||||||
|
retry++;
|
||||||
|
Pthread_mutex_unlock(&v_dst->lock);
|
||||||
|
goto top;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < VECTOR_SIZE; i++) {
|
||||||
|
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
|
||||||
|
}
|
||||||
|
Pthread_mutex_unlock(&v_dst->lock);
|
||||||
|
Pthread_mutex_unlock(&v_src->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fini() {
|
||||||
|
printf("Retries: %d\n", retry);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main-common.c"
|
||||||
|
|
Loading…
Reference in New Issue