Re-enable multi-threading

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-21 22:22:16 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-21 22:22:16 +0100
Commit c7ab12bba64d9c20ccd79b132dac475f7bc3923e (patch)
-rw-r--r-- .clang-format 4
-rw-r--r-- .editorconfig 23
-rw-r--r-- Makefile 2
-rw-r--r-- file.c 67
-rw-r--r-- file.h 6
-rw-r--r-- list.c 98
-rw-r--r-- list.h 12
-rw-r--r-- main.c 330
-rw-r--r-- tpool.c 141
-rw-r--r-- tpool.h 21
10 files changed, 434 insertions, 270 deletions
diff --git a/.clang-format b/.clang-format
  
1
UseTab: Always
  
2
IndentWidth: 4
  
3
TabWidth: 4
  
4
ColumnLimit: 0
diff --git a/.editorconfig b/.editorconfig
1
root = true
  
2
  
  
3
[*]
  
4
charset = utf-8
  
5
trim_trailing_whitespace = true
  
6
insert_final_newline = true
  
7
end_of_line = lf
  
8
  
  
9
[Makefile]
  
10
indent_style = tab
  
11
indent_size = 4
  
12
  
  
13
[*.c]
  
14
indent_style = space
  
15
indent_size = 2
  
16
  
  
17
[*.{css,html,js,django}]
  
18
indent_style = space
  
19
indent_size = 2
  
20
  
  
21
[*.go]
  
22
indent_style = tab
  
23
indent_size = 4
  
diff --git a/Makefile b/Makefile
1
TARGET = crep
1
TARGET = crep
2
SOURCES = $(wildcard *.c)
2
SOURCES = $(wildcard *.c *.h)
3
TS_ALIBS = $(shell find vendor -name "*.a" -print)
3
TS_ALIBS = $(shell find vendor -name "*.a" -print)
4
VENDOR_DIRS = $(wildcard vendor/*)
4
VENDOR_DIRS = $(wildcard vendor/*)
5
CFLAGS = $(EXTRA_FLAGS) -Wall -Wextra -std=gnu99 -pedantic -ggdb -O3
5
CFLAGS = $(EXTRA_FLAGS) -Wall -Wextra -std=gnu99 -pedantic -ggdb -O3
...
diff --git a/file.c b/file.c
...
3
  
3
  
4
#include "file.h"
4
#include "file.h"
5
  
5
  
6
struct FileContent read_entire_file(const char* file_path) {
6
struct FileContent read_entire_file(const char *file_path) {
7
  struct FileContent file_data;
7
	struct FileContent file_data;
8
  file_data.content = NULL;
8
	file_data.content = NULL;
9
  file_data.count = 0;
9
	file_data.count = 0;
10
  
10
  
11
  FILE* file = fopen(file_path, "rb");
11
	FILE *file = fopen(file_path, "rb");
12
  if (file == NULL) {
12
	if (file == NULL) {
13
    perror("Error opening file");
13
		perror("Error opening file");
14
    return file_data;
14
		return file_data;
15
  }
15
	}
16
  
16
  
17
  fseek(file, 0, SEEK_END);
17
	fseek(file, 0, SEEK_END);
18
  long file_size = ftell(file);
18
	long raw_size = ftell(file);
19
  fseek(file, 0, SEEK_SET);
19
	fseek(file, 0, SEEK_SET);
20
  
20
  
21
  if (file_size == -1) {
21
	if (raw_size == -1) {
22
    perror("Error getting file size");
22
		perror("Error getting file size");
23
    return file_data;
23
		fclose(file);
24
  }
24
		return file_data;
  
25
	}
25
  
26
  
26
  file_data.content = (const char*)malloc(file_size);
27
	size_t file_size = (size_t)raw_size;
27
  if (file_data.content == NULL) {
28
	char *content = (char *)malloc(file_size + 1);
28
    perror("Error allocating memory");
29
	if (content == NULL) {
29
    return file_data;
30
		perror("Error allocating memory");
30
  }
31
		fclose(file);
  
32
		return file_data;
  
33
	}
31
  
34
  
32
  size_t bytes_read = fread((void*)file_data.content, 1, file_size, file);
35
	size_t bytes_read = fread(content, 1, file_size, file);
33
  if (bytes_read != (size_t)file_size) {
36
	if (bytes_read != file_size) {
34
    perror("Error reading file");
37
		perror("Error reading file");
35
    free((void*)file_data.content);
38
		free(content);
36
    file_data.content = NULL;
39
		fclose(file);
37
    return file_data;
40
		return file_data;
38
  }
41
	}
39
  
42
  
40
  file_data.count = bytes_read;
43
	content[file_size] = '\0';
  
44
	file_data.content = content;
  
45
	file_data.count = file_size;
41
  
46
  
42
  fclose(file);
47
	fclose(file);
43
  return file_data;
48
	return file_data;
44
}
49
}
diff --git a/file.h b/file.h
...
4
#include <stdio.h>
4
#include <stdio.h>
5
  
5
  
6
struct FileContent {
6
struct FileContent {
7
  const char* content;
7
	const char *content;
8
  size_t count;
8
	size_t count;
9
};
9
};
10
  
10
  
11
struct FileContent read_entire_file(const char* file_path);
11
struct FileContent read_entire_file(const char *file_path);
12
  
12
  
13
#endif
13
#endif
diff --git a/list.c b/list.c
...
6
  
6
  
7
#include "list.h"
7
#include "list.h"
8
  
8
  
9
void add_file_path(Node** head, char* file_path) {
9
void add_file_path(Node **head, char *file_path) {
10
  Node* new = (Node*)malloc(sizeof(Node));
10
	Node *new = (Node *)malloc(sizeof(Node));
11
  new->file_path = strdup(file_path);
11
	if (new == NULL) {
12
  new->next = *head;
12
		perror("malloc");
13
  *head = new;
13
		exit(EXIT_FAILURE);
  
14
	}
  
15
	new->file_path = strdup(file_path);
  
16
	if (new->file_path == NULL) {
  
17
		perror("strdup");
  
18
		free(new);
  
19
		exit(EXIT_FAILURE);
  
20
	}
  
21
	new->next = *head;
  
22
	*head = new;
14
}
23
}
15
  
24
  
16
void list_files_recursively(char* base_path, Node** head) {
25
void list_files_recursively(char *base_path, Node **head) {
17
  char path[1000];
26
	char path[2048];
18
  struct dirent* dp;
27
	struct dirent *dp;
19
  DIR* dir = opendir(base_path);
28
	DIR *dir = opendir(base_path);
20
  
29
  
21
  if (!dir)
30
	if (!dir)
22
    return;
31
		return;
23
  
32
  
24
  while ((dp = readdir(dir)) != NULL) {
33
	while ((dp = readdir(dir)) != NULL) {
25
    if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
34
		if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
26
      strcpy(path, base_path);
35
			int ret = snprintf(path, sizeof(path), "%s%s%s", base_path, (base_path[strlen(base_path) - 1] == '/' ? "" : "/"), dp->d_name);
27
      strcat(path, "/");
  
28
      strcat(path, dp->d_name);
  
29
  
36
  
30
      struct stat statbuf;
37
			if (ret >= (int)sizeof(path)) {
31
      if (stat(path, &statbuf) != -1) {
38
				fprintf(stderr, "Path too long: %s/%s\n", base_path, dp->d_name);
32
        if (S_ISDIR(statbuf.st_mode)) {
39
				continue;
33
          list_files_recursively(path, head);
40
			}
34
        } else {
  
35
          add_file_path(head, path);
  
36
        }
  
37
      }
  
38
    }
  
39
  }
  
40
  
41
  
41
  closedir(dir);
42
			struct stat statbuf;
  
43
			if (stat(path, &statbuf) != -1) {
  
44
				if (S_ISDIR(statbuf.st_mode)) {
  
45
					list_files_recursively(path, head);
  
46
				} else {
  
47
					add_file_path(head, path);
  
48
				}
  
49
			}
  
50
		}
  
51
	}
  
52
  
  
53
	closedir(dir);
42
}
54
}
43
  
55
  
44
void free_file_list(Node* head) {
56
void free_file_list(Node *head) {
45
  Node* tmp;
57
	Node *tmp;
46
  
58
  
47
  while (head != NULL) {
59
	while (head != NULL) {
48
    tmp = head;
60
		tmp = head;
49
    head = head->next;
61
		head = head->next;
50
    free(tmp->file_path);
62
		free(tmp->file_path);
51
    free(tmp);
63
		free(tmp);
52
  }
64
	}
53
}
65
}
54
  
66
  
55
int size_of_file_list(Node* head) {
67
int size_of_file_list(Node *head) {
56
  int count = 0;
68
	int count = 0;
57
  
69
  
58
  Node* current = head;
70
	Node *current = head;
59
  while (current != NULL) {
71
	while (current != NULL) {
60
    count++;
72
		count++;
61
    current = current->next;
73
		current = current->next;
62
  }
74
	}
63
  
75
  
64
  return count;
76
	return count;
65
}
77
}
diff --git a/list.h b/list.h
...
2
#define LIST_H
2
#define LIST_H
3
  
3
  
4
typedef struct node {
4
typedef struct node {
5
  char* file_path;
5
	char *file_path;
6
  struct node* next;
6
	struct node *next;
7
} Node;
7
} Node;
8
  
8
  
9
void add_file_path(Node** head, char* file_path);
9
void add_file_path(Node **head, char *file_path);
10
void list_files_recursively(char* base_path, Node** head);
10
void list_files_recursively(char *base_path, Node **head);
11
void free_file_list(Node* head);
11
void free_file_list(Node *head);
12
int size_of_file_list(Node* head);
12
int size_of_file_list(Node *head);
13
  
13
  
14
#endif
14
#endif
diff --git a/main.c b/main.c
...
19
  
19
  
20
#include "file.h"
20
#include "file.h"
21
#include "list.h"
21
#include "list.h"
  
22
#include "tpool.h"
22
  
23
  
23
#define DEBUG 1
24
#define DEBUG 0
24
  
25
  
25
typedef struct {
26
typedef struct {
26
  const char* fname;
27
	const char *fname;
27
  const char* ftype;
28
	const char *ftype;
28
  const char* fparams;
29
	const char *fparams;
29
  size_t lineno;
30
	size_t lineno;
30
} Function;
31
} Function;
31
  
32
  
32
const char* extract_value(TSNode captured_node, const char* source_code) {
33
const char *extract_value(TSNode captured_node, const char *source_code) {
33
  size_t start = ts_node_start_byte(captured_node);
34
	size_t start = ts_node_start_byte(captured_node);
34
  size_t end = ts_node_end_byte(captured_node);
35
	size_t end = ts_node_end_byte(captured_node);
35
  size_t length = end - start;
36
	size_t length = end - start;
36
  char* buffer = malloc(length + 1);  // +1 for the null terminator
37
	char *buffer = malloc(length + 1); // +1 for the null terminator
37
  
38
  
38
  if (buffer != NULL) {
39
	if (buffer != NULL) {
39
    snprintf(buffer, length + 1, "%.*s", (int)length, &source_code[start]);
40
		snprintf(buffer, length + 1, "%.*s", (int)length, &source_code[start]);
40
    return buffer;
41
		return buffer;
41
  }
42
	} else {
  
43
		perror("malloc");
  
44
		exit(EXIT_FAILURE);
  
45
	}
42
  
46
  
43
  return NULL;
47
	return NULL;
44
}
48
}
45
  
49
  
46
char* remove_newlines(const char* str) {
50
char *remove_newlines(const char *str) {
47
  size_t length = strlen(str);
51
	if (str == NULL)
48
  char* result = (char*)malloc(length + 1);  // +1 for the null terminator
52
		return NULL;
49
  if (result == NULL) {
53
	size_t length = strlen(str);
50
    fprintf(stderr, "Memory allocation failed\n");
54
	char *result = (char *)malloc(length + 1); // +1 for the null terminator
51
    exit(1);
55
	if (result == NULL) {
52
  }
56
		fprintf(stderr, "Memory allocation failed\n");
  
57
		exit(1);
  
58
	}
53
  
59
  
54
  size_t j = 0;
60
	size_t j = 0;
55
  for (size_t i = 0; i < length; i++) {
61
	for (size_t i = 0; i < length; i++) {
56
    if (str[i] != '\n') {
62
		if (str[i] != '\n') {
57
      result[j++] = str[i];
63
			result[j++] = str[i];
58
    }
64
		}
59
  }
65
	}
60
  
66
  
61
  result[j] = '\0';
67
	result[j] = '\0';
62
  return result;
68
	return result;
63
}
69
}
64
  
70
  
65
struct ThreadArgs {
71
struct ThreadArgs {
66
  const char* file_path;
72
	const char *file_path;
67
  const char* source_code;
73
	const char *source_code;
68
  TSLanguage* language;
74
	TSLanguage *language;
69
  const char* cfname;
75
	const char *cfname;
70
};
76
};
71
  
77
  
72
// void parse_source_file(const char *file_path, const char *source_code,
78
// void parse_source_file(const char *file_path, const char *source_code,
73
// TSLanguage *language, const char *cfname) {
79
// TSLanguage *language, const char *cfname) {
74
void* parse_source_file(void* arg) {
80
void parse_source_file(void *arg) {
75
  struct ThreadArgs* args = (struct ThreadArgs*)arg;
81
	struct ThreadArgs *args = (struct ThreadArgs *)arg;
76
  
82
  
77
  const char* file_path = args->file_path;
83
	const char *file_path = args->file_path;
78
  const char* source_code = args->source_code;
84
	const char *source_code = args->source_code;
79
  TSLanguage* language = args->language;
85
	TSLanguage *language = args->language;
80
  const char* cfname = args->cfname;
86
	const char *cfname = args->cfname;
81
  
87
  
82
  TSParser* parser = ts_parser_new();
88
	TSParser *parser = ts_parser_new();
83
  ts_parser_set_language(parser, language);
89
	ts_parser_set_language(parser, language);
84
  
90
  
85
  TSTree* tree =
91
	TSTree *tree =
86
      ts_parser_parse_string(parser, NULL, source_code, strlen(source_code));
92
		ts_parser_parse_string(parser, NULL, source_code, strlen(source_code));
87
  TSNode root_node = ts_tree_root_node(tree);
93
	TSNode root_node = ts_tree_root_node(tree);
88
  
94
  
89
  const char* query_string =
95
	const char *query_string =
90
      "(function_definition type: (primitive_type) @ftype declarator: "
96
		"(function_definition type: (_) @ftype declarator: (function_declarator declarator: (identifier) @fname parameters: (parameter_list) @fparams))"
91
      "(function_declarator declarator: (identifier) @fname parameters: "
97
		"(function_definition type: (_) @ftype declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @fname parameters: (parameter_list) @fparams)))"
92
      "(parameter_list) @fparams))";
98
		"(declaration type: (_) @ftype declarator: (function_declarator declarator: (identifier) @fname parameters: (parameter_list) @fparams))"
  
99
		"(declaration type: (_) @ftype declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @fname parameters: (parameter_list) @fparams)))";
93
  
100
  
94
  uint32_t error_offset;
101
	uint32_t error_offset;
95
  TSQueryError error_type;
102
	TSQueryError error_type;
96
  TSQuery* query = ts_query_new(language, query_string, strlen(query_string),
103
	TSQuery *query = ts_query_new(language, query_string, strlen(query_string), &error_offset, &error_type);
97
                                &error_offset, &error_type);
  
98
  
104
  
99
  TSQueryCursor* query_cursor = ts_query_cursor_new();
105
	TSQueryCursor *query_cursor = ts_query_cursor_new();
100
  ts_query_cursor_exec(query_cursor, query, root_node);
106
	ts_query_cursor_exec(query_cursor, query, root_node);
101
  
107
  
102
  if (query != NULL) {
108
	if (query != NULL) {
103
    TSQueryMatch match;
109
		TSQueryMatch match;
104
    while (ts_query_cursor_next_match(query_cursor, &match)) {
110
		while (ts_query_cursor_next_match(query_cursor, &match)) {
105
      Function fn = {0};
111
			Function fn = {0};
106
  
112
  
107
      for (unsigned i = 0; i < match.capture_count; i++) {
113
			for (unsigned i = 0; i < match.capture_count; i++) {
108
        TSQueryCapture capture = match.captures[i];
114
				TSQueryCapture capture = match.captures[i];
109
        TSNode captured_node = capture.node;
115
				TSNode captured_node = capture.node;
110
  
116
  
111
        uint32_t capture_name_length;
117
				uint32_t capture_name_length;
112
        const char* capture_name = ts_query_capture_name_for_id(
118
				const char *capture_name = ts_query_capture_name_for_id(
113
            query, capture.index, &capture_name_length);
119
					query, capture.index, &capture_name_length);
114
  
120
  
115
        if (strcmp(capture_name, "fname") == 0) {
121
				if (strcmp(capture_name, "fname") == 0) {
116
          fn.fname = extract_value(captured_node, source_code);
122
					fn.fname = extract_value(captured_node, source_code);
117
  
123
  
118
          TSPoint start_point = ts_node_start_point(captured_node);
124
					TSPoint start_point = ts_node_start_point(captured_node);
119
          fn.lineno = start_point.row;
125
					fn.lineno = start_point.row;
120
        }
126
				}
121
  
127
  
122
        if (strcmp(capture_name, "ftype") == 0) {
128
				if (strcmp(capture_name, "ftype") == 0) {
123
          fn.ftype = extract_value(captured_node, source_code);
129
					fn.ftype = extract_value(captured_node, source_code);
124
        }
130
				}
125
  
131
  
126
        if (strcmp(capture_name, "fparams") == 0) {
132
				if (strcmp(capture_name, "fparams") == 0) {
127
          fn.fparams = extract_value(captured_node, source_code);
133
					fn.fparams = extract_value(captured_node, source_code);
128
        }
134
				}
129
      }
135
			}
130
  
136
  
131
      // Substring matching.
137
			// Substring matching.
132
      // FIXME: Add Levenshtein distance.
138
			// FIXME: Add Levenshtein distance.
133
      char* result = strstr(fn.fname, cfname);
139
			if (fn.fname != NULL) {
134
      if (result != NULL) {
140
				char *result = strstr(fn.fname, cfname);
135
        char* fparams_formatted = remove_newlines(fn.fparams);
141
				if (result != NULL) {
136
        printf("%s:%zu:\t%s %s %s\n", file_path, fn.lineno, fn.ftype, fn.fname,
142
					char *fparams_formatted = remove_newlines(fn.fparams);
137
               fparams_formatted);
143
					printf("%s:%zu:\t%s %s %s\n", file_path, fn.lineno, fn.ftype, fn.fname, fparams_formatted);
138
      }
144
					free(fparams_formatted);
139
    }
145
				}
140
  } else {
146
			}
141
    if (DEBUG) {
  
142
      printf("Query creation failed at offset %u with error type %d\n",
  
143
             error_offset, error_type);
  
144
    }
  
145
  }
  
146
  
147
  
147
  ts_query_cursor_delete(query_cursor);
148
			// Free captured values
148
  ts_query_delete(query);
149
			free((void *)fn.fname);
149
  ts_tree_delete(tree);
150
			free((void *)fn.ftype);
150
  ts_parser_delete(parser);
151
			free((void *)fn.fparams);
  
152
		}
  
153
	} else {
  
154
		if (DEBUG) {
  
155
			printf("Query creation failed at offset %u with error type %d\n", error_offset, error_type);
  
156
		}
  
157
	}
  
158
  
  
159
	ts_query_cursor_delete(query_cursor);
  
160
	ts_query_delete(query);
  
161
	ts_tree_delete(tree);
  
162
	ts_parser_delete(parser);
151
  
163
  
152
  return NULL;
164
	// Cleanup thread arguments
  
165
	free((void *)source_code);
  
166
	free(args);
153
}
167
}
154
  
168
  
155
const char* get_file_extension(const char* file_path) {
169
const char *get_file_extension(const char *file_path) {
156
  const char* extension = strrchr(file_path, '.');
170
	const char *extension = strrchr(file_path, '.');
157
  if (extension != NULL) {
171
	if (extension != NULL) {
158
    return extension + 1;
172
		return extension + 1;
159
  }
173
	}
160
  return NULL;
174
	return NULL;
161
}
175
}
162
  
176
  
163
int main(int argc, char* argv[]) {
177
int main(int argc, char *argv[]) {
164
  if (argc < 3) {
178
	if (argc < 3) {
165
    printf("Usage: %s <search term> <directory>\n", argv[0]);
179
		printf("Usage: %s <search term> <directory>\n", argv[0]);
166
    return 1;
180
		return 1;
167
  }
181
	}
168
  
182
  
169
  char* cfname = argv[1];
183
	const char *cfname = argv[1];
170
  char* directory = argv[2];
184
	char *directory = argv[2];
171
  
185
  
172
  TSLanguage* tree_sitter_c(void);
186
	TSLanguage *tree_sitter_c(void);
173
  TSLanguage* tree_sitter_python(void);
187
	TSLanguage *tree_sitter_python(void);
174
  
188
  
175
  Node* head = NULL;
189
	Node *head = NULL;
176
  list_files_recursively(directory, &head);
190
	list_files_recursively(directory, &head);
177
  int list_size = size_of_file_list(head);
191
	int list_size = size_of_file_list(head);
178
  /* pthread_t threads[list_size]; */
  
179
  
192
  
180
  if (DEBUG) {
193
	if (DEBUG) {
181
    printf("Scanning %d files\n", list_size);
194
		printf("Scanning %d files\n", list_size);
182
  }
195
	}
183
  
196
  
184
  Node* current = head;
197
	ThreadPool *pool = tp_create(8);
185
  //   int thread_index = 0;
198
	if (!pool) {
186
  while (current != NULL) {
199
		perror("Failed to create thread pool");
187
    const char* file_path = current->file_path;
200
		return 1;
188
    const char* extension = get_file_extension(file_path);
201
	}
189
    struct FileContent source_file = read_entire_file(file_path);
  
190
  
202
  
191
    if (source_file.content != NULL) {
203
	Node *current = head;
192
      if (extension != NULL) {
204
	while (current != NULL) {
193
        if (strcmp(extension, "c") == 0 || strcmp(extension, "h") == 0) {
205
		const char *file_path = current->file_path;
194
          /* parse_source_file(file_path, source_file.content, tree_sitter_c(),
206
		const char *extension = get_file_extension(file_path);
195
           * cfname); */
  
196
  
207
  
197
          struct ThreadArgs thread_args;
208
		if (extension != NULL && (strcmp(extension, "c") == 0 || strcmp(extension, "h") == 0)) {
198
          thread_args.file_path = file_path;
209
			struct FileContent source_file = read_entire_file(file_path);
199
          thread_args.source_code = source_file.content;
210
			if (source_file.content != NULL) {
200
          thread_args.language = tree_sitter_c();
211
				struct ThreadArgs *thread_args = malloc(sizeof(struct ThreadArgs));
201
          thread_args.cfname = cfname;
212
				if (!thread_args) {
  
213
					perror("Failed to allocate thread args");
  
214
					free((void *)source_file.content);
  
215
					continue;
  
216
				}
202
  
217
  
203
          parse_source_file(&thread_args);
218
				thread_args->file_path = file_path;
  
219
				thread_args->source_code = source_file.content;
  
220
				thread_args->language = tree_sitter_c();
  
221
				thread_args->cfname = cfname;
204
  
222
  
205
          /* printf("> creating thread #%d\n", thread_index); */
223
				tp_add_job(pool, (thread_func_t)parse_source_file, thread_args);
206
          /* if (pthread_create(&threads[thread_index], NULL, parse_source_file,
224
			} else {
207
           * &thread_args) != 0) { */
225
				if (DEBUG) {
208
          /*   fprintf(stderr, "Error creating thread %d\n", thread_index); */
226
					fprintf(stderr, "Failed to read file: %s\n", file_path);
209
          /*   return 1; */
227
				}
210
          /* } */
228
			}
211
        }
229
		}
212
      }
  
213
      free((void*)source_file.content);
  
214
    } else {
  
215
      if (DEBUG) {
  
216
        fprintf(stderr, "Failed to read file.\n");
  
217
      }
  
218
    }
  
219
    current = current->next;
  
220
    //     thread_index++;
  
221
  }
  
222
  
230
  
223
  // Collecting threads.
231
		current = current->next;
224
  /* for (int i = 0; i < list_size; i++) { */
232
	}
225
  /*   printf("> collecting thread #%d\n", thread_index); */
  
226
  /*   if (pthread_join(threads[i], NULL) != 0) { */
  
227
  /*     fprintf(stderr, "Error joining thread %d\n", i); */
  
228
  /*     return 1; */
  
229
  /*   } */
  
230
  /* } */
  
231
  
233
  
232
  free_file_list(head);
234
	tp_wait(pool);
233
  return 0;
235
	tp_destroy(pool);
  
236
	free_file_list(head);
  
237
	return 0;
234
}
238
}
diff --git a/tpool.c b/tpool.c
  
1
#include "tpool.h"
  
2
#include <stdio.h>
  
3
#include <stdlib.h>
  
4
  
  
5
typedef struct ThreadPoolJobNode {
  
6
	ThreadPoolJob job;
  
7
	struct ThreadPoolJobNode *next;
  
8
} ThreadPoolJobNode;
  
9
  
  
10
struct ThreadPool {
  
11
	pthread_mutex_t lock;
  
12
	pthread_cond_t notify;
  
13
	pthread_cond_t working_cond;
  
14
  
  
15
	pthread_t *threads;
  
16
	int num_threads;
  
17
  
  
18
	ThreadPoolJobNode *queue_head;
  
19
	ThreadPoolJobNode *queue_tail;
  
20
  
  
21
	int active_jobs; // Jobs currently running
  
22
	int queued_jobs; // Jobs waiting in queue
  
23
	bool stop;
  
24
};
  
25
  
  
26
static void *tp_worker(void *arg) {
  
27
	ThreadPool *pool = (ThreadPool *)arg;
  
28
  
  
29
	while (1) {
  
30
		pthread_mutex_lock(&pool->lock);
  
31
  
  
32
		while (pool->queue_head == NULL && !pool->stop) {
  
33
			pthread_cond_wait(&pool->notify, &pool->lock);
  
34
		}
  
35
  
  
36
		if (pool->stop && pool->queue_head == NULL) {
  
37
			pthread_mutex_unlock(&pool->lock);
  
38
			break;
  
39
		}
  
40
  
  
41
		ThreadPoolJobNode *node = pool->queue_head;
  
42
		pool->queue_head = node->next;
  
43
		if (pool->queue_head == NULL) {
  
44
			pool->queue_tail = NULL;
  
45
		}
  
46
  
  
47
		pool->queued_jobs--;
  
48
		pool->active_jobs++;
  
49
  
  
50
		pthread_mutex_unlock(&pool->lock);
  
51
  
  
52
		// Execute job
  
53
		if (node->job.function) {
  
54
			node->job.function(node->job.arg);
  
55
		}
  
56
		free(node);
  
57
  
  
58
		pthread_mutex_lock(&pool->lock);
  
59
		pool->active_jobs--;
  
60
		if (pool->active_jobs == 0 && pool->queue_head == NULL) {
  
61
			pthread_cond_signal(&pool->working_cond);
  
62
		}
  
63
		pthread_mutex_unlock(&pool->lock);
  
64
	}
  
65
  
  
66
	return NULL;
  
67
}
  
68
  
  
69
ThreadPool *tp_create(int num_threads) {
  
70
	ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool));
  
71
	if (pool == NULL)
  
72
		return NULL;
  
73
  
  
74
	pool->num_threads = num_threads;
  
75
	pool->queue_head = NULL;
  
76
	pool->queue_tail = NULL;
  
77
	pool->active_jobs = 0;
  
78
	pool->queued_jobs = 0;
  
79
	pool->stop = false;
  
80
  
  
81
	pthread_mutex_init(&pool->lock, NULL);
  
82
	pthread_cond_init(&pool->notify, NULL);
  
83
	pthread_cond_init(&pool->working_cond, NULL);
  
84
  
  
85
	pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * num_threads);
  
86
	for (int i = 0; i < num_threads; i++) {
  
87
		pthread_create(&pool->threads[i], NULL, tp_worker, pool);
  
88
	}
  
89
  
  
90
	return pool;
  
91
}
  
92
  
  
93
void tp_add_job(ThreadPool *pool, thread_func_t function, void *arg) {
  
94
	ThreadPoolJobNode *node = (ThreadPoolJobNode *)malloc(sizeof(ThreadPoolJobNode));
  
95
	if (node == NULL) {
  
96
		perror("malloc");
  
97
		exit(EXIT_FAILURE);
  
98
	}
  
99
	node->job.function = function;
  
100
	node->job.arg = arg;
  
101
	node->next = NULL;
  
102
  
  
103
	pthread_mutex_lock(&pool->lock);
  
104
  
  
105
	if (pool->queue_tail) {
  
106
		pool->queue_tail->next = node;
  
107
	} else {
  
108
		pool->queue_head = node;
  
109
	}
  
110
	pool->queue_tail = node;
  
111
  
  
112
	pool->queued_jobs++;
  
113
	pthread_cond_signal(&pool->notify);
  
114
  
  
115
	pthread_mutex_unlock(&pool->lock);
  
116
}
  
117
  
  
118
void tp_wait(ThreadPool *pool) {
  
119
	pthread_mutex_lock(&pool->lock);
  
120
	while (pool->active_jobs > 0 || pool->queue_head != NULL) {
  
121
		pthread_cond_wait(&pool->working_cond, &pool->lock);
  
122
	}
  
123
	pthread_mutex_unlock(&pool->lock);
  
124
}
  
125
  
  
126
void tp_destroy(ThreadPool *pool) {
  
127
	pthread_mutex_lock(&pool->lock);
  
128
	pool->stop = true;
  
129
	pthread_cond_broadcast(&pool->notify);
  
130
	pthread_mutex_unlock(&pool->lock);
  
131
  
  
132
	for (int i = 0; i < pool->num_threads; i++) {
  
133
		pthread_join(pool->threads[i], NULL);
  
134
	}
  
135
  
  
136
	free(pool->threads);
  
137
	pthread_mutex_destroy(&pool->lock);
  
138
	pthread_cond_destroy(&pool->notify);
  
139
	pthread_cond_destroy(&pool->working_cond);
  
140
	free(pool);
  
141
}
diff --git a/tpool.h b/tpool.h
  
1
#ifndef THREAD_POOL_H
  
2
#define THREAD_POOL_H
  
3
  
  
4
#include <pthread.h>
  
5
#include <stdbool.h>
  
6
  
  
7
typedef void (*thread_func_t)(void *arg);
  
8
  
  
9
typedef struct {
  
10
	thread_func_t function;
  
11
	void *arg;
  
12
} ThreadPoolJob;
  
13
  
  
14
typedef struct ThreadPool ThreadPool;
  
15
  
  
16
ThreadPool *tp_create(int num_threads);
  
17
void tp_add_job(ThreadPool *pool, thread_func_t function, void *arg);
  
18
void tp_wait(ThreadPool *pool);
  
19
void tp_destroy(ThreadPool *pool);
  
20
  
  
21
#endif