1 | // C includes |
2 | #include <stdio.h> |
3 | #include <stdint.h> |
4 | #include <stdlib.h> |
5 | |
6 | // C++ includes |
7 | #include <chrono> |
8 | #include <mutex> |
9 | #include <random> |
10 | #include <thread> |
11 | |
12 | std::thread g_thread_1; |
13 | std::thread g_thread_2; |
14 | std::thread g_thread_3; |
15 | std::mutex g_mask_mutex; |
16 | |
17 | enum MaskAction { |
18 | eGet, |
19 | eAssign, |
20 | eClearBits |
21 | }; |
22 | |
23 | uint32_t mask_access (MaskAction action, uint32_t mask = 0); |
24 | |
25 | uint32_t |
26 | mask_access (MaskAction action, uint32_t mask) |
27 | { |
28 | static uint32_t g_mask = 0; |
29 | |
30 | std::lock_guard<std::mutex> lock(g_mask_mutex); |
31 | switch (action) |
32 | { |
33 | case eGet: |
34 | break; |
35 | |
36 | case eAssign: |
37 | g_mask |= mask; |
38 | break; |
39 | |
40 | case eClearBits: |
41 | g_mask &= ~mask; |
42 | break; |
43 | } |
44 | return g_mask; |
45 | } |
46 | |
47 | void * |
48 | thread_func (void *arg) |
49 | { |
50 | uint32_t thread_index = *((uint32_t *)arg); |
51 | uint32_t thread_mask = (1u << (thread_index)); |
52 | printf (format: "%s (thread index = %u) startng...\n" , __FUNCTION__, thread_index); |
53 | |
54 | std::default_random_engine generator; |
55 | std::uniform_int_distribution<int> distribution(0, 3000000); |
56 | |
57 | while (mask_access(action: eGet) & thread_mask) |
58 | { |
59 | // random micro second sleep from zero to 3 seconds |
60 | int usec = distribution(generator); |
61 | printf (format: "%s (thread = %u) doing a usleep (%d)...\n" , __FUNCTION__, thread_index, usec); |
62 | |
63 | std::chrono::microseconds duration(usec); |
64 | std::this_thread::sleep_for(rtime: duration); |
65 | printf (format: "%s (thread = %u) after usleep ...\n" , __FUNCTION__, thread_index); // Set break point at this line. |
66 | } |
67 | printf (format: "%s (thread index = %u) exiting...\n" , __FUNCTION__, thread_index); |
68 | return NULL; |
69 | } |
70 | |
71 | |
72 | int main (int argc, char const *argv[]) |
73 | { |
74 | uint32_t thread_index_1 = 1; |
75 | uint32_t thread_index_2 = 2; |
76 | uint32_t thread_index_3 = 3; |
77 | uint32_t thread_mask_1 = (1u << thread_index_1); |
78 | uint32_t thread_mask_2 = (1u << thread_index_2); |
79 | uint32_t thread_mask_3 = (1u << thread_index_3); |
80 | |
81 | // Make a mask that will keep all threads alive |
82 | mask_access (action: eAssign, mask: thread_mask_1 | thread_mask_2 | thread_mask_3); // And that line. |
83 | |
84 | // Create 3 threads |
85 | g_thread_1 = std::thread(thread_func, (void*)&thread_index_1); |
86 | g_thread_2 = std::thread(thread_func, (void*)&thread_index_2); |
87 | g_thread_3 = std::thread(thread_func, (void*)&thread_index_3); |
88 | |
89 | char line[64]; |
90 | while (mask_access(action: eGet) != 0) |
91 | { |
92 | printf (format: "Enter thread index to kill or ENTER for all:\n" ); |
93 | fflush (stdout); |
94 | // Kill threads by index, or ENTER for all threads |
95 | |
96 | if (fgets (s: line, n: sizeof(line), stdin)) |
97 | { |
98 | if (line[0] == '\n' || line[0] == '\r' || line[0] == '\0') |
99 | { |
100 | printf (format: "Exiting all threads...\n" ); |
101 | break; |
102 | } |
103 | int32_t index = strtoul (nptr: line, NULL, base: 0); |
104 | switch (index) |
105 | { |
106 | case 1: mask_access (action: eClearBits, mask: thread_mask_1); break; |
107 | case 2: mask_access (action: eClearBits, mask: thread_mask_2); break; |
108 | case 3: mask_access (action: eClearBits, mask: thread_mask_3); break; |
109 | } |
110 | continue; |
111 | } |
112 | |
113 | break; |
114 | } |
115 | |
116 | // Clear all thread bits to they all exit |
117 | mask_access (action: eClearBits, UINT32_MAX); |
118 | |
119 | // Join all of our threads |
120 | g_thread_1.join(); |
121 | g_thread_2.join(); |
122 | g_thread_3.join(); |
123 | |
124 | return 0; |
125 | } |
126 | |