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