11 |
12 | ## ⚡ Introduction
13 |
14 | **This is a simple Linux thread pool library that supports multiple thread pool instances.** 😊
15 |
16 | ## 💻 Diagram
17 | The internal structure consists of multiple thread pools, each with its own circular queue. When a thread pool is full, new tasks will be placed in its circular queue. Once a thread exits, it will fetch a task from the queue for processing.
18 |
19 |
20 |
21 |
22 | ## 🚀 How to use?
23 |
24 | **The following operations are based on the root directory of the current project, please ensure to perform them correctly!**
25 |
26 | ### **Compile**
27 |
28 | ```bash
29 | cd z_thpool
30 | make
31 | cd lib
32 | ls
33 | libtestlib.a libz_thpool.a // Compilation completed
34 | ```
35 |
36 | ### **Test**
37 |
38 | #### Complete Test
39 | ```bash
40 | ./build/z_thpool_test
41 | Enter a command (type 'exit' to quit):
42 | > help // Enter help to view test commands
43 |
44 | Enter a command (type 'exit' to quit):
45 |
46 | create pool1 5 100 64 # Create pool named 'pool1' with 5 threads, 100 cache queues, 64k stack size
47 | destroy pool1 # Destroy pool named 'pool1'
48 | add pool1 10 # Add 10 tasks to pool named 'pool1'
49 | show pool1 # Show state of pool named 'pool1'
50 | test # Run test suite
51 | help # Show this help
52 |
53 | > test // Enter test to check complete test situation
54 | Starting thread pool extreme test...
55 | [debug ][src/z_thpool.c:182, z_thpool_create] enter
56 | [debug ][src/z_thpool.c:229, z_thpool_create] exit :0
57 | Task 0 added successfully
58 | ...
59 | Task 99 added successfully
60 | Waiting...
61 | Task 0 is being processed by thread 140675616458496
62 | ...
63 | Task 99 is being processed by thread 140675609564928
64 | Waiting...
65 | Waiting...
66 | Waiting...
67 | Waiting...
68 | Task 0 argument after processing: 0
69 | ...
70 | Task 99 argument after processing: 99
71 | Thread pool extreme test completed.
72 | ```
73 |
74 | #### Step-by-Step Test
75 | ```bash
76 | ./build/z_thpool_test
77 | Enter a command (type 'exit' to quit):
78 | > create pool1 5 100 64 // Create a thread pool named 'pool1' with 5 threads, 100 cache queues, 64K stack size
79 | Created thread pool 'pool1'
80 |
81 | > create pool2 3 50 64 // Create another thread pool named 'pool2'
82 | Created thread pool 'pool2'
83 |
84 | > add pool1 10 // Add 10 tasks to pool1
85 | Added task 0 to pool 'pool1'
86 | ...
87 | Added task 9 to pool 'pool1'
88 |
89 | > show pool1 // View pool1 state
90 | ------------------------------------------------------------------------------------------------------------------------
91 | | z_thpool module
92 | ------------------------------------------------------------------------------------------------------------------------
93 | Ver: 0.0.2.0
94 | Pool Name: pool1
95 | ------------------------------------------------------------------------------------------------------------------------
96 | max nums: 5 // Maximum threads is 5
97 | create nums: 5 // Threads already created is 5
98 | busy nums: 5 // Currently running threads is 5
99 | max cache nums: 100 // Created cache queue is 100
100 | use cache nums: 5 // Tasks waiting in the queue
101 | pub_bytes: 200
102 | sub_bytes: 0
103 |
104 | > destroy pool1 // Destroy pool1
105 | Destroyed thread pool 'pool1'
106 | ```
107 |
108 | ### **Usage Functions**
109 | Just four steps to use the thread pool:
110 | ```c
111 | // Step 1: Create a thread pool configuration
112 | struct z_thpool_config_struct config;
113 | config.max_thread_nums = 50; // Maximum number of concurrently running threads is 50
114 | config.msg_node_max = 1000; // Maximum capacity of the cache queue is 1000 tasks
115 | config.thread_stack_size = 64 * 1024; // Stack size of each thread is 64KB
116 | strncpy(config.pool_name, "pool1", sizeof(config.pool_name) - 1); // Name your thread pool
117 |
118 | // Step 2: Create the thread pool
119 | z_thpool_handle_t handle;
120 | if (z_thpool_create(&config, &handle) == 0) {
121 | printf("Thread pool created successfully\n");
122 | }
123 |
124 | // Step 3: Add tasks to the thread pool
125 | void task_callback(void *arg) {
126 | printf("Processing task: %d\n", *(int*)arg);
127 | }
128 |
129 | int task_arg = 1;
130 | if (z_thpool_add_work(handle, task_callback, &task_arg) == 0) {
131 | printf("Task added successfully\n");
132 | }
133 |
134 | // Step 4: When done, destroy the thread pool
135 | z_thpool_destroy(handle);
136 | ```
137 |
138 | ### **Multiple Thread Pools Example**
139 | ```c
140 | // Create two thread pools for different purposes
141 | struct z_thpool_config_struct config1 = {
142 | .max_thread_nums = 5,
143 | .msg_node_max = 100,
144 | .thread_stack_size = 64 * 1024
145 | };
146 | strncpy(config1.pool_name, "io_pool", sizeof(config1.pool_name) - 1);
147 |
148 | struct z_thpool_config_struct config2 = {
149 | .max_thread_nums = 10,
150 | .msg_node_max = 200,
151 | .thread_stack_size = 64 * 1024
152 | };
153 | strncpy(config2.pool_name, "compute_pool", sizeof(config2.pool_name) - 1);
154 |
155 | z_thpool_handle_t io_pool, compute_pool;
156 | z_thpool_create(&config1, &io_pool);
157 | z_thpool_create(&config2, &compute_pool);
158 |
159 | // Use different pools for different tasks
160 | z_thpool_add_work(io_pool, io_task_callback, io_arg);
161 | z_thpool_add_work(compute_pool, compute_task_callback, compute_arg);
162 |
163 | // Clean up when done
164 | z_thpool_destroy(io_pool);
165 | z_thpool_destroy(compute_pool);
166 | ```
167 |
168 | ### **File Description**
169 | The key files are:
170 | ```base
171 | test.c # Test program with command line interface
172 | z_kfifo.c # Cache queue implementation
173 | z_thpool.c # Thread pool implementation
174 | z_debug.h # Debug information toggle
175 | z_tool.h # Tool macros
176 | z_table_print.c # Table printing utility
177 | ```
178 |
179 | ## 🛠️ Features
180 | - Support for multiple thread pool instances
181 | - Named thread pools for better management
182 | - Independent configuration for each pool
183 | - Thread-safe operations
184 | - Resource cleanup on pool destruction
185 | - Command-line interface for testing
186 |
187 | ## 🛠️ About
188 |
189 | ## ❓ FAQ
190 |
191 | ## 🤝 Development Guide
192 |
193 | ## 🚀 Star
194 | [](https://starchart.cc/BitStreamlet/z_thpool)
195 |
196 | ## 🌟 Contribution
197 | Thanks to everyone who has contributed to z_thpool! 🎉
198 |
199 |
200 | ## 🌟 Acknowledgment
201 | **Thank you for taking the time to read our project documentation.**
202 | **If you find this project helpful, please support us with a Star. Thank you!**
--------------------------------------------------------------------------------
/src/z_kfifo.c:
--------------------------------------------------------------------------------
1 | #include "z_tool.h"
2 | #include "z_debug.h"
3 | #include "z_kfifo.h"
4 |
5 | // Initializes the kfifo structure with a buffer and its size.
6 | void z_kfifo_init(struct z_kfifo_struct *p_fifo, void *p_buffer, uint32_t size) {
7 | if (!p_fifo) return; // Exit if the fifo pointer is null.
8 | p_fifo->p_buffer = p_buffer; // Set the buffer pointer.
9 | p_fifo->size = size; // Set the size of the buffer.
10 | p_fifo->in = p_fifo->out = 0; // Initialize read and write pointers to zero.
11 | }
12 |
13 | // Allocates memory for the kfifo buffer and initializes the structure.
14 | int z_kfifo_malloc(struct z_kfifo_struct *p_fifo, uint32_t size) {
15 | if (!p_fifo) return -1; // Return error if fifo pointer is null.
16 | uint8_t *p_buffer;
17 |
18 | // Round up size to the nearest power of two if it's not already.
19 | if (size & (size - 1)) {
20 | size = Z_TOOL_ROUNDUP_POW_OF_TWO(size);
21 | }
22 |
23 | // Allocate memory for the buffer.
24 | p_buffer = (uint8_t *)z_tool_malloc(size);
25 | if (!p_buffer) {
26 | z_kfifo_init(p_fifo, NULL, 0); // Reset fifo if allocation fails.
27 | return -1; // Return error if memory allocation fails.
28 | }
29 |
30 | // Initialize fifo with allocated buffer and size.
31 | z_kfifo_init(p_fifo, p_buffer, size);
32 | return 0; // Return success.
33 | }
34 |
35 | // Frees the memory allocated for the kfifo buffer.
36 | void z_kfifo_free(struct z_kfifo_struct *p_fifo) {
37 | if (!p_fifo) return; // Exit if the fifo pointer is null.
38 | if (p_fifo->p_buffer) {
39 | z_tool_free(p_fifo->p_buffer); // Free the allocated buffer.
40 | }
41 | z_kfifo_init(p_fifo, NULL, 0); // Reset the fifo structure.
42 | }
43 |
44 | // Writes data into the kfifo buffer.
45 | uint32_t z_kfifo_in(struct z_kfifo_struct *p_fifo, const void *p_from, uint32_t len) {
46 | if (!p_fifo || !p_from) return 0; // Return zero if fifo or source pointer is null.
47 | uint32_t l, off;
48 |
49 | // Limit the length to the available space in the buffer.
50 | len = Z_TOOL_MIN(p_fifo->size - (p_fifo->in - p_fifo->out), len);
51 | off = p_fifo->in & (p_fifo->size - 1); // Calculate offset for circular buffer.
52 |
53 | // Determine how much data can be written in the first segment.
54 | l = Z_TOOL_MIN(len, p_fifo->size - off);
55 | memcpy(p_fifo->p_buffer + off, p_from, l); // Write to the buffer.
56 |
57 | // Write remaining data if the buffer wraps around.
58 | memcpy(p_fifo->p_buffer, (char *)p_from + l, len - l);
59 | p_fifo->in += len; // Update the write pointer.
60 | return len; // Return the number of bytes written.
61 | }
62 |
63 | // Reads data from the kfifo buffer.
64 | uint32_t z_kfifo_out(struct z_kfifo_struct *p_fifo, void *p_to, uint32_t len) {
65 | if (!p_fifo || !p_to) return 0; // Return zero if fifo or destination pointer is null.
66 | uint32_t off, l;
67 |
68 | // Limit the length to the available data in the buffer.
69 | len = Z_TOOL_MIN(p_fifo->in - p_fifo->out, len);
70 | off = p_fifo->out & (p_fifo->size - 1); // Calculate offset for circular buffer.
71 |
72 | // Determine how much data can be read in the first segment.
73 | l = Z_TOOL_MIN(len, p_fifo->size - off);
74 | memcpy(p_to, p_fifo->p_buffer + off, l); // Read from the buffer.
75 |
76 | // Read remaining data if the buffer wraps around.
77 | memcpy((char *)p_to + l, p_fifo->p_buffer, len - l);
78 | p_fifo->out += len; // Update the read pointer.
79 | return len; // Return the number of bytes read.
80 | }
81 |
82 | // Checks the number of bytes available to read from the kfifo.
83 | uint32_t z_kfifo_out_check(struct z_kfifo_struct *p_fifo, void *p_to, uint32_t len) {
84 | if (!p_fifo || !p_to) return 0; // Return zero if fifo or destination pointer is null.
85 | uint32_t off, l;
86 |
87 | // Limit the length to the available data in the buffer.
88 | len = Z_TOOL_MIN(p_fifo->in - p_fifo->out, len);
89 | off = p_fifo->out & (p_fifo->size - 1); // Calculate offset for circular buffer.
90 |
91 | // Determine how much data can be read in the first segment.
92 | l = Z_TOOL_MIN(len, p_fifo->size - off);
93 | memcpy(p_to, p_fifo->p_buffer + off, l); // Read from the buffer.
94 |
95 | // Read remaining data if the buffer wraps around.
96 | memcpy((char *)p_to + l, p_fifo->p_buffer, len - l);
97 | return len; // Return the number of bytes read.
98 | }
99 |
100 | // Returns the amount of free space in the kfifo.
101 | uint32_t z_kfifo_space(struct z_kfifo_struct *p_fifo) {
102 | if (!p_fifo) return 0; // Return zero if fifo pointer is null.
103 | return p_fifo->size - (p_fifo->in - p_fifo->out); // Calculate available space.
104 | }
105 |
106 | // Returns the amount of data currently stored in the kfifo.
107 | uint32_t z_kfifo_data_len(struct z_kfifo_struct *p_fifo) {
108 | if (!p_fifo) return 0; // Return zero if fifo pointer is null.
109 | return p_fifo->in - p_fifo->out; // Calculate the length of stored data.
110 | }
111 |
112 | // Tests various functionalities of the kfifo.
113 | uint32_t z_kfifo_test(void) {
114 | // Define the buffer size and initialize test data.
115 | const uint32_t buffer_size = 16;
116 | uint8_t test_data_in[buffer_size];
117 | uint8_t test_data_out[buffer_size];
118 |
119 | for (uint32_t i = 0; i < buffer_size; i++) {
120 | test_data_in[i] = i; // Fill test data with sequential values.
121 | }
122 |
123 | // Define kfifo structure and zero-initialize it.
124 | struct z_kfifo_struct fifo;
125 | memset(&fifo, 0, sizeof(fifo));
126 |
127 | // Test memory allocation for the kfifo.
128 | if (z_kfifo_malloc(&fifo, buffer_size) != 0) {
129 | Z_RAW("Memory allocation failed\n");
130 | return -1; // Return error if memory allocation fails.
131 | }
132 |
133 | // Test writing data into the kfifo.
134 | uint32_t written = z_kfifo_in(&fifo, test_data_in, buffer_size);
135 | if (written != buffer_size) {
136 | Z_RAW("Data write failed, written: %u\n", written);
137 | z_kfifo_free(&fifo);
138 | return -1; // Return error if the write operation fails.
139 | }
140 |
141 | // Test reading data from the kfifo.
142 | uint32_t read = z_kfifo_out(&fifo, test_data_out, buffer_size);
143 | if (read != buffer_size) {
144 | Z_RAW("Data read failed, read: %u\n", read);
145 | z_kfifo_free(&fifo);
146 | return -1; // Return error if the read operation fails.
147 | }
148 |
149 | // Verify that the read data matches the written data.
150 | for (uint32_t i = 0; i < buffer_size; i++) {
151 | if (test_data_out[i] != test_data_in[i]) {
152 | Z_RAW("Data mismatch at index %u, expected: %u, got: %u\n", i, test_data_in[i], test_data_out[i]);
153 | z_kfifo_free(&fifo);
154 | return -1; // Return error if there is a data mismatch.
155 | }
156 | }
157 |
158 | // Test writing to a full kfifo.
159 | written = z_kfifo_in(&fifo, test_data_in, buffer_size);
160 | if (written != buffer_size) {
161 | Z_RAW("Buffer full test failed, written: %u\n", written);
162 | z_kfifo_free(&fifo);
163 | return -1; // Return error if writing to full buffer fails.
164 | }
165 |
166 | // Test reading from an empty kfifo.
167 | read = z_kfifo_out(&fifo, test_data_out, buffer_size);
168 | if (read != buffer_size) {
169 | Z_RAW("Buffer empty test failed, read: %u\n", read);
170 | z_kfifo_free(&fifo);
171 | return -1; // Return error if reading from empty buffer fails.
172 | }
173 |
174 | // Test freeing memory allocated for the kfifo.
175 | z_kfifo_free(&fifo);
176 | if (fifo.p_buffer != NULL || fifo.size != 0 || fifo.in != 0 || fifo.out != 0) {
177 | Z_RAW("Memory free failed\n");
178 | return -1; // Return error if memory was not freed correctly.
179 | }
180 |
181 | Z_RAW("All tests passed successfully\n");
182 | return 0; // Return success if all tests pass.
183 | }
184 |
--------------------------------------------------------------------------------
/inc/z_debug.h:
--------------------------------------------------------------------------------
1 | #ifndef _Z_DEBUG_
2 | #define _Z_DEBUG_
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif /* __cplusplus */
7 |
8 | #define E_LEVEL_COLOR_ESC_START "\033["
9 | #define E_LEVEL_COLOR_ESC_END "\033[0m"
10 | #define E_LEVEL_COLOR_FATAL "1;31;40m" // Bold Red text on a black background
11 | #define E_LEVEL_COLOR_ERROR "1;35;40m" // Bold Magenta text on a black background
12 | #define E_LEVEL_COLOR_WARN "1;33;40m" // Bold Yellow text on a black background
13 | #define E_LEVEL_COLOR_NOTICE "1;34;40m" // Bold Blue text on a black background
14 | #define E_LEVEL_COLOR_INFO "1;32;40m" // Bold Green text on a black background
15 | #define E_LEVEL_COLORRE_DEBUG "1;36;40m" // Bold Cyan text on a black background
16 | #define E_LINE_MODE "\r\n"
17 |
18 | enum z_level_enum {
19 | E_Z_DEBUG_LEVEL_PRINTF = -1,
20 | E_Z_DEBUG_LEVEL_RAW = 0,
21 | E_Z_DEBUG_LEVEL_FATAL,
22 | E_Z_DEBUG_LEVEL_ERROR,
23 | E_Z_DEBUG_LEVEL_WARN,
24 | E_Z_DEBUG_LEVEL_NOTICE,
25 | E_Z_DEBUG_LEVEL_INFO,
26 | E_Z_DEBUG_LEVEL_DEBUG,
27 | E_Z_DEBUG_LEVEL_MAX
28 | };
29 | #define _Z_DEBUG(_level, format, args...) \
30 | while (1) { \
31 | switch (_level) { \
32 | case E_Z_DEBUG_LEVEL_FATAL: \
33 | printf("[fatal ][%s:%d, %s] " E_LEVEL_COLOR_ESC_START E_LEVEL_COLOR_FATAL format E_LEVEL_COLOR_ESC_END E_LINE_MODE, __FILE__, __LINE__, __FUNCTION__, ##args); \
34 | fflush(stdout); \
35 | break; \
36 | case E_Z_DEBUG_LEVEL_ERROR: \
37 | printf("[error ][%s:%d, %s] " E_LEVEL_COLOR_ESC_START E_LEVEL_COLOR_ERROR format E_LEVEL_COLOR_ESC_END E_LINE_MODE, __FILE__, __LINE__, __FUNCTION__, ##args); \
38 | fflush(stdout); \
39 | break; \
40 | case E_Z_DEBUG_LEVEL_WARN: \
41 | printf("[wart ][%s:%d, %s] " E_LEVEL_COLOR_ESC_START E_LEVEL_COLOR_WARN format E_LEVEL_COLOR_ESC_END E_LINE_MODE, __FILE__, __LINE__, __FUNCTION__, ##args); \
42 | fflush(stdout); \
43 | break; \
44 | case E_Z_DEBUG_LEVEL_NOTICE: \
45 | printf("[notice][%s:%d, %s] " E_LEVEL_COLOR_ESC_START E_LEVEL_COLOR_NOTICE format E_LEVEL_COLOR_ESC_END E_LINE_MODE, __FILE__, __LINE__, __FUNCTION__, ##args); \
46 | fflush(stdout); \
47 | break; \
48 | case E_Z_DEBUG_LEVEL_INFO: \
49 | printf("[info ][%s:%d, %s] " E_LEVEL_COLOR_ESC_START E_LEVEL_COLOR_INFO format E_LEVEL_COLOR_ESC_END E_LINE_MODE, __FILE__, __LINE__, __FUNCTION__, ##args); \
50 | fflush(stdout); \
51 | break; \
52 | case E_Z_DEBUG_LEVEL_DEBUG: \
53 | printf("[debug ][%s:%d, %s] " E_LEVEL_COLOR_ESC_START E_LEVEL_COLORRE_DEBUG format E_LEVEL_COLOR_ESC_END E_LINE_MODE, __FILE__, __LINE__, __FUNCTION__, ##args); \
54 | fflush(stdout); \
55 | break; \
56 | case E_Z_DEBUG_LEVEL_RAW: \
57 | case E_Z_DEBUG_LEVEL_PRINTF: \
58 | printf(format, ##args); \
59 | fflush(stdout); \
60 | break; \
61 | default: \
62 | break; \
63 | } \
64 | break; \
65 | }
66 |
67 | #define Z_DEBUG(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_DEBUG, format, ##args)
68 | #define Z_ERROR(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_ERROR, format, ##args)
69 | #define Z_WARN(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_WARN, format, ##args)
70 | #define Z_NOTICE(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_NOTICE, format, ##args)
71 | #define Z_FATAL(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_FATAL, format, ##args)
72 | #define Z_INFO(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_INFO, format, ##args)
73 | #define Z_RAW(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_RAW, format, ##args)
74 | #define Z_PRINTF(format, args...) _Z_DEBUG(E_Z_DEBUG_LEVEL_PRINTF, format, ##args)
75 | #define Z_DEBUG_ENTER() _Z_DEBUG(E_Z_DEBUG_LEVEL_DEBUG, "enter");
76 | #define Z_DEBUG_EXIT(_ret) _Z_DEBUG(E_Z_DEBUG_LEVEL_DEBUG, "exit :%d", _ret);
77 |
78 | #define Z_BASE_ASSERT(condition) \
79 | int *p = NULL; \
80 | while (1) { \
81 | *p = 0x5a5a5a5a; \
82 | assert(0); \
83 | }
84 |
85 | //_ASSERT(0):就会终止
86 | #define Z_ASSERT(condition) \
87 | do { \
88 | if (!(condition)) { \
89 | Z_FATAL("ASSERT: Condition '%s' is true !", #condition); \
90 | Z_BASE_ASSERT(condition); \
91 | } \
92 | } while (0)
93 |
94 | #define Z_WARN_ON(condition) \
95 | do { \
96 | if (condition) { \
97 | Z_WARN("WARNING: Condition '%s' is true!", #condition); \
98 | } \
99 | } while (0)
100 |
101 | #define Z_WARN_ON_ONCE(condition) \
102 | do { \
103 | static int __warned = 0; \
104 | if (!__warned && (condition)) { \
105 | Z_WARN("WARNING: Condition '%s' is true!", #condition); \
106 | __warned = 1; \
107 | } \
108 | } while (0)
109 |
110 | #define Z_BUG() \
111 | do { \
112 | z("BUG: failure!"); \
113 | z("BUG!"); \
114 | } while (0)
115 |
116 | #define Z_BUG_ON(condition) \
117 | do { \
118 | if ((condition)) { \
119 | z("BUG: Condition '%s' is true !", #condition); \
120 | Z_BASE_ASSERT(condition); \
121 | } \
122 | } while (0)
123 |
124 | // 测试断言宏
125 | #define Z_ASSERT_TEST(condition, message) \
126 | if (!(condition)) { \
127 | Z_ERROR("Test failed: %s", (message)); \
128 | } else { \
129 | Z_ERROR("Test passed: %s", (message)); \
130 | }
131 |
132 | #define Z_DEBUG_THREAD_SET_TRACE(func) \
133 | do { \
134 | char tmp[16]; \
135 | snprintf(tmp, sizeof(tmp), "%p", func); \
136 | prctl(PR_SET_NAME, (unsigned long)tmp, 0, 0, 0); \
137 | } while (0);
138 | #define Z_DEBUG_THREAD_CALL_INFO()
139 | #define Z_DEBUG_THREAD_CALL_INFO_FREE()
140 |
141 | #ifdef __cplusplus
142 | }
143 | #endif /* __cplusplus */
144 |
145 | #endif /* _Z_CONFIG_H_ */
146 |
--------------------------------------------------------------------------------
/src/z_thpool.c:
--------------------------------------------------------------------------------
1 | #include "z_tool.h"
2 | #include "z_kfifo.h"
3 | #include "z_debug.h"
4 | #include "z_thpool.h"
5 | #include "z_table_print.h"
6 |
7 |
8 | #define Z_THPOOL_VERION "0.0.3.0"
9 |
10 | // Structure to hold thread pool message details
11 | struct z_thpool_msg_struct {
12 | void (*cb)(void *); // Callback function
13 | void *p_arg; // Argument to the callback function
14 | };
15 |
16 | // Structure for managing the thread pool
17 | struct z_thpool_mng_struct {
18 | int32_t start_flag; // Flag indicating if the thread pool is started
19 | struct z_kfifo_struct t_info; // Information management for message queue
20 | int32_t th_run_flag; // Flag controlling thread pool run state
21 | uint32_t th_run_nums; // Number of currently running threads
22 | uint32_t th_busy_nums; // Number of busy threads
23 | uint32_t max_nums; // Maximum number of threads allowed
24 | uint32_t msg_node_max; // Maximum number of message nodes
25 | pthread_mutex_t mutex; // Mutex for synchronization
26 | pthread_cond_t cond; // Condition variable for thread synchronization
27 | uint32_t pub_bytes; // Data published (for statistics)
28 | uint32_t sub_bytes; // Data consumed (for statistics)
29 | struct z_thpool_config_struct t_config; // Configuration for thread pool
30 | char pool_name[32]; // Name of the thread pool
31 | };
32 |
33 | // Global variables for thread pool management and synchronization
34 | static struct z_thpool_mng_struct gs_thpool_mng = {0};
35 | static pthread_mutex_t gs_thpool_mutex = PTHREAD_MUTEX_INITIALIZER;
36 | static int32_t z_thpool_cmd(void);
37 |
38 | // Static function declarations
39 | static int32_t z_thpool_create_thread(pthread_t *p_pth, void *(*func)(void *), void *p_arg, uint32_t stack_size);
40 | static void z_thpool_msg_read(void *param);
41 | static void *z_thpool_proc(void *param);
42 |
43 | /**
44 | @brief Create a new thread pool instance
45 | @param p_config Configuration parameters for the thread pool
46 | @param p_handle Pointer to store the created thread pool handle
47 | @return Status of thread pool creation, success is 0
48 | */
49 | int32_t z_thpool_create(struct z_thpool_config_struct *p_config, z_thpool_handle_t *p_handle) {
50 | Z_DEBUG_ENTER();
51 | int32_t ret = -1;
52 |
53 | if (!p_config || !p_handle) {
54 | goto error0;
55 | }
56 |
57 | struct z_thpool_mng_struct *p_mng = (struct z_thpool_mng_struct *)z_tool_malloc(sizeof(struct z_thpool_mng_struct));
58 | if (!p_mng) {
59 | goto error0;
60 | }
61 | memset(p_mng, 0, sizeof(struct z_thpool_mng_struct));
62 |
63 | // Initialize mutex and condition variable
64 | if (pthread_mutex_init(&p_mng->mutex, NULL) != 0) {
65 | goto error1;
66 | }
67 |
68 | if (pthread_cond_init(&p_mng->cond, NULL) != 0) {
69 | goto error2;
70 | }
71 |
72 | // Initialize FIFO queue
73 | ret = z_kfifo_malloc(&p_mng->t_info, sizeof(struct z_thpool_msg_struct) * p_config->msg_node_max);
74 | if (ret != 0) {
75 | goto error3;
76 | }
77 |
78 | // Copy configuration
79 | memcpy(&p_mng->t_config, p_config, sizeof(struct z_thpool_config_struct));
80 | strncpy(p_mng->pool_name, p_config->pool_name, sizeof(p_mng->pool_name) - 1);
81 | p_mng->pool_name[sizeof(p_mng->pool_name) - 1] = '\0';
82 |
83 | p_mng->max_nums = p_config->max_thread_nums;
84 | p_mng->msg_node_max = p_config->msg_node_max;
85 | p_mng->th_run_flag = 1;
86 |
87 | // Create worker threads
88 | for (uint32_t i = 0; i < p_config->max_thread_nums; i++) {
89 | pthread_t tid;
90 | ret = z_thpool_create_thread(&tid, z_thpool_proc, p_mng, p_config->thread_stack_size);
91 | if (ret != 0) {
92 | p_mng->th_run_flag = 0;
93 | pthread_cond_broadcast(&p_mng->cond);
94 | while (p_mng->th_run_nums > 0) {
95 | usleep(10000);
96 | }
97 | goto error4;
98 | }
99 | p_mng->th_run_nums++;
100 | }
101 |
102 | p_mng->start_flag = 1;
103 | *p_handle = p_mng;
104 | ret = 0;
105 | Z_DEBUG_EXIT(0);
106 | return ret;
107 |
108 | error4:
109 | z_kfifo_free(&p_mng->t_info);
110 | error3:
111 | pthread_cond_destroy(&p_mng->cond);
112 | error2:
113 | pthread_mutex_destroy(&p_mng->mutex);
114 | error1:
115 | z_tool_free(p_mng);
116 | error0:
117 | Z_DEBUG_EXIT(ret);
118 | return ret;
119 | }
120 |
121 | /**
122 | @brief Destroy a thread pool instance
123 | @param handle Handle to the thread pool to destroy
124 | @return Status of thread pool destruction, success is 0
125 | */
126 | int32_t z_thpool_destroy(z_thpool_handle_t handle) {
127 | Z_DEBUG_ENTER();
128 | int32_t ret = -1;
129 |
130 | if (!handle) {
131 | goto error0;
132 | }
133 |
134 | struct z_thpool_mng_struct *p_mng = (struct z_thpool_mng_struct *)handle;
135 |
136 | pthread_mutex_lock(&p_mng->mutex);
137 | if (!p_mng->start_flag) {
138 | pthread_mutex_unlock(&p_mng->mutex);
139 | goto error0;
140 | }
141 |
142 | p_mng->th_run_flag = 0;
143 | pthread_cond_broadcast(&p_mng->cond);
144 | pthread_mutex_unlock(&p_mng->mutex);
145 |
146 | // Wait for all threads to exit
147 | while (p_mng->th_run_nums > 0) {
148 | usleep(10000);
149 | }
150 |
151 | // Cleanup resources
152 | z_kfifo_free(&p_mng->t_info);
153 | pthread_mutex_destroy(&p_mng->mutex);
154 | pthread_cond_destroy(&p_mng->cond);
155 | z_tool_free(p_mng);
156 |
157 | ret = 0;
158 | Z_DEBUG_EXIT(0);
159 | return ret;
160 |
161 | error0:
162 | Z_DEBUG_EXIT(ret);
163 | return ret;
164 | }
165 |
166 | /**
167 | @brief Create a thread, set its attributes, and start it
168 | @param p_pth Pointer to the thread ID
169 | @param func Function to be executed by the thread
170 | @param p_arg Argument passed to the function
171 | @param stack_size Stack size for the thread
172 | @return Status of thread creation, success is 0
173 | */
174 | static int32_t z_thpool_create_thread(pthread_t *p_pth, void *(*func)(void *), void *p_arg, uint32_t stack_size) {
175 | int32_t ret;
176 | pthread_attr_t attr;
177 |
178 | pthread_attr_init(&attr);
179 |
180 | // Set the thread to be detached
181 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
182 | pthread_attr_setstacksize(&attr, stack_size);
183 |
184 | #ifndef CFG_PLATFORM_X86
185 | // Set explicit scheduling if not on X86 platform
186 | ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
187 | if (ret) {
188 | fprintf(stderr, "pthread_attr_setinheritsched:%d\n", ret);
189 | goto error;
190 | }
191 | #endif
192 |
193 | // Create the thread with specified attributes
194 | ret = pthread_create(p_pth, &attr, func, p_arg);
195 | if (ret) {
196 | fprintf(stderr, "pthread_create:%d\n", ret);
197 | goto error;
198 | }
199 |
200 | error:
201 | // Clean up thread attributes
202 | pthread_attr_destroy(&attr);
203 | return ret;
204 | }
205 |
206 | /**
207 | @brief Read and process messages from the message queue
208 | @param param Pointer to the thread pool management structure
209 | @return No return value
210 | */
211 | static void z_thpool_msg_read(void *param) {
212 | struct z_thpool_mng_struct *mng = (struct z_thpool_mng_struct *)param;
213 | struct z_thpool_msg_struct msg;
214 |
215 | pthread_mutex_lock(&mng->mutex);
216 | while (mng->th_run_flag && z_kfifo_data_len(&mng->t_info) < sizeof(struct z_thpool_msg_struct)) {
217 | pthread_cond_wait(&mng->cond, &mng->mutex);
218 | }
219 |
220 | if (mng->th_run_flag == 0 || z_kfifo_data_len(&mng->t_info) < sizeof(struct z_thpool_msg_struct)) {
221 | pthread_mutex_unlock(&mng->mutex);
222 | return;
223 | }
224 |
225 | // Retrieve message from the queue
226 | uint32_t len = z_kfifo_out(&mng->t_info, &msg, sizeof(struct z_thpool_msg_struct));
227 | mng->th_busy_nums++;
228 | mng->sub_bytes += len;
229 | pthread_mutex_unlock(&mng->mutex);
230 |
231 | // Execute the callback function for the message
232 | msg.cb(msg.p_arg);
233 |
234 | pthread_mutex_lock(&mng->mutex);
235 | mng->th_busy_nums--;
236 | pthread_mutex_unlock(&mng->mutex);
237 | }
238 |
239 | /**
240 | @brief Thread pool processing function
241 | @param param Pointer to the thread pool management structure
242 | @return No return value
243 | */
244 | static void *z_thpool_proc(void *param) {
245 | prctl(PR_SET_NAME, "thp");
246 | struct z_thpool_mng_struct *mng = (struct z_thpool_mng_struct *)param;
247 |
248 | // Continue processing messages as long as the run flag is set
249 | while (mng->th_run_flag) {
250 | z_thpool_msg_read(mng);
251 | }
252 |
253 | // Decrement the run number after processing is complete
254 | pthread_mutex_lock(&mng->mutex);
255 | mng->th_run_nums--;
256 | pthread_mutex_unlock(&mng->mutex);
257 | return NULL;
258 | }
259 |
260 | /**
261 | @brief Add a work task to the thread pool
262 | @param handle Handle to the thread pool
263 | @param cb Callback function
264 | @param p_arg Argument for the callback function
265 | @return Status, success is 0
266 | */
267 | int32_t z_thpool_add_work(z_thpool_handle_t handle, void (*cb)(void *), void *p_arg) {
268 | if (!handle || !cb || !p_arg) {
269 | return -EINVAL;
270 | }
271 |
272 | struct z_thpool_mng_struct *p_mng = (struct z_thpool_mng_struct *)handle;
273 | int32_t ret = -1;
274 |
275 | pthread_mutex_lock(&p_mng->mutex);
276 | if (!p_mng->start_flag) {
277 | goto error;
278 | }
279 |
280 | if (z_kfifo_space(&p_mng->t_info) < sizeof(struct z_thpool_msg_struct)) {
281 | goto error;
282 | }
283 |
284 | struct z_thpool_msg_struct msg;
285 | msg.cb = cb;
286 | msg.p_arg = p_arg;
287 |
288 | uint32_t len = z_kfifo_in(&p_mng->t_info, &msg, sizeof(struct z_thpool_msg_struct));
289 | if (len != sizeof(struct z_thpool_msg_struct)) {
290 | goto error;
291 | }
292 |
293 | p_mng->pub_bytes += len;
294 | pthread_cond_signal(&p_mng->cond);
295 | ret = 0;
296 |
297 | error:
298 | pthread_mutex_unlock(&p_mng->mutex);
299 | return ret;
300 | }
301 |
302 | /**
303 | @brief Display thread pool status
304 | @param handle Handle to the thread pool
305 | @return Status, success is 0
306 | */
307 | int32_t z_thpool_cmd_shell_show(z_thpool_handle_t handle) {
308 | if (!handle) {
309 | return -EINVAL;
310 | }
311 |
312 | struct z_thpool_mng_struct *p_mng = (struct z_thpool_mng_struct *)handle;
313 | pthread_mutex_lock(&p_mng->mutex);
314 |
315 | z_table_print_title("z_thpool module");
316 | z_table_print_row("%-18s %s\n", "Ver: ", Z_THPOOL_VERION);
317 | z_table_print_row("%-18s %s\n", "Pool Name: ", p_mng->pool_name);
318 | z_table_print_border();
319 | z_table_print_row("%-18s %d\n", "max nums: ", p_mng->max_nums);
320 | z_table_print_row("%-18s %d\n", "create nums: ", p_mng->th_run_nums);
321 | z_table_print_row("%-18s %d\n", "busy nums:", p_mng->th_busy_nums);
322 | z_table_print_row("%-18s %d\n", "max cache nums:", p_mng->msg_node_max);
323 | z_table_print_row("%-18s %d\n", "use cache nums:",
324 | z_kfifo_data_len(&p_mng->t_info) / sizeof(struct z_thpool_msg_struct));
325 | z_table_print_row("%-18s %d\n", "pub_bytes:", p_mng->pub_bytes);
326 | z_table_print_row("%-18s %d\n", "sub_bytes:", p_mng->sub_bytes);
327 |
328 | pthread_mutex_unlock(&p_mng->mutex);
329 | return 0;
330 | }
331 |
332 | /**
333 | @brief Task callback function
334 | @param p_arg Parameter
335 | @return No return value
336 | */
337 | static void z_thpool_test_task_cb(void *p_arg) {
338 | int32_t num = *((int32_t *)p_arg);
339 | printf("Task %d is being processed by thread %lu\n", num, pthread_self());
340 | sleep(1); // Simulate task processing time
341 | }
342 |
343 | /**
344 | @brief Test the thread pool functionality
345 | @return Status, success is 0
346 | */
347 | int32_t z_thpool_test(void) {
348 | // Configuration for the thread pool extreme test
349 | struct z_thpool_config_struct t_config = {
350 | .max_thread_nums = 100,
351 | .msg_node_max = 100,
352 | .thread_stack_size = 64 * 1024
353 | };
354 | strncpy(t_config.pool_name, "test_pool", sizeof(t_config.pool_name) - 1);
355 | t_config.pool_name[sizeof(t_config.pool_name) - 1] = '\0';
356 |
357 | printf("Starting thread pool extreme test...\n");
358 |
359 | // Create thread pool
360 | z_thpool_handle_t handle;
361 | if (z_thpool_create(&t_config, &handle) != 0) {
362 | printf("Failed to create thread pool\n");
363 | return -1;
364 | }
365 |
366 | // Define task parameters array
367 | int32_t task_args[100];
368 | for (int32_t i = 0; i < 100; i++) {
369 | task_args[i] = i;
370 | }
371 |
372 | // Add tasks to the thread pool
373 | for (int32_t i = 0; i < 100; i++) {
374 | if (z_thpool_add_work(handle, z_thpool_test_task_cb, &task_args[i]) != 0) {
375 | fprintf(stderr, "Failed to add task %d\n", i);
376 | } else {
377 | printf("Task %d added successfully\n", i);
378 | }
379 | }
380 |
381 | // Wait for tasks to be processed
382 | for (int32_t i = 0; i < 5; i++) {
383 | printf("Waiting...\n");
384 | sleep(1);
385 | }
386 |
387 | // Verify data integrity
388 | for (int32_t i = 0; i < 100; i++) {
389 | printf("Task %d argument after processing: %d\n", i, task_args[i]);
390 | }
391 |
392 | // Show thread pool status
393 | z_thpool_cmd_shell_show(handle);
394 |
395 | // Destroy the thread pool
396 | z_thpool_destroy(handle);
397 | printf("Thread pool extreme test completed.\n");
398 | return 0;
399 | }
400 |
--------------------------------------------------------------------------------