multithread¶
1/*
2 @mindmaze_header@
3*/
4
5/* multithreaded data write
6 *
7 * example of child process, to be used by pshared-parent.c example.
8 *
9 * This program writes to a shared text (in shared memory) concurrently with
10 * other threads in the same process. When notified, a worker thread tries
11 * to write its identification string ("|-thread-X+|") onto a text field of
12 * the shared memory. The text after update of several worker threads looks
13 * something like:
14 *
15 * ...|+thread-Z+||+thread-W+||+thread-X+||+thread-Y+|...
16 *
17 * This file demonstrates how to:
18 * - map file into memory
19 * - use process shared mutex
20 */
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24#include <mmthread.h>
25
26#define NUM_THREAD 6
27#define MAX_ID_LEN 16
28
29struct shared_data {
30 mm_thr_mutex_t mutex;
31 int len;
32 char text[1024];
33 mm_thr_mutex_t notif_mtx;
34 mm_thr_cond_t notif_cond;
35 int start;
36};
37
38struct thread_data {
39 struct shared_data* shdata;
40 char id_str[MAX_ID_LEN];
41};
42
43
44/*
45 * This function do the update of the shared text. It happens the
46 * string |+@id_str+| to the text field in @psh_data.
47 */
48static
49void write_shared_data(struct shared_data* shdata, const char* id_str)
50{
51 int id_str_len = strlen(id_str);
52
53 // Get the shared lock. Since we are using a normal mutex, we do not
54 // have to check the return value
55 mm_thr_mutex_lock(&shdata->mutex);
56
57 // Add "|+" in the text
58 shdata->text[shdata->len++] = '|';
59 shdata->text[shdata->len++] = '+';
60
61 // Append process identifier on text
62 memcpy(shdata->text + shdata->len, id_str, id_str_len);
63 shdata->len += id_str_len;
64
65 // Add "+|" in the text
66 shdata->text[shdata->len++] = '+';
67 shdata->text[shdata->len++] = '|';
68
69 mm_thr_mutex_unlock(&shdata->mutex);
70}
71
72
73static
74void wait_start_notification(struct shared_data* shdata)
75{
76 mm_thr_mutex_lock(&shdata->notif_mtx);
77
78 // A while loop is necessary, because a spurious wakeup is always
79 // possible
80 while (!shdata->start)
81 mm_thr_cond_wait(&shdata->notif_cond, &shdata->notif_mtx);
82
83 mm_thr_mutex_unlock(&shdata->notif_mtx);
84}
85
86
87static
88void broadcast_start_notification(struct shared_data* shdata)
89{
90 // We want a worker thread to be be scheduled in a predictable way,
91 // so we must own shdata->notif_mtx when calling
92 // mm_thr_cond_broadcast()
93 mm_thr_mutex_lock(&shdata->notif_mtx);
94
95 shdata->start = 1;
96 mm_thr_cond_broadcast(&shdata->notif_cond);
97
98 mm_thr_mutex_unlock(&shdata->notif_mtx);
99}
100
101
102static
103void* thread_func(void* data)
104{
105 struct thread_data* thdata = data;
106 struct shared_data* shdata = thdata->shdata;
107 const char* id_str = thdata->id_str;
108
109 // Put a wait here to force a litle bit of more contention. This is
110 // here only for demonstration purpose... Without it, since the
111 // update of text is short and simple, the text would be likely
112 // filed in the order of thread creation
113 wait_start_notification(shdata);
114
115 write_shared_data(shdata, id_str);
116
117 return NULL;
118}
119
120
121int main(void)
122{
123 int i;
124 mm_thread_t thid[NUM_THREAD];
125 struct thread_data thdata[NUM_THREAD];
126 struct shared_data shared = {
127 .mutex = MM_THR_MUTEX_INITIALIZER,
128 .notif_mtx = MM_THR_MUTEX_INITIALIZER,
129 .notif_cond = MM_THR_COND_INITIALIZER,
130 .start = 0,
131 };
132
133 // Create threads and assign each an ID string
134 for (i = 0; i < NUM_THREAD; i++) {
135 thdata[i].shdata = &shared;
136 sprintf(thdata[i].id_str, "thread-%i", i);
137 mm_thr_create(&thid[i], thread_func, &thdata[i]);
138 }
139
140 // Now that all thread are created, we can signal them to start
141 broadcast_start_notification(&shared);
142
143 for (i = 0; i < NUM_THREAD; i++)
144 mm_thr_join(thid[i], NULL);
145
146 printf("result string:%s\n", shared.text);
147 return EXIT_SUCCESS;
148}
149
parse_args¶
1/*
2 * @mindmaze_header@
3 */
4
5#include <mmargparse.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <errno.h>
10#include <mmsysio.h>
11
12struct config {
13 const char* detach_flag;
14 unsigned int num_instance;
15 const char* ip;
16 const char* use_local_storage;
17};
18static
19struct config cfg = {
20 .num_instance = 10,
21 .ip = "127.0.0.1",
22};
23
24#define LOREM_IPSUM "Lorem ipsum dolor sit amet, consectetur adipiscing" \
25 "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." \
26 "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi " \
27 "ut aliquip ex ea commodo consequat..."
28
29#define DEFAULT_PATH "/default/path"
30
31static
32struct mm_arg_opt cmdline_optv[] = {
33 {"detach", MM_OPT_NOVAL, "set", {.sptr = &cfg.detach_flag},
34 "detach server process."},
35 {"n|num-instance", MM_OPT_NEEDUINT, NULL, {.uiptr = &cfg.num_instance},
36 "Server can accommodate up to @NUM client simultaneously. Here is "
37 "more explanation to test text wrapping. " LOREM_IPSUM},
38 {"l|use-local-storage", MM_OPT_OPTSTR, DEFAULT_PATH, {NULL},
39 "Use local storage located at @PATH which must exist. "
40 "If unspecified @PATH is assumed "DEFAULT_PATH "."},
41 {.name = "i", .flags = MM_OPT_NEEDSTR, .defval = NULL,
42 {.sptr = &cfg.ip},
43 .desc = "IP address of remote server. @ADDR must have dotted form."},
44};
45
46
47/**
48 * parse_option_cb() - validate some option value and parse other
49 * @opt: parser configuration of option recognized
50 * @value: value about to be set for option
51 * @data: callback data
52 * @state: flags indicating the state of option parsing.
53 *
54 * Return: 0 is parsing must continue, -1 if error has been detect and
55 * parsing must stop.
56 */
57static
58int parse_option_cb(const struct mm_arg_opt* opt, union mm_arg_val value,
59 void* data, int state)
60{
61 struct config* conf = data;
62 (void)state;
63
64 switch (mm_arg_opt_get_key(opt)) {
65 case 'n':
66 if (value.ui < 1) {
67 fprintf(stderr,
68 "Server must support at least 1 instance\n");
69 return -1;
70 }
71
72 // We don't set value here, since variable to set is already
73 // configured in option setup ({.strptr = &cfg.ip})
74 return 0;
75
76 case 'l':
77 if (mm_check_access(value.str, F_OK) != 0) {
78 fprintf(stderr,
79 "storage file %s does not exist\n",
80 value.str);
81 return -1;
82 }
83
84 conf->use_local_storage = value.str;
85 return 0;
86
87 default:
88 return 0;
89 }
90}
91
92
93int main(int argc, char* argv[])
94{
95 int i, arg_index;
96 struct mm_arg_parser parser = {
97 .doc = LOREM_IPSUM,
98 .args_doc = "[options] cmd argument\n[options] hello",
99 .optv = cmdline_optv,
100 .num_opt = MM_NELEM(cmdline_optv),
101 .cb = parse_option_cb,
102 .cb_data = &cfg,
103 .execname = argv[0],
104 };
105
106
107 arg_index = mm_arg_parse(&parser, argc, argv);
108
109 fprintf(stdout, "options used:\n\tdetach_flag: %s\n\tinstance: %u\n"
110 "\tserver address: %s\n\tuse local path: %s\n",
111 cfg.detach_flag, cfg.num_instance,
112 cfg.ip, cfg.use_local_storage);
113
114 fprintf(stdout, "Execute ");
115 for (i = arg_index; i < argc; i++)
116 fprintf(stdout, "%s ", argv[i]);
117
118 fputc('\n', stdout);
119
120 return EXIT_SUCCESS;
121}