Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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)

AltStyle によって変換されたページ (->オリジナル) /