Skip to content

Commit 4b6ba12

Browse files
author
Anton Yarkov
committed
Moving forward with pthreads.
1 parent a995bbb commit 4b6ba12

15 files changed

+1016
-22
lines changed

multithreading/01 - Fork Processes And End Differently.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "Common.h"
77

88
// Compilation:
9-
// gcc -std=gnu99 "01 - Fork Processes And End Differently.c" -o forkEndProcesses
9+
// gcc -std=gnu99 ErrorHandling.c LogF.c "01 - Fork Processes And End Differently.c" -o forkEndProcesses
1010

1111
// Task:
1212
// Use fork to create a child processes, which stop working with different reasons.

multithreading/04 - Process attributes.c

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,98 @@
66
#include "Common.h"
77

88
// Compilation:
9-
// gcc -std=gnu99 "04 - Process attributes.c" -o processAttrs
9+
// gcc -std=gnu99 ErrorHandling.c LogF.c "04 - Process attributes.c" -o processAttrs
1010

1111
// Task:
1212
// Work with process attributes.
1313

14+
static void printLimitValue(rlim_t limit)
15+
{
16+
if (limit == RLIM_INFINITY)
17+
printf("RLIM_INFINITY");
18+
else
19+
printf("%llu", (unsigned long long)limit);
20+
}
21+
22+
static bool readLimits(int resource, const char *name)
23+
{
24+
struct rlimit r;
25+
ec_neg1( getrlimit(resource, &r) )
26+
printf("%s: ", name);
27+
printf(" current: ");
28+
printLimitValue(r.rlim_cur);
29+
printf(" max: ");
30+
printLimitValue(r.rlim_max);
31+
printf("\n");
32+
33+
return true;
34+
35+
EC_CLEANUP_BGN
36+
return false;
37+
EC_CLEANUP_END
38+
}
39+
40+
static bool readAndChangeProcessLimits()
41+
{
42+
printf("6 CHILD: Changing process limits...\n");
43+
struct rlimit r;
44+
int fd;
45+
char buf[500] = { 0 };
46+
if (sizeof(rlim_t) > sizeof(long long))
47+
printf("Warning: rlim_t > long long, results might be wrong!\n");
48+
49+
ec_false( readLimits(RLIMIT_CORE, "6 CHILD: RLIMIT_CORE") )
50+
ec_false( readLimits(RLIMIT_CPU, "6 CHILD: RLIMIT_CPU") )
51+
ec_false( readLimits(RLIMIT_DATA, "6 CHILD: RLIMIT_DATA") )
52+
ec_false( readLimits(RLIMIT_FSIZE, "6 CHILD: RLIMIT_FSIZE") )
53+
ec_false( readLimits(RLIMIT_NOFILE, "6 CHILD: RLIMIT_NOFILE") )
54+
ec_false( readLimits(RLIMIT_STACK, "6 CHILD: RLIMIT_STACK") )
55+
56+
ec_neg1( getrlimit(RLIMIT_FSIZE, &r) )
57+
58+
r.rlim_cur = 500;
59+
60+
ec_neg1( setrlimit(RLIMIT_FSIZE, &r) )
61+
ec_neg1( fd = open("tmp", O_WRONLY | O_CREAT | O_TRUNC, PERM_FILE) )
62+
ec_neg1( write(fd, buf, sizeof(buf)) )
63+
ec_neg1( write(fd, buf, sizeof(buf)) )
64+
printf("6 CHILD: 2 buffers has been written!\n");
65+
66+
return true;
67+
68+
EC_CLEANUP_BGN
69+
return false;
70+
EC_CLEANUP_END
71+
}
72+
73+
static bool showProcessResourcesStat()
74+
{
75+
struct rusage r;
76+
ec_neg1( getrusage(RUSAGE_SELF, &r) )
77+
printf("7 CHILD: user CPU time used %d \n", r.ru_utime);
78+
printf("7 CHILD: system CPU time used %d \n", r.ru_stime);
79+
printf("7 CHILD: max resident set size %d \n", r.ru_maxrss);
80+
printf("7 CHILD: integral shared size %d \n", r.ru_ixrss);
81+
printf("7 CHILD: integral unshared data (data segment) size %d \n", r.ru_idrss);
82+
printf("7 CHILD: integral unshared stack size %d \n", r.ru_isrss);
83+
printf("7 CHILD: page reclaims (soft page faults) %d \n", r.ru_minflt);
84+
printf("7 CHILD: hard page faults %d \n", r.ru_majflt);
85+
printf("7 CHILD: swaps / page file touches %d \n", r.ru_nswap);
86+
printf("7 CHILD: block input operations %d \n", r.ru_inblock);
87+
printf("7 CHILD: block output operations %d \n", r.ru_oublock);
88+
printf("7 CHILD: IPC messages sent %d \n", r.ru_msgsnd);
89+
printf("7 CHILD: IPC messages received %d \n", r.ru_msgrcv);
90+
printf("7 CHILD: signals received %d \n", r.ru_nsignals);
91+
printf("7 CHILD: voluntary context switches by process %d \n", r.ru_nvcsw);
92+
printf("7 CHILD: involuntary context switches by core %d \n", r.ru_nivcsw);
93+
94+
return true;
95+
96+
EC_CLEANUP_BGN
97+
return false;
98+
EC_CLEANUP_END
99+
}
100+
14101
int main(int argc, char *argv[])
15102
{
16103
printf("Enter to main!\n");
@@ -39,10 +126,14 @@ int main(int argc, char *argv[])
39126
printf("5 CHILD: Let's decrease Priority Level for N=5: %d (N-20)\n", nice(5));
40127
printf("5 CHILD: Let's increase Priority Level for N=-5: %d (N-20)\n", nice(-5));
41128

42-
printf("6 CHILD: Wait 4 seconds...\n");
129+
readAndChangeProcessLimits();
130+
131+
showProcessResourcesStat();
132+
133+
printf("8 CHILD: Wait 4 seconds...\n");
43134
sleep(4);
44-
printf("7 CHILD: Exit!\n");
45-
exit(EXIT_FAILURE);
135+
printf("9 CHILD: Exit!\n");
136+
exit(EXIT_SUCCESS);
46137
default:
47138
printf("1 PARENT: Parent process %d!\n", getpid());
48139
printf("2 PARENT: Child PID %d\n", pid);
@@ -57,4 +148,8 @@ int main(int argc, char *argv[])
57148

58149
printf("Exit process!\n");
59150
exit(EXIT_SUCCESS);
151+
152+
EC_CLEANUP_BGN
153+
exit(EXIT_FAILURE);
154+
EC_CLEANUP_END
60155
}

multithreading/05 - PThreads.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "05 - PThreads.c" -o pthreads
77

88
// Task:
9-
// Simple example of a counter working in 2 threads.
9+
// Simple example of a counter working in 2 threads. Example shows that sequence of ++ calls is not deterministic.
1010

1111
static long x = 0;
1212

1313
static void* thread_func(void* arg)
1414
{
1515
while (true)
1616
{
17-
printf("Thread 2, counter value %ld\n", ++x);
17+
printf("Thread 2, counter value %ld\n", ++x); // !!! WARNING!!! 2 Threads use the same variable. But increment operation is not atomic.
1818
sleep(1);
1919
}
2020
}
@@ -23,11 +23,15 @@ int main(void)
2323
{
2424
pthread_t tid;
2525

26-
ec_rv(pthread_create(&tid, NULL, thread_func, NULL))
26+
// Use macros ec_rv to determine returned error (if happened), because pthread doesn't use errno variable to set error info.
27+
ec_rv(pthread_create(&tid,
28+
NULL, // Use attributes by default. To create attribute, call pthread_attr_setscope or pthread_attr_setstaoksize
29+
thread_func,
30+
NULL))
2731

2832
while (x < 10)
2933
{
30-
printf("Thread 1, counter value %ld\n", ++x);
34+
printf("Thread 1, counter value %ld\n", ++x); // !!! WARNING!!! 2 Threads use the same variable. But increment operation is not atomic.
3135
sleep(2);
3236
}
3337
return EXIT_SUCCESS;

multithreading/06 - PThreads Joinable BAD.c renamed to multithreading/06 - PThreads Joinable.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "06 - PThreads Joinable BAD.c" -o joinable_threads
77

88
// Task:
9-
// Simple example of an application working with 2 threads, where parent waits results from child.
9+
// Simple example of an application working with 2 threads, where parent WAITS for a results from child.
1010
// Thread 2 stops calculation if common value already more than limit.
1111

12-
// WARNING! Code contains a problem marked with comment.
13-
1412
static long x = 0;
1513

1614
static void* thread_func(void* arg)

multithreading/07 - PThreads Joinable with Sync.c renamed to multithreading/07 - PThreads Joinable Mutexed.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
#include "Defs.h"
44

55
// Compilation:
6-
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "07 - PThreads Joinable with Sync.c" -o joinable_threads_synced
6+
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "07 - PThreads Joinable Mutexed.c" -o joinable_threads_synced
77

88
// Task:
9-
// Simple example of an application working with 2 threads, where parent waits results from child.
9+
// Simple example of an application working with 2 threads, where parent WAITS for a results from child.
1010
// Thread 2 stops calculation if common value already more than limit.
1111

1212
// Access to variable is controlled with mutex.
1313

14+
// Can be static, extern and even auto. In case of auto need to initialize with function pthread_mutex_init, not by a constant (for thread safety).
1415
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
16+
1517
static long x = 0;
1618

1719
static void* thread_func(void* arg)
@@ -34,6 +36,8 @@ static void* thread_func(void* arg)
3436
return (void *)x;
3537

3638
EC_CLEANUP_BGN
39+
// To avoid dead lock: Thread 2 faulted on mutex_unlock, this Thread 2 is locked forever.
40+
(void)pthread_mutex_unlock(&mtx);
3741
EC_FLUSH("thread_func")
3842
return NULL;
3943
EC_CLEANUP_END

multithreading/08 - PThreads Joinable with Sync improved.c renamed to multithreading/08 - PThreads Joinable Mutexed improved.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include "Defs.h"
44

55
// Compilation:
6-
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "08 - PThreads Joinable with Sync improved.c" -o joinable_threads_synced1
6+
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "08 - PThreads Joinable Mutexed improved.c" -o joinable_threads_synced1
77

88
// Task:
99
// Simple example of an application working with 2 threads, where parent waits results from child.
@@ -17,7 +17,7 @@ static long get_and_incr_x(long incr)
1717
// And C++ may even use lazy initialization for mutex.
1818
static long x = 0;
1919

20-
// rtn and incr variables are on stack - i.e. local
20+
// rtn and incr variables are on stack - i.e. local, so we need to use mutex for access to it.
2121
long rtn;
2222

2323
ec_rv( pthread_mutex_lock(&mtx) )
@@ -36,7 +36,7 @@ static void* thread_func(void* arg)
3636
long val = 0;
3737
while (val < (long)arg)
3838
{
39-
val = get_and_incr_x(1);
39+
val = get_and_incr_x(1); // Use local variable, because it is thread safe.
4040
printf("Thread 2, counter value %ld\n", val);
4141
sleep(1);
4242
}
@@ -56,7 +56,7 @@ int main(void)
5656
long val = 0;
5757
while (val < 10)
5858
{
59-
val = get_and_incr_x(1);
59+
val = get_and_incr_x(1); // Use local variable, because it is thread safe.
6060
printf("Thread 1, counter value %ld\n", val);
6161
sleep(2);
6262
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <pthread.h>
2+
3+
#include "Defs.h"
4+
5+
// Compilation:
6+
// gcc -std=gnu99 -pthread ErrorHandling.c LogF.c "09 - PThreads Mutexed with Conditional Variables.c" -o threads_and_queue
7+
8+
// Task:
9+
// Application working with 2 threads - one is putting data into queue, another one gets data from the queue.
10+
// a. Threads to be stopped forcibly with pthread_cancel.
11+
// b. Queue data do not contain mark of END of data flow for normal cases (we use (a) for both
12+
// normal and abnormal termination). This is TO BE DONE.
13+
// c. We may use Signals for normal and abnormal termination. But it has a problems:
14+
15+
// Access to queue is controlled with mutex and conditional variable.
16+
17+
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
18+
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
19+
20+
struct node
21+
{
22+
int n_number;
23+
struct node *n_next;
24+
} *head = NULL;
25+
26+
static void cleanup_handler(void *arg)
27+
{
28+
free(arg);
29+
(void)pthread_mutex_unlock(&mtx);
30+
}
31+
32+
static void* thread_func(void* arg)
33+
{
34+
struct node *p = NULL; // Should be initialized at least with NULL, since we use free.
35+
36+
pthread_cleanup_push(cleanup_handler, p); // Set up cleanup handler for case of forced thread termination
37+
// to avoid dead lock on locked mutex.
38+
while (true)
39+
{
40+
ec_rv( pthread_mutex_lock(&mtx) )
41+
42+
while (head == NULL) // Loop is used because pthread_cond_wait
43+
// can be interrupted by signal (SIGINT, etc.)
44+
// and we need to get back to conditional wait.
45+
ec_rv( pthread_cond_wait(&cond, &mtx) ) // Conditional variable UNLOCKS mutex while waiting,
46+
// but locks it back when condition happened.
47+
48+
p = head;
49+
head = head->n_next;
50+
int number = p->n_number;
51+
free(p); // Free called here, because next point - printf(), might be a
52+
// Point of Interruption (free() or malloc() cannot),
53+
// and we need to make sure memory is freed.
54+
printf("Got number %d from the queue\n", number);
55+
ec_rv( pthread_mutex_unlock(&mtx) )
56+
}
57+
58+
pthread_cleanup_pop(cleanup_handler, p);
59+
return (void *)true;
60+
61+
EC_CLEANUP_BGN
62+
(void)pthread_mutex_unlock(&mtx);
63+
EC_FLUSH("thread_func")
64+
return (void *)false;
65+
EC_CLEANUP_END
66+
}
67+
68+
int main(void)
69+
{
70+
pthread_t tid;
71+
int i;
72+
struct node *p;
73+
74+
ec_rv( pthread_create(&tid, NULL, thread_func, NULL) )
75+
76+
for (i = 0; i < 10; i++)
77+
{
78+
// Imitation of data receive here...
79+
ec_null( p = malloc(sizeof(struct node)) )
80+
p->n_number = i;
81+
82+
// Put into queue.... Using mutex protection.
83+
ec_rv( pthread_mutex_lock(&mtx) )
84+
p->n_next = head;
85+
head = p;
86+
ec_rv( pthread_cond_signal(&cond) ) // Signal about data in the queue.
87+
ec_rv( pthread_mutex_unlock(&mtx) )
88+
sleep(1);
89+
}
90+
91+
ec_rv( pthread_cancel(tid) )
92+
ec_rv( pthread_join(tid, NULL) )
93+
printf("End of transmission\n");
94+
95+
return EXIT_SUCCESS;
96+
97+
EC_CLEANUP_BGN
98+
return EXIT_FAILURE;
99+
EC_CLEANUP_END
100+
}

0 commit comments

Comments
 (0)