Table of Contents
ex_parallel_team.c
N_PARALLEL_TEAM: many parallel loops on threads that stay alive Runs many loops on one team and checks every item was written exactly once each time; that the slicing is the same on every run (each slice records which participant ran it); that a nested loop on the same team runs inline instead of deadlocking; that two threads sharing a team both finish right; and that a one-participant team and a NULL team run on the caller. ```c
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
#include "nilorea/n_common.h" #include "nilorea/n_log.h" #include "nilorea/n_thread_pool.h"
static int failures = 0;
static void expect_true(const char* what, int ok) { if (!ok) { n_log(LOG_ERR, "FAIL: %s", what); failures++; } }
static void bump(size_t begin, size_t end, void* user) { int* items = (int*)user; size_t i = 0;
for (i = begin; i < end; i++) items[i]++;
}
static int all_equal(const int* items, size_t count, int value) { size_t i = 0;
for (i = 0; i < count; i++)
if (items[i] != value) return 0;
return 1;
}
typedef struct { N_PARALLEL_TEAM* team; int* items; size_t count; } NESTED;
static void nested(size_t begin, size_t end, void* user) { NESTED* n = (NESTED*)user; size_t i = 0;
for (i = begin; i < end; i++)
n_parallel_team_run(n->team, 0, n->count, 1, bump, n->items + i * n->count);
}
typedef struct { N_PARALLEL_TEAM* team; int* items; } SHARER;
static void* sharer(void* arg) { SHARER* s = (SHARER*)arg; int i = 0;
for (i = 0; i < 100; i++) n_parallel_team_run(s->team, 0, 100000, 100, bump, s->items);
return NULL;
}
int main(void) { N_PARALLEL_TEAM* team = NULL; N_PARALLEL_TEAM* solo = NULL; int* a = NULL; int* b = NULL; int* inner = NULL; int i = 0, first = 0, same = 1; pthread_t other; SHARER share; NESTED nest;
set_log_level(LOG_NOTICE);
a = calloc(100000, sizeof(int));
b = calloc(100000, sizeof(int));
inner = calloc(16 * 64, sizeof(int));
team = n_parallel_team_new(4);
expect_true("a team of four", team && n_parallel_team_size(team) == 4);
if (!a || !b || !inner || !team) {
free(a);
free(b);
free(inner);
n_parallel_team_free(&team);
return 1;
}
/* many short loops, each item once per loop */
for (i = 0; i < 1000; i++) {
int slices = n_parallel_team_run(team, 0, 100000, 100, bump, a);
if (i == 0) first = slices;
if (slices != first) same = 0;
}
expect_true("every item written once per loop", all_equal(a, 100000, 1000));
expect_true("four slices every time", same && first == 4);
memset(a, 0, 100000 * sizeof(int));
expect_true("a range under two grains runs as one slice", n_parallel_team_run(team, 0, 150, 100, bump, a) == 1);
expect_true("and does all of it", all_equal(a, 150, 1));
expect_true("an empty range does nothing", n_parallel_team_run(team, 10, 10, 1, bump, a) == 0);
expect_true("a NULL function does nothing", n_parallel_team_run(team, 0, 10, 1, NULL, a) == 0);
expect_true("a NULL team runs on the caller", n_parallel_team_run(NULL, 0, 10, 1, bump, a) == 1);
/* nested on the same team */
nest.team = team;
nest.items = inner;
nest.count = 64;
n_parallel_team_run(team, 0, 16, 1, nested, &nest);
expect_true("a nested loop runs inline and does all of it", all_equal(inner, 16 * 64, 1));
/* two threads sharing one team */
memset(a, 0, 100000 * sizeof(int));
share.team = team;
share.items = b;
if (pthread_create(&other, NULL, sharer, &share) != 0) return 1;
for (i = 0; i < 100; i++) n_parallel_team_run(team, 0, 100000, 100, bump, a);
pthread_join(other, NULL);
expect_true("two threads sharing a team both finish right", all_equal(a, 100000, 100) && all_equal(b, 100000, 100));
/* a team of one starts no thread */
solo = n_parallel_team_new(1);
memset(a, 0, 100000 * sizeof(int));
expect_true("a team of one runs as one slice", solo && n_parallel_team_run(solo, 0, 100000, 1, bump, a) == 1);
expect_true("and does all of it", all_equal(a, 100000, 1));
n_parallel_team_free(&solo);
n_parallel_team_free(&team);
expect_true("free sets the pointer to NULL", team == NULL);
n_parallel_team_free(&team);
free(a);
free(b);
free(inner);
n_log(LOG_NOTICE, "ex_parallel_team: %s", failures ? "FAILED" : "ok");
return failures ? 1 : 0;
}
_Filename: ex_parallel_team.c_
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB