commit deae69d49607fd68023adfdf1c5171ff4915fefa
parent 34ce6801da6310dc1c83e26e14c247b135da8368
Author: Christian Ermann <christianermann@gmail.com>
Date: Wed, 18 Aug 2021 13:25:46 -0500
Load chunk meshes on separate threads
Diffstat:
11 files changed, 426 insertions(+), 123 deletions(-)
diff --git a/include/chunk.h b/include/chunk.h
@@ -4,23 +4,33 @@
#include "mesh.h"
#include "sdf.h"
+#include <pthread.h>
+
#define CHUNK_WIDTH 16
#define CHUNK_VOLUME (CHUNK_WIDTH * CHUNK_WIDTH * CHUNK_WIDTH)
#define CHUNK_PWIDTH (CHUNK_WIDTH + 1)
#define CHUNK_PVOLUME (CHUNK_PWIDTH * CHUNK_PWIDTH * CHUNK_PWIDTH)
+typedef struct UpdateArgs UpdateArgs;
+
typedef struct {
IVec3 origin;
Mesh mesh;
-} Chunk;
-void Chunk_init(Chunk *c, IVec3 origin);
+ UpdateArgs *update_args;
+ pthread_mutex_t mesh_mutex;
+ bool mesh_updated;
+} Chunk;
+void Chunk_init(Chunk *c);
void Chunk_free(Chunk *c);
-void Chunk_updateMesh(Chunk *c, SDF f, float isolevel);
+void Chunk_updateOrigin(Chunk *c, IVec3 origin);
+
+void Chunk_setUpdateArgs(Chunk *c, SDF f, float isolevel);
+void Chunk_updateFunc(void *arg);
-void Chunk_drawMesh(const Chunk *c);
+void Chunk_drawMesh(Chunk *c);
#endif
diff --git a/include/chunk_manager.h b/include/chunk_manager.h
@@ -2,6 +2,7 @@
#define CHUNK_MANAGER_H
#include "chunk.h"
+#include "threadpool.h"
typedef struct {
IVec3 origin;
@@ -14,6 +15,11 @@ typedef struct {
IVec3 *offsets;
Chunk *chunks;
+ bool *is_new_offset;
+ bool *is_old_chunk;
+
+ ThreadPool *pool;
+
} ChunkManager;
ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f,
diff --git a/include/mesh.h b/include/mesh.h
@@ -3,6 +3,8 @@
#include "vec.h"
+#include "glad/glad.h"
+
typedef struct {
unsigned int vertex_capacity;
unsigned int vertex_count;
@@ -12,20 +14,18 @@ typedef struct {
unsigned int index_count;
unsigned int *indices;
- unsigned int vao;
- unsigned int vbo;
- unsigned int ebo;
+ unsigned int draw_index;
+ unsigned int write_index;
+ unsigned int vao[2];
+ unsigned int vbo[2];
+ unsigned int ebo[2];
} Mesh;
-void Mesh_init(Mesh *m);
+void Mesh_init(Mesh *m, unsigned int vertex_count, unsigned int index_count);
void Mesh_free(Mesh *m);
-void Mesh_reserveVertices(Mesh *m, unsigned int vertex_count);
-void Mesh_reserveIndices(Mesh *m, unsigned int index_count);
-
-void Mesh_updateVertexBuffer(const Mesh *m);
-void Mesh_updateIndexBuffer(const Mesh *m);
+void Mesh_swapBuffers(Mesh *m);
void Mesh_draw(const Mesh *m);
diff --git a/include/threadpool.h b/include/threadpool.h
@@ -0,0 +1,16 @@
+#ifndef THREADPOOL_H
+#define THREADPOOL_H
+
+// https://nachtimwald.com/2019/04/12/thread-pool-in-c/
+
+typedef void (*ThreadFunc)(void *arg);
+
+typedef struct ThreadPool ThreadPool;
+
+ThreadPool *ThreadPool_make(unsigned int thread_count);
+void ThreadPool_free(ThreadPool *pool);
+
+void ThreadPool_addWork(ThreadPool *pool, ThreadFunc func, void *arg);
+void ThreadPool_wait(ThreadPool *pool);
+
+#endif
diff --git a/shaders/basic.fs b/shaders/basic.fs
@@ -19,7 +19,7 @@ void main()
vec3 object_color = mix(vec3(1.0, 0.8, 0.6), vec3(0.7, 0.7, 0.7), slope);
float d = length(pointlight_pos - frag_pos);
- float attenuation = 1.0 / (1.0 + 0.07 * d + 0.017 * d * d);
+ float attenuation = 1.0 / (1.0 + 0.022 * d + 0.0019 * d * d);
vec3 light_dir = normalize(frag_pos - pointlight_pos);
vec3 view_dir = normalize(view_pos - frag_pos);
diff --git a/src/camera.c b/src/camera.c
@@ -22,7 +22,7 @@ void Camera_defaultSettings(Camera* camera)
camera->fovy = 90.0f;
camera->aspect = 1.0f;
camera->near = 0.1f;
- camera->far = 100.0f;
+ camera->far = 200.0f;
camera->speed = 0.1f;
camera->sensitivity = 0.3f;
}
diff --git a/src/chunk.c b/src/chunk.c
@@ -4,20 +4,42 @@
#include <stdlib.h>
-void Chunk_init(Chunk *c, IVec3 origin)
+struct UpdateArgs {
+ Chunk *chunk;
+ SDF f;
+ float isolevel;
+};
+
+static UpdateArgs *UpdateArgs_make(Chunk *c)
{
- c->origin[0] = origin[0];
- c->origin[1] = origin[1];
- c->origin[2] = origin[2];
+ UpdateArgs *args = malloc(sizeof *args);
+ args->chunk = c;
+ args->f = NULL;
+ args->isolevel = 0.0f;
+ return args;
+}
+
+static void UpdateArgs_free(UpdateArgs *args)
+{
+ if (args)
+ {
+ free(args);
+ }
+}
- Mesh_init(&c->mesh);
- Mesh_reserveVertices(&c->mesh, CHUNK_PVOLUME * 3);
- Mesh_reserveIndices(&c->mesh, CHUNK_VOLUME * 15);
+void Chunk_init(Chunk *c)
+{
+ Mesh_init(&c->mesh, CHUNK_PVOLUME * 3, CHUNK_VOLUME * 15);
+
+ c->update_args = UpdateArgs_make(c);
+ pthread_mutex_init(&c->mesh_mutex, NULL);
+ c->mesh_updated = false;
}
void Chunk_free(Chunk *c)
{
Mesh_free(&c->mesh);
+ UpdateArgs_free(c->update_args);
}
static void worldOrigin(const IVec3 chunk_origin, Vec3 world_origin)
@@ -116,19 +138,45 @@ static void Chunk_updateMeshData(Chunk *c, SDF f, float isolevel)
}
}
-static void Chunk_updateMeshBuffers(const Chunk *c)
+void Chunk_setUpdateArgs(Chunk *c, SDF f, float isolevel)
{
- Mesh_updateVertexBuffer(&c->mesh);
- Mesh_updateIndexBuffer(&c->mesh);
+ c->update_args->chunk = c;
+ c->update_args->f = f;
+ c->update_args->isolevel = isolevel;
}
-void Chunk_updateMesh(Chunk *c, SDF f, float isolevel)
+void Chunk_updateFunc(void *arg)
{
- Chunk_updateMeshData(c, f, isolevel);
- Chunk_updateMeshBuffers(c);
+ UpdateArgs *args = (UpdateArgs*)arg;
+
+ pthread_mutex_lock(&args->chunk->mesh_mutex);
+
+ Chunk_updateMeshData(args->chunk, args->f, args->isolevel);
+
+ args->chunk->mesh_updated = true;
+
+ pthread_mutex_unlock(&args->chunk->mesh_mutex);
}
-void Chunk_drawMesh(const Chunk *c)
+void Chunk_updateOrigin(Chunk *c, IVec3 origin)
{
+ pthread_mutex_lock(&c->mesh_mutex);
+ c->origin[0] = origin[0];
+ c->origin[1] = origin[1];
+ c->origin[2] = origin[2];
+ pthread_mutex_unlock(&c->mesh_mutex);
+}
+
+void Chunk_drawMesh(Chunk *c)
+{
+ if (pthread_mutex_trylock(&c->mesh_mutex) == 0)
+ {
+ if (c->mesh_updated)
+ {
+ Mesh_swapBuffers(&c->mesh);
+ c->mesh_updated = false;
+ }
+ pthread_mutex_unlock(&c->mesh_mutex);
+ }
Mesh_draw(&c->mesh);
}
diff --git a/src/chunk_manager.c b/src/chunk_manager.c
@@ -40,65 +40,84 @@ static void ChunkManager_createChunks(ChunkManager *cm)
cm->chunks = malloc(sizeof *cm->chunks * cm->chunk_count);
for (int i = 0; i < cm->chunk_count; i++)
{
- Chunk_init(&cm->chunks[i], cm->offsets[i]);
- Chunk_updateMesh(&cm->chunks[i], cm->f, cm->isolevel);
+ Chunk_init(&cm->chunks[i]);
+
+ IVec3 chunk_origin = {
+ cm->origin[0] + cm->offsets[i][0],
+ cm->origin[1] + cm->offsets[i][1],
+ cm->origin[2] + cm->offsets[i][2]
+ };
+ Chunk_updateOrigin(&cm->chunks[i], chunk_origin);
+ Chunk_setUpdateArgs(&cm->chunks[i], cm->f, cm->isolevel);
+ ThreadPool_addWork(cm->pool, Chunk_updateFunc,
+ cm->chunks[i].update_args);
}
}
-static void ChunkManager_updateChunks(ChunkManager *cm)
+static void ChunkManager_resetFlags(ChunkManager *cm)
{
- // is_new indicates whether an offset is new
- bool *is_new = malloc(sizeof *is_new * cm->chunk_count);
- // is_old indicates whether a chunk is old
- bool *is_old = malloc(sizeof *is_old * cm->chunk_count);
for (int i = 0; i < cm->chunk_count; i++)
{
- is_new[i] = true;
- is_old[i] = true;
+ cm->is_new_offset[i] = true;
+ cm->is_old_chunk[i] = true;
}
+}
- // Determine which offsets are new and which chunks are old
- IVec3 *new_origins = malloc(sizeof *new_origins * cm->chunk_count);
+static void ChunkManager_updateFlags(ChunkManager *cm)
+{
for (int i = 0; i < cm->chunk_count; i++)
{
- new_origins[i][0] = cm->origin[0] + cm->offsets[i][0];
- new_origins[i][1] = cm->origin[1] + cm->offsets[i][1];
- new_origins[i][2] = cm->origin[2] + cm->offsets[i][2];
+ IVec3 chunk_origin = {
+ cm->origin[0] + cm->offsets[i][0],
+ cm->origin[1] + cm->offsets[i][1],
+ cm->origin[2] + cm->offsets[i][2]
+ };
for (int j = 0; j < cm->chunk_count; j++)
{
- if (IVec3_equal(new_origins[i], cm->chunks[j].origin) != 0)
+ if (IVec3_equal(chunk_origin, cm->chunks[j].origin))
{
- is_new[i] = false;
- is_old[j] = false;
+ cm->is_new_offset[i] = false;
+ cm->is_old_chunk[j] = false;
break;
}
}
}
+}
- // Set old chunks to have new origins
+static void ChunkManager_updateChunkMeshes(ChunkManager *cm)
+{
for (int i = 0; i < cm->chunk_count; i++)
{
- if (is_new[i])
+ if (cm->is_new_offset[i])
{
for (int j = 0; j < cm->chunk_count; j++)
{
- if (is_old[j])
+ if (cm->is_old_chunk[j])
{
- cm->chunks[j].origin[0] = new_origins[i][0];
- cm->chunks[j].origin[1] = new_origins[i][1];
- cm->chunks[j].origin[2] = new_origins[i][2];
- Chunk_updateMesh(&cm->chunks[j], cm->f, cm->isolevel);
- is_new[i] = false;
- is_old[j] = false;
+ IVec3 chunk_origin = {
+ cm->origin[0] + cm->offsets[i][0],
+ cm->origin[1] + cm->offsets[i][1],
+ cm->origin[2] + cm->offsets[i][2]
+ };
+ Chunk_updateOrigin(&cm->chunks[j], chunk_origin);
+ Chunk_setUpdateArgs(&cm->chunks[j], cm->f, cm->isolevel);
+ ThreadPool_addWork(cm->pool, Chunk_updateFunc,
+ cm->chunks[j].update_args);
+
+ cm->is_new_offset[i] = false;
+ cm->is_old_chunk[j] = false;
break;
}
}
}
}
+}
- free(new_origins);
- free(is_old);
- free(is_new);
+static void ChunkManager_updateChunks(ChunkManager *cm)
+{
+ ChunkManager_resetFlags(cm);
+ ChunkManager_updateFlags(cm);
+ ChunkManager_updateChunkMeshes(cm);
}
static void ChunkManager_worldToChunk(const Vec3 src, IVec3 dst)
@@ -113,6 +132,8 @@ ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f,
{
ChunkManager cm;
+ cm.pool = ThreadPool_make(5);
+
ChunkManager_worldToChunk(target, cm.origin);
cm.radius = radius;
@@ -123,6 +144,9 @@ ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f,
ChunkManager_createOffsets(&cm);
ChunkManager_createChunks(&cm);
+ cm.is_new_offset = malloc(sizeof *cm.is_new_offset * cm.chunk_count);
+ cm.is_old_chunk = malloc(sizeof *cm.is_old_chunk * cm.chunk_count);
+
return cm;
}
@@ -130,12 +154,17 @@ void ChunkManager_free(ChunkManager *cm)
{
free(cm->offsets);
+ ThreadPool_free(cm->pool);
+
for (int i = 0; i < cm->chunk_count; i++)
{
Chunk_free(&cm->chunks[i]);
}
free(cm->chunks);
+ free(cm->is_new_offset);
+ free(cm->is_old_chunk);
+
cm->chunk_count = 0;
}
diff --git a/src/main.c b/src/main.c
@@ -82,7 +82,7 @@ int main(int argc, char** argv)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- ChunkManager chunk_manager = ChunkManager_create(camera.position, 2,
+ ChunkManager chunk_manager = ChunkManager_create(camera.position, 3,
perlinSDF, 0.0f);
Shader shader = Shader_create("shaders/basic.vs", "shaders/basic.fs");
diff --git a/src/mesh.c b/src/mesh.c
@@ -4,99 +4,86 @@
#include <stdlib.h>
-void Mesh_init(Mesh *m)
+void Mesh_init(Mesh *m, unsigned int vertex_count, unsigned int index_count)
{
- m->vertex_capacity = 0;
+ m->vertex_capacity = vertex_count;
m->vertex_count = 0;
- m->vertices = NULL;
- m->index_capacity = 0;
+ m->index_capacity = index_count;
m->index_count = 0;
- m->indices = NULL;
- glGenVertexArrays(1, &m->vao);
- glBindVertexArray(m->vao);
+ m->draw_index = 0;
+ m->write_index = 1;
+
+ glGenVertexArrays(2, m->vao);
+ glGenBuffers(2, m->vbo);
+ glGenBuffers(2, m->ebo);
+ for (int i = 0; i < 2; i++)
+ {
+ glBindVertexArray(m->vao[i]);
- glGenBuffers(1, &m->vbo);
- glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, m->vbo[i]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof *m->vertices * vertex_count, NULL,
+ GL_STATIC_DRAW);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (GLvoid*)0);
- glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), 0);
+ glEnableVertexAttribArray(0);
- glGenBuffers(1, &m->ebo);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo[i]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof *m->indices * index_count,
+ NULL, GL_STATIC_DRAW);
- glBindVertexArray(0);
+ glBindVertexArray(0);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m->vbo[m->write_index]);
+ m->vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo[m->write_index]);
+ m->indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
}
void Mesh_free(Mesh *m)
{
- if (m->vertices)
- {
- free(m->vertices);
- m->vertices = NULL;
- }
m->vertex_capacity = 0;
m->vertex_count = 0;
- if (m->indices)
- {
- free(m->indices);
- m->indices = NULL;
- }
m->index_capacity = 0;
m->index_count = 0;
+
+ glBindBuffer(GL_ARRAY_BUFFER, m->vbo[m->write_index]);
+ glUnmapBuffer(GL_ARRAY_BUFFER);
- glDeleteVertexArrays(1, &m->vao);
- glDeleteBuffers(1, &m->vbo);
- glDeleteBuffers(1, &m->ebo);
-}
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo[m->write_index]);
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
-void Mesh_reserveVertices(Mesh *m, unsigned int vertex_count)
-{
- if (vertex_count > m->vertex_capacity)
- {
- if (m->vertices)
- {
- free(m->vertices);
- m->vertices = NULL;
- }
- m->vertices = malloc(sizeof *m->vertices * vertex_count);
- m->vertex_capacity = vertex_count;
- }
+ glDeleteVertexArrays(2, m->vao);
+ glDeleteBuffers(2, m->vbo);
+ glDeleteBuffers(2, m->ebo);
}
-void Mesh_reserveIndices(Mesh *m, unsigned int index_count)
+void Mesh_swapBuffers(Mesh *m)
{
- if (index_count > m->index_capacity)
- {
- if (m->indices)
- {
- free(m->indices);
- m->indices = NULL;
- }
- m->indices = malloc(sizeof *m->indices * index_count);
- m->index_capacity = index_count;
- }
-}
+ glBindBuffer(GL_ARRAY_BUFFER, m->vbo[m->draw_index]);
+ m->vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
-void Mesh_updateVertexBuffer(const Mesh *m)
-{
- glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
- glBufferData(GL_ARRAY_BUFFER, sizeof *m->vertices * m->vertex_count,
- m->vertices, GL_STATIC_DRAW);
-}
+ glBindBuffer(GL_ARRAY_BUFFER, m->vbo[m->write_index]);
+ glUnmapBuffer(GL_ARRAY_BUFFER);
-void Mesh_updateIndexBuffer(const Mesh *m)
-{
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof *m->indices * m->index_count,
- m->indices, GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo[m->draw_index]);
+ m->indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo[m->write_index]);
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+
+ unsigned int temp = m->draw_index;
+ m->draw_index = m->write_index;
+ m->write_index = temp;
}
void Mesh_draw(const Mesh *m)
{
- glBindVertexArray(m->vao);
+ glBindVertexArray(m->vao[m->draw_index]);
glDrawElements(GL_TRIANGLES, m->index_count, GL_UNSIGNED_INT, (GLvoid*)0);
glBindVertexArray(0);
}
diff --git a/src/threadpool.c b/src/threadpool.c
@@ -0,0 +1,207 @@
+#include "threadpool.h"
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef struct ThreadWork ThreadWork;
+
+struct ThreadWork {
+ ThreadFunc func;
+ void *arg;
+ ThreadWork *next;
+};
+
+static ThreadWork *ThreadWork_make(ThreadFunc func, void *arg)
+{
+ ThreadWork *work;
+
+ work = malloc(sizeof *work);
+ work->func = func;
+ work->arg = arg;
+ work->next = NULL;
+
+ return work;
+}
+
+static void ThreadWork_free(ThreadWork *work)
+{
+ if (work)
+ {
+ free(work);
+ }
+}
+
+struct ThreadPool {
+ ThreadWork *work_first;
+ ThreadWork *work_last;
+ pthread_mutex_t work_mutex;
+ pthread_cond_t work_cond;
+ pthread_cond_t active_cond;
+ unsigned int thread_count;
+ unsigned int active_count;
+ bool stop;
+};
+
+void ThreadPool_addWork(ThreadPool *pool, ThreadFunc func, void *arg)
+{
+ ThreadWork *work;
+ work = ThreadWork_make(func, arg);
+
+ pthread_mutex_lock(&pool->work_mutex);
+ if (pool->work_first == NULL)
+ {
+ pool->work_first = work;
+ pool->work_last = pool->work_first;
+ }
+ else
+ {
+ pool->work_last->next = work;
+ pool->work_last = work;
+ }
+
+ pthread_cond_broadcast(&pool->work_cond);
+ pthread_mutex_unlock(&pool->work_mutex);
+}
+
+static ThreadWork *ThreadPool_getWork(ThreadPool *pool)
+{
+ ThreadWork *work;
+
+ work = pool->work_first;
+ if (work == NULL)
+ {
+ return NULL;
+ }
+
+ if (work->next == NULL)
+ {
+ pool->work_first = NULL;
+ pool->work_last = NULL;
+ }
+ else
+ {
+ pool->work_first = work->next;
+ }
+
+ return work;
+}
+
+static void *ThreadPool_worker(void *arg)
+{
+ ThreadPool *pool = arg;
+ ThreadWork *work;
+
+ while (true)
+ {
+ pthread_mutex_lock(&pool->work_mutex);
+
+ while (pool->work_first == NULL && !pool->stop)
+ {
+ pthread_cond_wait(&pool->work_cond, &pool->work_mutex);
+ }
+
+ if (pool->stop)
+ {
+ break;
+ }
+
+ work = ThreadPool_getWork(pool);
+ pool->active_count += 1;
+
+ pthread_mutex_unlock(&pool->work_mutex);
+
+ if (work != NULL)
+ {
+ work->func(work->arg);
+ ThreadWork_free(work);
+ }
+
+ pthread_mutex_lock(&pool->work_mutex);
+
+ pool->active_count -= 1;
+ if (!pool->stop && pool->active_count == 0 && pool->work_first == NULL)
+ {
+ pthread_cond_signal(&pool->active_cond);
+ }
+
+ pthread_mutex_unlock(&pool->work_mutex);
+ }
+
+ pool->thread_count -= 1;
+ pthread_cond_signal(&pool->active_cond);
+ pthread_mutex_unlock(&pool->work_mutex);
+
+ return NULL;
+}
+
+ThreadPool *ThreadPool_make(unsigned int thread_count)
+{
+ ThreadPool *pool;
+ pool = malloc(sizeof *pool);
+ pool->thread_count = thread_count;
+
+ pthread_mutex_init(&pool->work_mutex, NULL);
+ pthread_cond_init(&pool->work_cond, NULL);
+ pthread_cond_init(&pool->active_cond, NULL);
+
+ pool->work_first = NULL;
+ pool->work_last = NULL;
+
+ pthread_t thread;
+ for (unsigned int i = 0; i < thread_count; i++)
+ {
+ pthread_create(&thread, NULL, ThreadPool_worker, pool);
+ pthread_detach(thread);
+ }
+
+ return pool;
+}
+
+void ThreadPool_wait(ThreadPool *pool)
+{
+ pthread_mutex_lock(&pool->work_mutex);
+ while (true)
+ {
+ if ((!pool->stop && pool->active_count != 0)
+ || (pool->stop && pool->thread_count != 0))
+ {
+ pthread_cond_wait(&pool->active_cond, &pool->work_mutex);
+ }
+ else
+ {
+ break;
+ }
+ }
+ pthread_mutex_unlock(&pool->work_mutex);
+}
+
+void ThreadPool_free(ThreadPool *pool)
+{
+ ThreadWork *work1;
+ ThreadWork *work2;
+
+ if (pool == NULL)
+ {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->work_mutex);
+ work1 = pool->work_first;
+ while (work1 != NULL)
+ {
+ work2 = work1->next;
+ ThreadWork_free(work1);
+ work1 = work2;
+ }
+
+ pool->stop = true;
+ pthread_cond_broadcast(&pool->work_cond);
+ pthread_mutex_unlock(&pool->work_mutex);
+
+ ThreadPool_wait(pool);
+
+ pthread_mutex_destroy(&pool->work_mutex);
+ pthread_cond_destroy(&pool->work_cond);
+ pthread_cond_destroy(&pool->active_cond);
+}