Merge branch 'master' into gonzus/fix-function-pointer-casts

This commit is contained in:
Gonzalo Diethelm 2020-07-21 10:51:40 +02:00 committed by GitHub
commit f8e457b3b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 77 additions and 49 deletions

4
.circleci/Dockerfile Normal file
View File

@ -0,0 +1,4 @@
FROM ubuntu
RUN apt-get update && \
apt-get -y install gcc valgrind time python

4
.circleci/README.md Normal file
View File

@ -0,0 +1,4 @@
Build and push container
docker build -t pithikos/test-c-thread-pool .
docker push pithikos/test-c-thread-pool

19
.circleci/config.yml Normal file
View File

@ -0,0 +1,19 @@
version: 2.1
jobs:
test:
docker:
- image: pithikos/test-c-thread-pool
steps:
- checkout
- run:
name: Test
command: |
cd tests/
./normal_compile.sh
./optimized_compile.sh
workflows:
test:
jobs:
- test

View File

@ -1,4 +1,4 @@
![Build status](http://178.62.170.124:3000/pithikos/c-thread-pool/badge/?branch=master) [![CircleCI](https://circleci.com/gh/Pithikos/C-Thread-Pool.svg?style=svg)](https://circleci.com/gh/Pithikos/C-Thread-Pool)
# C Thread Pool # C Thread Pool

View File

@ -1,47 +1,47 @@
## High level ## High level
Description: Library providing a threading pool where you can add work on the fly. The number Description: Library providing a threading pool where you can add work on the fly. The number
of threads in the pool is adjustable when creating the pool. In most cases of threads in the pool is adjustable when creating the pool. In most cases
this should equal the number of threads supported by your cpu. this should equal the number of threads supported by your cpu.
For an example on how to use the threadpool, check the main.c file or just read For an example on how to use the threadpool, check the main.c file or just read
the documentation found in the README.md file. the documentation found in the README.md file.
In this header file a detailed overview of the functions and the threadpool's logical In this header file a detailed overview of the functions and the threadpool's logical
scheme is presented in case you wish to tweak or alter something. scheme is presented in case you wish to tweak or alter something.
_______________________________________________________ _______________________________________________________
/ \ / \
| JOB QUEUE | job1 | job2 | job3 | job4 | .. | | JOB QUEUE | job1 | job2 | job3 | job4 | .. |
| | | |
| threadpool | thread1 | thread2 | .. | | threadpool | thread1 | thread2 | .. |
\_______________________________________________________/ \_______________________________________________________/
Description: Jobs are added to the job queue. Once a thread in the pool Description: Jobs are added to the job queue. Once a thread in the pool
is idle, it is assigned with the first job from the queue(and is idle, it is assigned the first job from the queue (and that job is
erased from the queue). It's each thread's job to read from erased from the queue). It is each thread's job to read from
the queue serially(using lock) and executing each job the queue serially (using lock) and executing each job
until the queue is empty. until the queue is empty.
Scheme: Scheme:
thpool______ jobqueue____ ______ thpool______ jobqueue____ ______
| | | | .----------->|_job0_| Newly added job | | | | .----------->|_job0_| Newly added job
| | | rear ----------' |_job1_| | | | rear ----------' |_job1_|
| jobqueue----------------->| | |_job2_| | jobqueue----------------->| | |_job2_|
| | | front ----------. |__..__| | | | front ----------. |__..__|
|___________| |___________| '----------->|_jobn_| Job for thread to take |___________| |___________| '----------->|_jobn_| Job for thread to take
job0________ job0________
| | | |
| function----> | function---->
| | | |
| arg-------> | arg------->
| | job1________ | | job1________
| next-------------->| | | next-------------->| |
|___________| | |.. |___________| | |..

View File

@ -1,7 +1,7 @@
### Why isn't `pthread_exit()` used to exit a thread?
###Why isn't pthread_exit() used to exit a thread? `thread_do` used to use `pthread_exit()`. However that resulted in
`thread_do` used to use pthread_exit(). However that resulted in hard times of testing for memory leaks. The reason is that on `pthread_exit()`
hard times of testing for memory leaks. The reason is that on pthread_exit()
not all memory is freed bt pthread (probably for future threads or false not all memory is freed bt pthread (probably for future threads or false
belief that the application is terminating). For these reasons a simple return belief that the application is terminating). For these reasons a simple return
is used. is used.
@ -9,27 +9,28 @@ is used.
Interestingly using `pthread_exit()` results in much more memory being allocated. Interestingly using `pthread_exit()` results in much more memory being allocated.
###Why do you use sleep() after calling thpool_destroy()? ### Why do you use `sleep()` after calling `thpool_destroy()`?
This is needed only in the tests. The reason is that if you call thpool_destroy
and then exit immedietely, maybe the program will exit before all the threads This is needed only in the tests. The reason is that if you call `thpool_destroy()`
and then exit immediately, maybe the program will exit before all the threads
had the time to deallocate. In that way it is impossible to check for memory had the time to deallocate. In that way it is impossible to check for memory
leaks. leaks.
In production you don't have to worry about this since if you call exit, In production you don't have to worry about this since if you call `exit()`,
immedietely after you destroyied the pool, the threads will be freed immediately after you destroyed the pool, the threads will be freed
anyway by the OS. If you eitherway destroy the pool in the middle of your anyway by the OS. If you anyway destroy the pool in the middle of your
program it doesn't matter again since the program will not exit immediately program it doesn't matter again since the program will not exit immediately
and thus threads will have more than enough time to terminate. and thus threads will have more than enough time to terminate.
### Why does `wait()` use all my CPU?
###Why does wait() use all my CPU? Notice: As of 11-Dec-2015 `wait()` doesn't use polling anymore. Instead a conditional variable is being used so in theory there should not be any CPU overhead.
Notice: As of 11-Dec-2015 wait() doesn't use polling anymore. Instead a conditional variable is being used so in theory there should not be any CPU overhead.
Normally `wait()` will spike CPU usage to full when called. This is normal as long as it doesn't last for more than 1 second. The reason this happens is that `wait()` goes through various phases of polling (what is called smart polling). Normally `wait()` will spike CPU usage to full when called. This is normal as long as it doesn't last for more than 1 second. The reason this happens is that `wait()` goes through various phases of polling (what is called smart polling).
* Initially there is no interval between polling and hence the 100% use of your CPU. * Initially there is no interval between polling and hence the 100% use of your CPU.
* After that the polling interval grows exponentially. * After that the polling interval grows exponentially.
* Finally after x seconds, if there is still work, polling falls back to a very big interval. * Finally after x seconds, if there is still work, polling falls back to a very big interval.
The reason `wait()` works in this way, is that the function is mostly used when someone wants to wait for some calculation to finish. So if the calculation is assumed to take a long time then we don't want to poll too often. Still we want to poll fast in case the calculation is a simple one. To solve these two problems, this seemingly awkward behaviour is present. The reason `wait()` works in this way, is that the function is mostly used when someone wants to wait for some calculation to finish. So if the calculation is assumed to take a long time then we don't want to poll too often. Still we want to poll fast in case the calculation is a simple one. To solve these two problems, this seemingly awkward behaviour is present.

View File

@ -13,16 +13,11 @@
#include <stdio.h> #include <stdio.h>
#include <pthread.h> #include <pthread.h>
#include <stdint.h>
#include "thpool.h" #include "thpool.h"
void task(void *arg){
void task1(){ printf("Thread #%u working on %d\n", (int)pthread_self(), (int) arg);
printf("Thread #%u working on task1\n", (int)pthread_self());
}
void task2(){
printf("Thread #%u working on task2\n", (int)pthread_self());
} }
@ -33,11 +28,11 @@ int main(){
puts("Adding 40 tasks to threadpool"); puts("Adding 40 tasks to threadpool");
int i; int i;
for (i=0; i<20; i++){ for (i=0; i<40; i++){
thpool_add_work(thpool, task1, NULL); thpool_add_work(thpool, task, (void*)(uintptr_t)i);
thpool_add_work(thpool, task2, NULL);
}; };
thpool_wait(thpool);
puts("Killing threadpool"); puts("Killing threadpool");
thpool_destroy(thpool); thpool_destroy(thpool);

View File

@ -62,7 +62,12 @@ test_thread_free 8
test_thread_free 1 test_thread_free 1
test_thread_free 20 test_thread_free 20
test_thread_free_multi 4 20 test_thread_free_multi 4 20
test_thread_free_multi 3 1000
test_thread_free_multi 100 100 # test_thread_free_multi 3 1000 # Takes way too long
test_thread_free_multi 3 200
# test_thread_free_multi 100 100 # Takes way too long
test_thread_free_multi 100 20
echo "No memory leaks" echo "No memory leaks"